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.

680 lines
16 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. wxString envPath;
  283. // AppImages have a different path to the plugins, otherwise we end up with host sytem
  284. // plugins being loaded.
  285. if( wxGetEnv( wxT( "APPDIR" ), &envPath ) )
  286. {
  287. fn.Assign( envPath, wxEmptyString );
  288. fn.AppendDir( wxT( "usr" ) );
  289. fn.AppendDir( wxT( "lib" ) );
  290. fn.AppendDir( wxT( "x86_64-linux-gnu" ) );
  291. }
  292. else
  293. {
  294. // KICAD_PLUGINDIR = CMAKE_INSTALL_FULL_LIBDIR path is the absolute path
  295. // corresponding to the install path used for constructing KICAD_USER_PLUGIN
  296. wxString tfname = wxString::FromUTF8Unchecked( KICAD_PLUGINDIR );
  297. fn.Assign( tfname, "" );
  298. }
  299. fn.AppendDir( wxT( "kicad" ) );
  300. fn.AppendDir( wxT( "plugins" ) );
  301. #endif
  302. fn.AppendDir( wxT( "3d" ) );
  303. return fn.GetPathWithSep();
  304. }
  305. wxString PATHS::GetStockDemosPath()
  306. {
  307. wxFileName fn;
  308. fn.AssignDir( PATHS::GetStockDataPath( false ) );
  309. fn.AppendDir( wxT( "demos" ) );
  310. return fn.GetPathWithSep();
  311. }
  312. wxString PATHS::GetUserCachePath()
  313. {
  314. wxString envPath;
  315. wxFileName tmp;
  316. tmp.AssignDir( KIPLATFORM::ENV::GetUserCachePath() );
  317. // Use KICAD_CACHE_HOME to allow the user to force a specific cache path.
  318. if( wxGetEnv( wxT( "KICAD_CACHE_HOME" ), &envPath ) && !envPath.IsEmpty() )
  319. {
  320. // Override the assignment above with KICAD_CACHE_HOME
  321. tmp.AssignDir( envPath );
  322. }
  323. tmp.AppendDir( KICAD_PATH_STR );
  324. tmp.AppendDir( GetMajorMinorVersion().ToStdString() );
  325. return tmp.GetPathWithSep();
  326. }
  327. wxString PATHS::GetDocumentationPath()
  328. {
  329. wxString path;
  330. #if defined( __WXMAC__ )
  331. path = GetOSXKicadDataDir();
  332. #elif defined( __WXMSW__ )
  333. path = getWindowsKiCadRoot() + wxT( "share/doc/kicad" );
  334. #else
  335. path = wxString::FromUTF8Unchecked( KICAD_DOCS );
  336. #endif
  337. return path;
  338. }
  339. wxString PATHS::GetInstanceCheckerPath()
  340. {
  341. wxFileName path;
  342. path.AssignDir( wxStandardPaths::Get().GetTempDir() );
  343. path.AppendDir( "org.kicad.kicad" );
  344. path.AppendDir( "instances" );
  345. return path.GetPathWithSep();
  346. }
  347. wxString PATHS::GetLogsPath()
  348. {
  349. wxFileName tmp;
  350. getUserDocumentPath( tmp );
  351. tmp.AppendDir( wxT( "logs" ) );
  352. return tmp.GetPath();
  353. }
  354. bool PATHS::EnsurePathExists( const wxString& aPath, bool aPathToFile )
  355. {
  356. wxString pathString = aPath;
  357. if( !aPathToFile )
  358. {
  359. // ensures the path is treated fully as directory
  360. pathString += wxFileName::GetPathSeparator();
  361. }
  362. wxFileName path( pathString );
  363. if( !path.MakeAbsolute() )
  364. {
  365. return false;
  366. }
  367. if( !wxFileName::DirExists( path.GetPath() ) )
  368. {
  369. if( !wxFileName::Mkdir( path.GetPath(), wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) )
  370. {
  371. return false;
  372. }
  373. }
  374. return true;
  375. }
  376. void PATHS::EnsureUserPathsExist()
  377. {
  378. EnsurePathExists( GetUserCachePath() );
  379. EnsurePathExists( GetUserPluginsPath() );
  380. EnsurePathExists( GetUserScriptingPath() );
  381. EnsurePathExists( GetUserTemplatesPath() );
  382. EnsurePathExists( GetDefaultUserProjectsPath() );
  383. EnsurePathExists( GetDefaultUserSymbolsPath() );
  384. EnsurePathExists( GetDefaultUserFootprintsPath() );
  385. EnsurePathExists( GetDefaultUser3DModelsPath() );
  386. EnsurePathExists( GetDefault3rdPartyPath() );
  387. }
  388. #ifdef __WXMAC__
  389. wxString PATHS::GetOSXKicadUserDataDir()
  390. {
  391. // According to wxWidgets documentation for GetUserDataDir:
  392. // Mac: ~/Library/Application Support/appname
  393. wxFileName udir( wxStandardPaths::Get().GetUserDataDir(), wxEmptyString );
  394. // Since appname is different if started via launcher or standalone binary
  395. // map all to "kicad" here
  396. udir.RemoveLastDir();
  397. udir.AppendDir( wxT( "kicad" ) );
  398. return udir.GetPath();
  399. }
  400. wxString PATHS::GetOSXKicadMachineDataDir()
  401. {
  402. // 6.0 forward: Same as the main data dir
  403. return GetOSXKicadDataDir();
  404. }
  405. wxString PATHS::GetOSXKicadDataDir()
  406. {
  407. // According to wxWidgets documentation for GetDataDir:
  408. // Mac: appname.app/Contents/SharedSupport bundle subdirectory
  409. wxFileName ddir( wxStandardPaths::Get().GetDataDir(), wxEmptyString );
  410. // This must be mapped to main bundle for everything but kicad.app
  411. const wxArrayString dirs = ddir.GetDirs();
  412. // Check if we are the main kicad binary. in this case, the path will be
  413. // /path/to/bundlename.app/Contents/SharedSupport
  414. // If we are an aux binary, the path will be something like
  415. // /path/to/bundlename.app/Contents/Applications/<standalone>.app/Contents/SharedSupport
  416. if( dirs.GetCount() >= 6 &&
  417. dirs[dirs.GetCount() - 4] == wxT( "Applications" ) &&
  418. dirs[dirs.GetCount() - 6].Lower().EndsWith( wxT( "app" ) ) )
  419. {
  420. ddir.RemoveLastDir();
  421. ddir.RemoveLastDir();
  422. ddir.RemoveLastDir();
  423. ddir.RemoveLastDir();
  424. ddir.AppendDir( wxT( "SharedSupport" ) );
  425. }
  426. return ddir.GetPath();
  427. }
  428. #endif
  429. #ifdef _WIN32
  430. wxString PATHS::GetWindowsFontConfigDir()
  431. {
  432. wxFileName fn;
  433. fn.AssignDir( getWindowsKiCadRoot() );
  434. fn.AppendDir( wxS( "etc" ) );
  435. fn.AppendDir( wxS( "fonts" ) );
  436. return fn.GetPathWithSep();
  437. }
  438. wxString PATHS::getWindowsKiCadRoot()
  439. {
  440. wxFileName root( GetExecutablePath() + wxT( "/../" ) );
  441. root.MakeAbsolute();
  442. return root.GetPathWithSep();
  443. }
  444. #endif
  445. wxString PATHS::GetUserSettingsPath()
  446. {
  447. static wxString user_settings_path;
  448. if( user_settings_path.empty() )
  449. user_settings_path = CalculateUserSettingsPath();
  450. return user_settings_path;
  451. }
  452. wxString PATHS::CalculateUserSettingsPath( bool aIncludeVer, bool aUseEnv )
  453. {
  454. wxFileName cfgpath;
  455. // http://docs.wxwidgets.org/3.0/classwx_standard_paths.html#a7c7cf595d94d29147360d031647476b0
  456. wxString envstr;
  457. if( aUseEnv && wxGetEnv( wxT( "KICAD_CONFIG_HOME" ), &envstr ) && !envstr.IsEmpty() )
  458. {
  459. // Override the assignment above with KICAD_CONFIG_HOME
  460. cfgpath.AssignDir( envstr );
  461. }
  462. else
  463. {
  464. cfgpath.AssignDir( KIPLATFORM::ENV::GetUserConfigPath() );
  465. cfgpath.AppendDir( TO_STR( KICAD_CONFIG_DIR ) );
  466. }
  467. if( aIncludeVer )
  468. cfgpath.AppendDir( GetMajorMinorVersion().ToStdString() );
  469. return cfgpath.GetPath();
  470. }
  471. const wxString& PATHS::GetExecutablePath()
  472. {
  473. static wxString exe_path;
  474. if( exe_path.empty() )
  475. {
  476. wxString bin_dir = wxStandardPaths::Get().GetExecutablePath();
  477. #ifdef __WXMAC__
  478. // On OSX GetExecutablePath() will always point to main
  479. // bundle directory, e.g., /Applications/kicad.app/
  480. wxFileName fn( bin_dir );
  481. WX_FILENAME::ResolvePossibleSymlinks( fn );
  482. if( fn.GetName() == wxT( "kicad" ) || fn.GetName() == wxT( "kicad-cli" ) )
  483. {
  484. // kicad launcher, so just remove the Contents/MacOS part
  485. fn.RemoveLastDir();
  486. fn.RemoveLastDir();
  487. }
  488. else
  489. {
  490. // standalone binaries live in Contents/Applications/<standalone>.app/Contents/MacOS
  491. fn.RemoveLastDir();
  492. fn.RemoveLastDir();
  493. fn.RemoveLastDir();
  494. fn.RemoveLastDir();
  495. fn.RemoveLastDir();
  496. }
  497. bin_dir = fn.GetPath() + wxT( "/" );
  498. #else
  499. // Use unix notation for paths. I am not sure this is a good idea,
  500. // but it simplifies compatibility between Windows and Unices.
  501. // However it is a potential problem in path handling under Windows.
  502. bin_dir.Replace( WIN_STRING_DIR_SEP, UNIX_STRING_DIR_SEP );
  503. // Remove file name form command line:
  504. while( bin_dir.Last() != '/' && !bin_dir.IsEmpty() )
  505. bin_dir.RemoveLast();
  506. #endif
  507. exe_path = bin_dir;
  508. }
  509. return exe_path;
  510. }