You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

665 lines
15 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software: you can redistribute it and/or modify it
  7. * under the terms of the GNU General Public License as published by the
  8. * Free Software Foundation, either version 3 of the License, or (at your
  9. * option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <wx/dir.h>
  20. #include <wx/filename.h>
  21. #include <wx/stdpaths.h>
  22. #include <wx/string.h>
  23. #include <wx/utils.h>
  24. #include <kiplatform/environment.h>
  25. #include <paths.h>
  26. #include <config.h>
  27. #include <build_version.h>
  28. #include <macros.h>
  29. #include <wx_filename.h>
  30. // lowercase or pretty case depending on platform
  31. #if defined( __WXMAC__ ) || defined( __WXMSW__ )
  32. #define KICAD_PATH_STR wxT( "KiCad" )
  33. #else
  34. #define KICAD_PATH_STR wxT( "kicad" )
  35. #endif
  36. void PATHS::getUserDocumentPath( wxFileName& aPath )
  37. {
  38. wxString envPath;
  39. if( wxGetEnv( wxT( "KICAD_DOCUMENTS_HOME" ), &envPath ) )
  40. aPath.AssignDir( envPath );
  41. else
  42. aPath.AssignDir( KIPLATFORM::ENV::GetDocumentsPath() );
  43. aPath.AppendDir( KICAD_PATH_STR );
  44. aPath.AppendDir( GetMajorMinorVersion().ToStdString() );
  45. }
  46. wxString PATHS::GetUserPluginsPath()
  47. {
  48. wxFileName tmp;
  49. getUserDocumentPath( tmp );
  50. tmp.AppendDir( wxT( "plugins" ) );
  51. return tmp.GetPath();
  52. }
  53. wxString PATHS::GetUserScriptingPath()
  54. {
  55. wxFileName tmp;
  56. getUserDocumentPath( tmp );
  57. tmp.AppendDir( wxT( "scripting" ) );
  58. return tmp.GetPath();
  59. }
  60. wxString PATHS::GetUserTemplatesPath()
  61. {
  62. wxFileName tmp;
  63. getUserDocumentPath( tmp );
  64. tmp.AppendDir( wxT( "template" ) );
  65. return tmp.GetPathWithSep();
  66. }
  67. wxString PATHS::GetDefaultUserSymbolsPath()
  68. {
  69. wxFileName tmp;
  70. getUserDocumentPath( tmp );
  71. tmp.AppendDir( wxT( "symbols" ) );
  72. return tmp.GetPath();
  73. }
  74. wxString PATHS::GetDefaultUserFootprintsPath()
  75. {
  76. wxFileName tmp;
  77. getUserDocumentPath( tmp );
  78. tmp.AppendDir( wxT( "footprints" ) );
  79. return tmp.GetPath();
  80. }
  81. wxString PATHS::GetDefaultUserDesignBlocksPath()
  82. {
  83. wxFileName tmp;
  84. getUserDocumentPath( tmp );
  85. tmp.AppendDir( wxT( "blocks" ) );
  86. return tmp.GetPath();
  87. }
  88. wxString PATHS::GetDefaultUser3DModelsPath()
  89. {
  90. wxFileName tmp;
  91. getUserDocumentPath( tmp );
  92. tmp.AppendDir( wxT( "3dmodels" ) );
  93. return tmp.GetPath();
  94. }
  95. wxString PATHS::GetDefault3rdPartyPath()
  96. {
  97. wxFileName tmp;
  98. getUserDocumentPath( tmp );
  99. tmp.AppendDir( wxT( "3rdparty" ) );
  100. return tmp.GetPath();
  101. }
  102. wxString PATHS::GetDefaultUserProjectsPath()
  103. {
  104. wxFileName tmp;
  105. getUserDocumentPath( tmp );
  106. tmp.AppendDir( wxT( "projects" ) );
  107. return tmp.GetPath();
  108. }
  109. /**
  110. * Get the CMake build root directory for the current executable
  111. * (which assumes the executable is in a build directory).
  112. *
  113. * This is done because not all executable are located at the same
  114. * depth in the build directory.
  115. */
  116. static wxString getBuildDirectoryRoot()
  117. {
  118. // We don't have a perfect way to spot a build directory (e.g. when archived as artifacts in
  119. // CI) but we can assume that the build directory will have a schemas directory that contains
  120. // JSON files, as that's one of the things that we use this path for.
  121. const auto looksLikeBuildDir = []( const wxFileName& aPath ) -> bool
  122. {
  123. const wxDir schema_dir( aPath.GetPathWithSep() + wxT( "schemas" ) );
  124. if( !schema_dir.IsOpened() )
  125. return false;
  126. wxString filename;
  127. const bool found = schema_dir.GetFirst( &filename, wxT( "*.json" ), wxDIR_FILES );
  128. return found;
  129. };
  130. const wxString execPath = PATHS::GetExecutablePath();
  131. wxFileName fn = execPath;
  132. // Climb the directory tree until we find a directory that looks like a build directory
  133. // Normally we expect to climb one or two levels only.
  134. while( fn.GetDirCount() > 0 && !looksLikeBuildDir( fn ) )
  135. {
  136. fn.RemoveLastDir();
  137. }
  138. wxASSERT_MSG(
  139. fn.GetDirCount() > 0,
  140. wxString::Format( wxT( "Could not find build root directory above %s" ), execPath ) );
  141. return fn.GetPath();
  142. }
  143. wxString PATHS::GetStockDataPath( bool aRespectRunFromBuildDir )
  144. {
  145. wxString path;
  146. if( aRespectRunFromBuildDir && wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
  147. {
  148. // Allow debugging from build dir by placing relevant files/folders in the build root
  149. #if defined( __WXMAC__ )
  150. wxFileName fn = wxStandardPaths::Get().GetExecutablePath();
  151. fn.RemoveLastDir();
  152. fn.RemoveLastDir();
  153. fn.RemoveLastDir();
  154. fn.RemoveLastDir();
  155. path = fn.GetPath();
  156. #elif defined( __WXMSW__ )
  157. path = getWindowsKiCadRoot();
  158. #else
  159. path = getBuildDirectoryRoot();
  160. #endif
  161. }
  162. else if( wxGetEnv( wxT( "KICAD_STOCK_DATA_HOME" ), &path ) && !path.IsEmpty() )
  163. {
  164. return path;
  165. }
  166. else
  167. {
  168. #if defined( __WXMAC__ )
  169. path = GetOSXKicadDataDir();
  170. #elif defined( __WXMSW__ )
  171. path = getWindowsKiCadRoot() + wxT( "share/kicad" );
  172. #else
  173. path = wxString::FromUTF8Unchecked( KICAD_DATA );
  174. #endif
  175. }
  176. return path;
  177. }
  178. #ifdef _WIN32
  179. wxString PATHS::GetWindowsBaseSharePath()
  180. {
  181. return getWindowsKiCadRoot() + wxT( "share\\" );
  182. }
  183. #endif
  184. wxString PATHS::GetStockEDALibraryPath()
  185. {
  186. wxString path;
  187. #if defined( __WXMAC__ )
  188. path = GetOSXKicadMachineDataDir();
  189. #elif defined( __WXMSW__ )
  190. path = GetStockDataPath( false );
  191. #else
  192. path = wxString::FromUTF8Unchecked( KICAD_LIBRARY_DATA );
  193. #endif
  194. return path;
  195. }
  196. wxString PATHS::GetStockSymbolsPath()
  197. {
  198. wxString path;
  199. path = GetStockEDALibraryPath() + wxT( "/symbols" );
  200. return path;
  201. }
  202. wxString PATHS::GetStockFootprintsPath()
  203. {
  204. wxString path;
  205. path = GetStockEDALibraryPath() + wxT( "/footprints" );
  206. return path;
  207. }
  208. wxString PATHS::GetStockDesignBlocksPath()
  209. {
  210. wxString path;
  211. path = GetStockEDALibraryPath() + wxT( "/blocks" );
  212. return path;
  213. }
  214. wxString PATHS::GetStock3dmodelsPath()
  215. {
  216. wxString path;
  217. path = GetStockEDALibraryPath() + wxT( "/3dmodels" );
  218. return path;
  219. }
  220. wxString PATHS::GetStockScriptingPath()
  221. {
  222. wxString path;
  223. path = GetStockDataPath() + wxT( "/scripting" );
  224. return path;
  225. }
  226. wxString PATHS::GetStockTemplatesPath()
  227. {
  228. wxString path;
  229. path = GetStockEDALibraryPath() + wxT( "/template" );
  230. return path;
  231. }
  232. wxString PATHS::GetLocaleDataPath()
  233. {
  234. wxString path;
  235. path = GetStockDataPath() + wxT( "/internat" );
  236. return path;
  237. }
  238. wxString PATHS::GetStockPluginsPath()
  239. {
  240. wxFileName fn;
  241. #if defined( __WXMSW__ )
  242. fn.AssignDir( GetExecutablePath() );
  243. fn.AppendDir( wxT( "scripting" ) );
  244. #else
  245. fn.AssignDir( PATHS::GetStockDataPath( false ) );
  246. #endif
  247. fn.AppendDir( wxT( "plugins" ) );
  248. return fn.GetPathWithSep();
  249. }
  250. wxString PATHS::GetStockPlugins3DPath()
  251. {
  252. wxFileName fn;
  253. #if defined( __WXMSW__ )
  254. if( wxGetEnv( wxT( "KICAD_RUN_FROM_BUILD_DIR" ), nullptr ) )
  255. {
  256. fn.AssignDir( getWindowsKiCadRoot() );
  257. }
  258. else
  259. {
  260. fn.AssignDir( GetExecutablePath() );
  261. }
  262. fn.AppendDir( wxT( "plugins" ) );
  263. #elif defined( __WXMAC__ )
  264. fn.Assign( wxStandardPaths::Get().GetPluginsDir(), wxEmptyString );
  265. // This must be mapped to main bundle for everything but kicad.app
  266. const wxArrayString dirs = fn.GetDirs();
  267. // Check if we are the main kicad binary. in this case, the path will be
  268. // /path/to/bundlename.app/Contents/PlugIns
  269. // If we are an aux binary, the path will be something like
  270. // /path/to/bundlename.app/Contents/Applications/<standalone>.app/Contents/PlugIns
  271. if( dirs.GetCount() >= 6 &&
  272. dirs[dirs.GetCount() - 4] == wxT( "Applications" ) &&
  273. dirs[dirs.GetCount() - 6].Lower().EndsWith( wxT( "app" ) ) )
  274. {
  275. fn.RemoveLastDir();
  276. fn.RemoveLastDir();
  277. fn.RemoveLastDir();
  278. fn.RemoveLastDir();
  279. fn.AppendDir( wxT( "PlugIns" ) );
  280. }
  281. #else
  282. // KICAD_PLUGINDIR = CMAKE_INSTALL_FULL_LIBDIR path is the absolute path
  283. // corresponding to the install path used for constructing KICAD_USER_PLUGIN
  284. wxString tfname = wxString::FromUTF8Unchecked( KICAD_PLUGINDIR );
  285. fn.Assign( tfname, "" );
  286. fn.AppendDir( wxT( "kicad" ) );
  287. fn.AppendDir( wxT( "plugins" ) );
  288. #endif
  289. fn.AppendDir( wxT( "3d" ) );
  290. return fn.GetPathWithSep();
  291. }
  292. wxString PATHS::GetStockDemosPath()
  293. {
  294. wxFileName fn;
  295. fn.AssignDir( PATHS::GetStockDataPath( false ) );
  296. fn.AppendDir( wxT( "demos" ) );
  297. return fn.GetPathWithSep();
  298. }
  299. wxString PATHS::GetUserCachePath()
  300. {
  301. wxString envPath;
  302. wxFileName tmp;
  303. tmp.AssignDir( KIPLATFORM::ENV::GetUserCachePath() );
  304. // Use KICAD_CACHE_HOME to allow the user to force a specific cache path.
  305. if( wxGetEnv( wxT( "KICAD_CACHE_HOME" ), &envPath ) && !envPath.IsEmpty() )
  306. {
  307. // Override the assignment above with KICAD_CACHE_HOME
  308. tmp.AssignDir( envPath );
  309. }
  310. tmp.AppendDir( KICAD_PATH_STR );
  311. tmp.AppendDir( GetMajorMinorVersion().ToStdString() );
  312. return tmp.GetPathWithSep();
  313. }
  314. wxString PATHS::GetDocumentationPath()
  315. {
  316. wxString path;
  317. #if defined( __WXMAC__ )
  318. path = GetOSXKicadDataDir();
  319. #elif defined( __WXMSW__ )
  320. path = getWindowsKiCadRoot() + wxT( "share/doc/kicad" );
  321. #else
  322. path = wxString::FromUTF8Unchecked( KICAD_DOCS );
  323. #endif
  324. return path;
  325. }
  326. wxString PATHS::GetInstanceCheckerPath()
  327. {
  328. wxFileName path;
  329. path.AssignDir( wxStandardPaths::Get().GetTempDir() );
  330. path.AppendDir( "org.kicad.kicad" );
  331. path.AppendDir( "instances" );
  332. return path.GetPathWithSep();
  333. }
  334. wxString PATHS::GetLogsPath()
  335. {
  336. wxFileName tmp;
  337. getUserDocumentPath( tmp );
  338. tmp.AppendDir( wxT( "logs" ) );
  339. return tmp.GetPath();
  340. }
  341. bool PATHS::EnsurePathExists( const wxString& aPath, bool aPathToFile )
  342. {
  343. wxString pathString = aPath;
  344. if( !aPathToFile )
  345. {
  346. // ensures the path is treated fully as directory
  347. pathString += wxFileName::GetPathSeparator();
  348. }
  349. wxFileName path( pathString );
  350. if( !path.MakeAbsolute() )
  351. {
  352. return false;
  353. }
  354. if( !wxFileName::DirExists( path.GetPath() ) )
  355. {
  356. if( !wxFileName::Mkdir( path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
  357. {
  358. return false;
  359. }
  360. }
  361. return true;
  362. }
  363. void PATHS::EnsureUserPathsExist()
  364. {
  365. EnsurePathExists( GetUserCachePath() );
  366. EnsurePathExists( GetUserPluginsPath() );
  367. EnsurePathExists( GetUserScriptingPath() );
  368. EnsurePathExists( GetUserTemplatesPath() );
  369. EnsurePathExists( GetDefaultUserProjectsPath() );
  370. EnsurePathExists( GetDefaultUserSymbolsPath() );
  371. EnsurePathExists( GetDefaultUserFootprintsPath() );
  372. EnsurePathExists( GetDefaultUser3DModelsPath() );
  373. EnsurePathExists( GetDefault3rdPartyPath() );
  374. }
  375. #ifdef __WXMAC__
  376. wxString PATHS::GetOSXKicadUserDataDir()
  377. {
  378. // According to wxWidgets documentation for GetUserDataDir:
  379. // Mac: ~/Library/Application Support/appname
  380. wxFileName udir( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
  381. // Since appname is different if started via launcher or standalone binary
  382. // map all to "kicad" here
  383. udir.RemoveLastDir();
  384. udir.AppendDir( wxT( "kicad" ) );
  385. return udir.GetPath();
  386. }
  387. wxString PATHS::GetOSXKicadMachineDataDir()
  388. {
  389. // 6.0 forward: Same as the main data dir
  390. return GetOSXKicadDataDir();
  391. }
  392. wxString PATHS::GetOSXKicadDataDir()
  393. {
  394. // According to wxWidgets documentation for GetDataDir:
  395. // Mac: appname.app/Contents/SharedSupport bundle subdirectory
  396. wxFileName ddir( wxStandardPaths::Get().GetDataDir(), wxEmptyString );
  397. // This must be mapped to main bundle for everything but kicad.app
  398. const wxArrayString dirs = ddir.GetDirs();
  399. // Check if we are the main kicad binary. in this case, the path will be
  400. // /path/to/bundlename.app/Contents/SharedSupport
  401. // If we are an aux binary, the path will be something like
  402. // /path/to/bundlename.app/Contents/Applications/<standalone>.app/Contents/SharedSupport
  403. if( dirs.GetCount() >= 6 &&
  404. dirs[dirs.GetCount() - 4] == wxT( "Applications" ) &&
  405. dirs[dirs.GetCount() - 6].Lower().EndsWith( wxT( "app" ) ) )
  406. {
  407. ddir.RemoveLastDir();
  408. ddir.RemoveLastDir();
  409. ddir.RemoveLastDir();
  410. ddir.RemoveLastDir();
  411. ddir.AppendDir( wxT( "SharedSupport" ) );
  412. }
  413. return ddir.GetPath();
  414. }
  415. #endif
  416. #ifdef _WIN32
  417. wxString PATHS::GetWindowsFontConfigDir()
  418. {
  419. wxFileName fn;
  420. fn.AssignDir( getWindowsKiCadRoot() );
  421. fn.AppendDir( wxS( "etc" ) );
  422. fn.AppendDir( wxS( "fonts" ) );
  423. return fn.GetPathWithSep();
  424. }
  425. wxString PATHS::getWindowsKiCadRoot()
  426. {
  427. wxFileName root( GetExecutablePath() + wxT( "/../" ) );
  428. root.MakeAbsolute();
  429. return root.GetPathWithSep();
  430. }
  431. #endif
  432. wxString PATHS::GetUserSettingsPath()
  433. {
  434. static wxString user_settings_path;
  435. if( user_settings_path.empty() )
  436. user_settings_path = CalculateUserSettingsPath();
  437. return user_settings_path;
  438. }
  439. wxString PATHS::CalculateUserSettingsPath( bool aIncludeVer, bool aUseEnv )
  440. {
  441. wxFileName cfgpath;
  442. // http://docs.wxwidgets.org/3.0/classwx_standard_paths.html#a7c7cf595d94d29147360d031647476b0
  443. wxString envstr;
  444. if( aUseEnv && wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
  445. {
  446. // Override the assignment above with KICAD_CONFIG_HOME
  447. cfgpath.AssignDir( envstr );
  448. }
  449. else
  450. {
  451. cfgpath.AssignDir( KIPLATFORM::ENV::GetUserConfigPath() );
  452. cfgpath.AppendDir( TO_STR( KICAD_CONFIG_DIR ) );
  453. }
  454. if( aIncludeVer )
  455. cfgpath.AppendDir( GetMajorMinorVersion().ToStdString() );
  456. return cfgpath.GetPath();
  457. }
  458. const wxString& PATHS::GetExecutablePath()
  459. {
  460. static wxString exe_path;
  461. if( exe_path.empty() )
  462. {
  463. wxString bin_dir = wxStandardPaths::Get().GetExecutablePath();
  464. #ifdef __WXMAC__
  465. // On OSX GetExecutablePath() will always point to main
  466. // bundle directory, e.g., /Applications/kicad.app/
  467. wxFileName fn( bin_dir );
  468. WX_FILENAME::ResolvePossibleSymlinks( fn );
  469. if( fn.GetName() == wxT( "kicad" ) || fn.GetName() == wxT( "kicad-cli" ) )
  470. {
  471. // kicad launcher, so just remove the Contents/MacOS part
  472. fn.RemoveLastDir();
  473. fn.RemoveLastDir();
  474. }
  475. else
  476. {
  477. // standalone binaries live in Contents/Applications/<standalone>.app/Contents/MacOS
  478. fn.RemoveLastDir();
  479. fn.RemoveLastDir();
  480. fn.RemoveLastDir();
  481. fn.RemoveLastDir();
  482. fn.RemoveLastDir();
  483. }
  484. bin_dir = fn.GetPath() + wxT( "/" );
  485. #else
  486. // Use unix notation for paths. I am not sure this is a good idea,
  487. // but it simplifies compatibility between Windows and Unices.
  488. // However it is a potential problem in path handling under Windows.
  489. bin_dir.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
  490. // Remove file name form command line:
  491. while( bin_dir.Last() != '/' && !bin_dir.IsEmpty() )
  492. bin_dir.RemoveLast();
  493. #endif
  494. exe_path = bin_dir;
  495. }
  496. return exe_path;
  497. }