The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

872 lines
29KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "jucer_Module.h"
  18. #include "jucer_ProjectType.h"
  19. #include "../Project Saving/jucer_ProjectExporter.h"
  20. #include "../Project Saving/jucer_ProjectSaver.h"
  21. #include "jucer_AudioPluginModule.h"
  22. ModuleDescription::ModuleDescription (const File& manifest)
  23. : moduleInfo (JSON::parse (manifest)), manifestFile (manifest)
  24. {
  25. if (moduleInfo.isVoid() && manifestFile.exists())
  26. {
  27. var json;
  28. Result r (JSON::parse (manifestFile.loadFileAsString(), json));
  29. if (r.failed() && manifestFile.loadFileAsString().isNotEmpty())
  30. {
  31. DBG (r.getErrorMessage());
  32. jassertfalse; // broken JSON in a module manifest.
  33. }
  34. }
  35. }
  36. //==============================================================================
  37. ModuleList::ModuleList()
  38. {
  39. }
  40. ModuleList::ModuleList (const ModuleList& other)
  41. {
  42. modules.addCopiesOf (other.modules);
  43. }
  44. const ModuleDescription* ModuleList::getModuleWithID (const String& moduleID) const
  45. {
  46. for (int i = 0; i < modules.size(); ++i)
  47. {
  48. ModuleDescription* m = modules.getUnchecked(i);
  49. if (m->getID() == moduleID)
  50. return m;
  51. }
  52. return nullptr;
  53. }
  54. struct ModuleSorter
  55. {
  56. static int compareElements (const ModuleDescription* m1, const ModuleDescription* m2)
  57. {
  58. return m1->getID().compareIgnoreCase (m2->getID());
  59. }
  60. };
  61. void ModuleList::sort()
  62. {
  63. ModuleSorter sorter;
  64. modules.sort (sorter);
  65. }
  66. StringArray ModuleList::getIDs() const
  67. {
  68. StringArray results;
  69. for (int i = 0; i < modules.size(); ++i)
  70. results.add (modules.getUnchecked(i)->getID());
  71. results.sort (true);
  72. return results;
  73. }
  74. Result ModuleList::addAllModulesInFolder (const File& path)
  75. {
  76. const File moduleDef (path.getChildFile (ModuleDescription::getManifestFileName()));
  77. if (moduleDef.exists())
  78. {
  79. ModuleDescription m (moduleDef);
  80. if (! m.isValid())
  81. return Result::fail ("Failed to load module manifest: " + moduleDef.getFullPathName());
  82. modules.add (new ModuleDescription (m));
  83. }
  84. else
  85. {
  86. for (DirectoryIterator iter (path, false, "*", File::findDirectories); iter.next();)
  87. {
  88. Result r = addAllModulesInFolder (iter.getFile().getLinkedTarget());
  89. if (r.failed())
  90. return r;
  91. }
  92. }
  93. return Result::ok();
  94. }
  95. static Array<File> getAllPossibleModulePaths (Project& project)
  96. {
  97. StringArray paths;
  98. for (Project::ExporterIterator exporter (project); exporter.next();)
  99. {
  100. if (exporter->canLaunchProject())
  101. {
  102. for (int i = 0; i < project.getModules().getNumModules(); ++i)
  103. {
  104. const String path (exporter->getPathForModuleString (project.getModules().getModuleID (i)));
  105. if (path.isNotEmpty())
  106. paths.addIfNotAlreadyThere (path);
  107. }
  108. String oldPath (exporter->getLegacyModulePath());
  109. if (oldPath.isNotEmpty())
  110. paths.addIfNotAlreadyThere (oldPath);
  111. }
  112. }
  113. Array<File> files;
  114. for (int i = 0; i < paths.size(); ++i)
  115. {
  116. const File f (project.resolveFilename (paths[i]));
  117. if (f.isDirectory())
  118. {
  119. files.add (f);
  120. if (f.getChildFile ("modules").isDirectory())
  121. files.addIfNotAlreadyThere (f.getChildFile ("modules"));
  122. }
  123. }
  124. return files;
  125. }
  126. Result ModuleList::scanAllKnownFolders (Project& project)
  127. {
  128. modules.clear();
  129. Result result (Result::ok());
  130. const Array<File> modulePaths (getAllPossibleModulePaths (project));
  131. for (int i = 0; i < modulePaths.size(); ++i)
  132. {
  133. result = addAllModulesInFolder (modulePaths.getReference(i));
  134. if (result.failed())
  135. break;
  136. }
  137. sort();
  138. return result;
  139. }
  140. bool ModuleList::loadFromWebsite()
  141. {
  142. modules.clear();
  143. URL baseURL ("http://www.juce.com/juce/modules");
  144. URL url (baseURL.getChildURL ("modulelist.php"));
  145. const ScopedPointer<InputStream> in (url.createInputStream (false, nullptr, nullptr, String::empty, 10000));
  146. if (in == nullptr)
  147. return false;
  148. var infoList (JSON::parse (in->readEntireStreamAsString()));
  149. if (! infoList.isArray())
  150. return false;
  151. const Array<var>* moduleList = infoList.getArray();
  152. for (int i = 0; i < moduleList->size(); ++i)
  153. {
  154. const var& m = moduleList->getReference(i);
  155. const String file (m [Ids::file].toString());
  156. if (file.isNotEmpty())
  157. {
  158. ModuleDescription lm (m [Ids::info]);
  159. if (lm.isValid())
  160. {
  161. lm.url = baseURL.getChildURL (file);
  162. modules.add (new ModuleDescription (lm));
  163. }
  164. }
  165. }
  166. sort();
  167. return true;
  168. }
  169. //==============================================================================
  170. LibraryModule::LibraryModule (const ModuleDescription& d)
  171. : moduleInfo (d)
  172. {
  173. }
  174. bool LibraryModule::isAUPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); }
  175. bool LibraryModule::isVSTPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); }
  176. File LibraryModule::getModuleHeaderFile (const File& folder) const
  177. {
  178. return folder.getChildFile (moduleInfo.getHeaderName());
  179. }
  180. //==============================================================================
  181. void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
  182. {
  183. const File localModuleFolder (projectSaver.getLocalModuleFolder (getID()));
  184. const File localHeader (getModuleHeaderFile (localModuleFolder));
  185. localModuleFolder.createDirectory();
  186. if (projectSaver.project.getModules().shouldCopyModuleFilesLocally (getID()).getValue())
  187. {
  188. projectSaver.copyFolder (moduleInfo.getFolder(), localModuleFolder);
  189. }
  190. else
  191. {
  192. localModuleFolder.createDirectory();
  193. createLocalHeaderWrapper (projectSaver, getModuleHeaderFile (moduleInfo.getFolder()), localHeader);
  194. }
  195. out << CodeHelpers::createIncludeStatement (localHeader, projectSaver.getGeneratedCodeFolder()
  196. .getChildFile ("AppConfig.h")) << newLine;
  197. }
  198. static void writeGuardedInclude (OutputStream& out, StringArray paths, StringArray guards)
  199. {
  200. StringArray uniquePaths (paths);
  201. uniquePaths.removeDuplicates (false);
  202. if (uniquePaths.size() == 1)
  203. {
  204. out << "#include " << paths[0] << newLine;
  205. }
  206. else
  207. {
  208. for (int i = paths.size(); --i >= 0;)
  209. {
  210. for (int j = i; --j >= 0;)
  211. {
  212. if (paths[i] == paths[j] && guards[i] == guards[j])
  213. {
  214. paths.remove (i);
  215. guards.remove (i);
  216. }
  217. }
  218. }
  219. for (int i = 0; i < paths.size(); ++i)
  220. {
  221. out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
  222. << " #include " << paths[i] << newLine;
  223. }
  224. out << "#else" << newLine
  225. << " #error \"This file is designed to be used in an Introjucer-generated project!\"" << newLine
  226. << "#endif" << newLine;
  227. }
  228. }
  229. void LibraryModule::createLocalHeaderWrapper (ProjectSaver& projectSaver, const File& originalHeader, const File& localHeader) const
  230. {
  231. Project& project = projectSaver.project;
  232. MemoryOutputStream out;
  233. out << "// This is an auto-generated file to redirect any included" << newLine
  234. << "// module headers to the correct external folder." << newLine
  235. << newLine;
  236. StringArray paths, guards;
  237. for (Project::ExporterIterator exporter (project); exporter.next();)
  238. {
  239. const RelativePath headerFromProject (exporter->getModuleFolderRelativeToProject (getID(), projectSaver)
  240. .getChildFile (originalHeader.getFileName()));
  241. const RelativePath fileFromHere (headerFromProject.rebased (project.getProjectFolder(),
  242. localHeader.getParentDirectory(), RelativePath::unknown));
  243. paths.add (fileFromHere.toUnixStyle().quoted());
  244. guards.add ("defined (" + exporter->getExporterIdentifierMacro() + ")");
  245. }
  246. writeGuardedInclude (out, paths, guards);
  247. out << newLine;
  248. projectSaver.replaceFileIfDifferent (localHeader, out);
  249. }
  250. //==============================================================================
  251. void LibraryModule::prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
  252. {
  253. Project& project = exporter.getProject();
  254. exporter.addToExtraSearchPaths (exporter.getModuleFolderRelativeToProject (getID(), projectSaver).getParentDirectory());
  255. {
  256. Array<File> compiled;
  257. findAndAddCompiledCode (exporter, projectSaver, moduleInfo.getFolder(), compiled);
  258. if (project.getModules().shouldShowAllModuleFilesInProject (getID()).getValue())
  259. addBrowsableCode (exporter, projectSaver, compiled, moduleInfo.getFolder());
  260. }
  261. if (isVSTPluginHost (project))
  262. VSTHelpers::addVSTFolderToPath (exporter, exporter.extraSearchPaths);
  263. if (exporter.isXcode())
  264. {
  265. if (isAUPluginHost (project))
  266. exporter.xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
  267. const String frameworks (moduleInfo.moduleInfo [exporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString());
  268. exporter.xcodeFrameworks.addTokens (frameworks, ", ", String::empty);
  269. }
  270. else if (exporter.isLinux())
  271. {
  272. const String libs (moduleInfo.moduleInfo ["LinuxLibs"].toString());
  273. exporter.linuxLibs.addTokens (libs, ", ", String::empty);
  274. exporter.linuxLibs.trim();
  275. exporter.linuxLibs.sort (false);
  276. exporter.linuxLibs.removeDuplicates (false);
  277. }
  278. else if (exporter.isCodeBlocks())
  279. {
  280. const String libs (moduleInfo.moduleInfo ["mingwLibs"].toString());
  281. exporter.mingwLibs.addTokens (libs, ", ", String::empty);
  282. exporter.mingwLibs.trim();
  283. exporter.mingwLibs.sort (false);
  284. exporter.mingwLibs.removeDuplicates (false);
  285. }
  286. if (moduleInfo.isPluginClient())
  287. {
  288. if (shouldBuildVST (project).getValue()) VSTHelpers::prepareExporter (exporter, projectSaver);
  289. if (shouldBuildAU (project).getValue()) AUHelpers::prepareExporter (exporter, projectSaver);
  290. if (shouldBuildAAX (project).getValue()) AAXHelpers::prepareExporter (exporter, projectSaver);
  291. if (shouldBuildRTAS (project).getValue()) RTASHelpers::prepareExporter (exporter, projectSaver);
  292. }
  293. }
  294. void LibraryModule::createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props) const
  295. {
  296. if (isVSTPluginHost (exporter.getProject())
  297. && ! (moduleInfo.isPluginClient() && shouldBuildVST (exporter.getProject()).getValue()))
  298. VSTHelpers::createVSTPathEditor (exporter, props);
  299. if (moduleInfo.isPluginClient())
  300. {
  301. if (shouldBuildVST (exporter.getProject()).getValue()) VSTHelpers::createPropertyEditors (exporter, props);
  302. if (shouldBuildRTAS (exporter.getProject()).getValue()) RTASHelpers::createPropertyEditors (exporter, props);
  303. if (shouldBuildAAX (exporter.getProject()).getValue()) AAXHelpers::createPropertyEditors (exporter, props);
  304. }
  305. }
  306. void LibraryModule::getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
  307. {
  308. const File header (getModuleHeaderFile (moduleInfo.getFolder()));
  309. jassert (header.exists());
  310. StringArray lines;
  311. header.readLines (lines);
  312. for (int i = 0; i < lines.size(); ++i)
  313. {
  314. String line (lines[i].trim());
  315. if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
  316. {
  317. ScopedPointer <Project::ConfigFlag> config (new Project::ConfigFlag());
  318. config->sourceModuleID = getID();
  319. config->symbol = line.fromFirstOccurrenceOf (":", false, false).trim();
  320. if (config->symbol.length() > 2)
  321. {
  322. ++i;
  323. while (! (lines[i].contains ("*/") || lines[i].contains ("@see")))
  324. {
  325. if (lines[i].trim().isNotEmpty())
  326. config->description = config->description.trim() + " " + lines[i].trim();
  327. ++i;
  328. }
  329. config->description = config->description.upToFirstOccurrenceOf ("*/", false, false);
  330. config->value.referTo (project.getConfigFlag (config->symbol));
  331. flags.add (config.release());
  332. }
  333. }
  334. }
  335. }
  336. //==============================================================================
  337. static bool exporterTargetMatches (const String& test, String target)
  338. {
  339. StringArray validTargets;
  340. validTargets.addTokens (target, ",;", "");
  341. validTargets.trim();
  342. validTargets.removeEmptyStrings();
  343. if (validTargets.size() == 0)
  344. return true;
  345. for (int i = validTargets.size(); --i >= 0;)
  346. {
  347. const String& targetName = validTargets[i];
  348. if (targetName == test
  349. || (targetName.startsWithChar ('!') && test != targetName.substring (1).trimStart()))
  350. return true;
  351. }
  352. return false;
  353. }
  354. bool LibraryModule::fileTargetMatches (ProjectExporter& exporter, const String& target)
  355. {
  356. if (exporter.isXcode()) return exporterTargetMatches ("xcode", target);
  357. if (exporter.isWindows()) return exporterTargetMatches ("msvc", target);
  358. if (exporter.isLinux()) return exporterTargetMatches ("linux", target);
  359. if (exporter.isAndroid()) return exporterTargetMatches ("android", target);
  360. if (exporter.isCodeBlocks()) return exporterTargetMatches ("mingw", target);
  361. return target.isEmpty();
  362. }
  363. struct FileSorter
  364. {
  365. static int compareElements (const File& f1, const File& f2)
  366. {
  367. return f1.getFileName().compareIgnoreCase (f2.getFileName());
  368. }
  369. };
  370. void LibraryModule::findWildcardMatches (const File& localModuleFolder, const String& wildcardPath, Array<File>& result) const
  371. {
  372. String path (wildcardPath.upToLastOccurrenceOf ("/", false, false));
  373. String wildCard (wildcardPath.fromLastOccurrenceOf ("/", false, false));
  374. Array<File> tempList;
  375. FileSorter sorter;
  376. DirectoryIterator iter (localModuleFolder.getChildFile (path), false, wildCard);
  377. bool isHiddenFile;
  378. while (iter.next (nullptr, &isHiddenFile, nullptr, nullptr, nullptr, nullptr))
  379. if (! isHiddenFile)
  380. tempList.addSorted (sorter, iter.getFile());
  381. result.addArray (tempList);
  382. }
  383. void LibraryModule::findAndAddCompiledCode (ProjectExporter& exporter, ProjectSaver& projectSaver,
  384. const File& localModuleFolder, Array<File>& result) const
  385. {
  386. const var compileArray (moduleInfo.moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
  387. if (const Array<var>* const files = compileArray.getArray())
  388. {
  389. for (int i = 0; i < files->size(); ++i)
  390. {
  391. const var& file = files->getReference(i);
  392. const String filename (file ["file"].toString());
  393. if (filename.isNotEmpty()
  394. && fileTargetMatches (exporter, file ["target"].toString()))
  395. {
  396. const File compiledFile (localModuleFolder.getChildFile (filename));
  397. result.add (compiledFile);
  398. Project::Item item (projectSaver.addFileToGeneratedGroup (compiledFile));
  399. if (file ["warnings"].toString().equalsIgnoreCase ("disabled"))
  400. item.getShouldInhibitWarningsValue() = true;
  401. if (file ["stdcall"])
  402. item.getShouldUseStdCallValue() = true;
  403. }
  404. }
  405. }
  406. }
  407. void LibraryModule::getLocalCompiledFiles (const File& localModuleFolder, Array<File>& result) const
  408. {
  409. const var compileArray (moduleInfo.moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
  410. if (const Array<var>* const files = compileArray.getArray())
  411. {
  412. for (int i = 0; i < files->size(); ++i)
  413. {
  414. const var& file = files->getReference(i);
  415. const String filename (file ["file"].toString());
  416. if (filename.isNotEmpty()
  417. #if JUCE_MAC
  418. && exporterTargetMatches ("xcode", file ["target"].toString())
  419. #elif JUCE_WINDOWS
  420. && exporterTargetMatches ("msvc", file ["target"].toString())
  421. #elif JUCE_LINUX
  422. && exporterTargetMatches ("linux", file ["target"].toString())
  423. #endif
  424. )
  425. {
  426. result.add (localModuleFolder.getChildFile (filename));
  427. }
  428. }
  429. }
  430. }
  431. static void addFileWithGroups (Project::Item& group, const RelativePath& file, const String& path)
  432. {
  433. const int slash = path.indexOfChar (File::separator);
  434. if (slash >= 0)
  435. {
  436. const String topLevelGroup (path.substring (0, slash));
  437. const String remainingPath (path.substring (slash + 1));
  438. Project::Item newGroup (group.getOrCreateSubGroup (topLevelGroup));
  439. addFileWithGroups (newGroup, file, remainingPath);
  440. }
  441. else
  442. {
  443. if (! group.containsChildForFile (file))
  444. group.addRelativeFile (file, -1, false);
  445. }
  446. }
  447. void LibraryModule::findBrowseableFiles (const File& localModuleFolder, Array<File>& filesFound) const
  448. {
  449. const var filesArray (moduleInfo.moduleInfo ["browse"]);
  450. if (const Array<var>* const files = filesArray.getArray())
  451. for (int i = 0; i < files->size(); ++i)
  452. findWildcardMatches (localModuleFolder, files->getReference(i), filesFound);
  453. }
  454. void LibraryModule::addBrowsableCode (ProjectExporter& exporter, ProjectSaver& projectSaver,
  455. const Array<File>& compiled, const File& localModuleFolder) const
  456. {
  457. if (sourceFiles.size() == 0)
  458. findBrowseableFiles (localModuleFolder, sourceFiles);
  459. Project::Item sourceGroup (Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID()));
  460. const RelativePath moduleFromProject (exporter.getModuleFolderRelativeToProject (getID(), projectSaver));
  461. for (int i = 0; i < sourceFiles.size(); ++i)
  462. {
  463. const String pathWithinModule (FileHelpers::getRelativePathFrom (sourceFiles.getReference(i), localModuleFolder));
  464. // (Note: in exporters like MSVC we have to avoid adding the same file twice, even if one of those instances
  465. // is flagged as being excluded from the build, because this overrides the other and it fails to compile)
  466. if (exporter.canCopeWithDuplicateFiles() || ! compiled.contains (sourceFiles.getReference(i)))
  467. addFileWithGroups (sourceGroup,
  468. moduleFromProject.getChildFile (pathWithinModule),
  469. pathWithinModule);
  470. }
  471. sourceGroup.addFile (localModuleFolder.getChildFile (FileHelpers::getRelativePathFrom (moduleInfo.manifestFile,
  472. moduleInfo.getFolder())), -1, false);
  473. sourceGroup.addFile (getModuleHeaderFile (localModuleFolder), -1, false);
  474. exporter.getModulesGroup().state.addChild (sourceGroup.state.createCopy(), -1, nullptr);
  475. }
  476. //==============================================================================
  477. EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s)
  478. : project (p), state (s)
  479. {
  480. }
  481. ModuleDescription EnabledModuleList::getModuleInfo (const String& moduleID)
  482. {
  483. return ModuleDescription (getModuleInfoFile (moduleID));
  484. }
  485. bool EnabledModuleList::isModuleEnabled (const String& moduleID) const
  486. {
  487. for (int i = 0; i < state.getNumChildren(); ++i)
  488. if (state.getChild(i) [Ids::ID] == moduleID)
  489. return true;
  490. return false;
  491. }
  492. bool EnabledModuleList::isAudioPluginModuleMissing() const
  493. {
  494. return project.getProjectType().isAudioPlugin()
  495. && ! isModuleEnabled ("juce_audio_plugin_client");
  496. }
  497. Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID)
  498. {
  499. return state.getChildWithProperty (Ids::ID, moduleID)
  500. .getPropertyAsValue (Ids::showAllCode, getUndoManager());
  501. }
  502. File EnabledModuleList::getModuleInfoFile (const String& moduleID)
  503. {
  504. for (Project::ExporterIterator exporter (project); exporter.next();)
  505. {
  506. if (exporter->canLaunchProject())
  507. {
  508. const String path (exporter->getPathForModuleString (moduleID));
  509. if (path.isNotEmpty())
  510. {
  511. const File moduleFolder (project.resolveFilename (path));
  512. File f (moduleFolder.getChildFile (ModuleDescription::getManifestFileName()));
  513. if (f.exists())
  514. return f;
  515. f = moduleFolder.getChildFile (moduleID)
  516. .getChildFile (ModuleDescription::getManifestFileName());
  517. if (f.exists())
  518. return f;
  519. f = moduleFolder.getChildFile ("modules")
  520. .getChildFile (moduleID)
  521. .getChildFile (ModuleDescription::getManifestFileName());
  522. if (f.exists())
  523. return f;
  524. }
  525. }
  526. }
  527. return File::nonexistent;
  528. }
  529. File EnabledModuleList::getModuleFolder (const String& moduleID)
  530. {
  531. const File infoFile (getModuleInfoFile (moduleID));
  532. return infoFile.exists() ? infoFile.getParentDirectory()
  533. : File::nonexistent;
  534. }
  535. struct ModuleTreeSorter
  536. {
  537. static int compareElements (const ValueTree& m1, const ValueTree& m2)
  538. {
  539. return m1[Ids::ID].toString().compareIgnoreCase (m2[Ids::ID]);
  540. }
  541. };
  542. void EnabledModuleList::sortAlphabetically()
  543. {
  544. ModuleTreeSorter sorter;
  545. state.sort (sorter, getUndoManager(), false);
  546. }
  547. Value EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID)
  548. {
  549. return state.getChildWithProperty (Ids::ID, moduleID)
  550. .getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
  551. }
  552. void EnabledModuleList::addModule (const File& moduleManifestFile, bool copyLocally)
  553. {
  554. ModuleDescription info (moduleManifestFile);
  555. if (info.isValid())
  556. {
  557. const String moduleID (info.getID());
  558. if (! isModuleEnabled (moduleID))
  559. {
  560. ValueTree module (Ids::MODULES);
  561. module.setProperty (Ids::ID, moduleID, nullptr);
  562. state.addChild (module, -1, getUndoManager());
  563. sortAlphabetically();
  564. shouldShowAllModuleFilesInProject (moduleID) = true;
  565. shouldCopyModuleFilesLocally (moduleID) = copyLocally;
  566. String path (moduleManifestFile.getParentDirectory().getParentDirectory()
  567. .getRelativePathFrom (project.getProjectFolder()));
  568. for (Project::ExporterIterator exporter (project); exporter.next();)
  569. exporter->getPathForModuleValue (moduleID) = path;
  570. }
  571. }
  572. }
  573. void EnabledModuleList::removeModule (const String& moduleID)
  574. {
  575. for (int i = 0; i < state.getNumChildren(); ++i)
  576. if (state.getChild(i) [Ids::ID] == moduleID)
  577. state.removeChild (i, getUndoManager());
  578. for (Project::ExporterIterator exporter (project); exporter.next();)
  579. exporter->removePathForModule (moduleID);
  580. }
  581. void EnabledModuleList::createRequiredModules (OwnedArray<LibraryModule>& modules)
  582. {
  583. for (int i = 0; i < getNumModules(); ++i)
  584. {
  585. ModuleDescription info (getModuleInfo (getModuleID (i)));
  586. if (info.isValid())
  587. modules.add (new LibraryModule (info));
  588. }
  589. }
  590. StringArray EnabledModuleList::getAllModules() const
  591. {
  592. StringArray moduleIDs;
  593. for (int i = 0; i < getNumModules(); ++i)
  594. moduleIDs.add (getModuleID(i));
  595. return moduleIDs;
  596. }
  597. static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies)
  598. {
  599. ModuleDescription info (project.getModules().getModuleInfo (moduleID));
  600. if (info.isValid())
  601. {
  602. const var depsArray (info.moduleInfo ["dependencies"]);
  603. if (const Array<var>* const deps = depsArray.getArray())
  604. {
  605. for (int i = 0; i < deps->size(); ++i)
  606. {
  607. const var& d = deps->getReference(i);
  608. String uid (d [Ids::ID].toString());
  609. String version (d [Ids::version].toString());
  610. if (! dependencies.contains (uid, true))
  611. {
  612. dependencies.add (uid);
  613. getDependencies (project, uid, dependencies);
  614. }
  615. }
  616. }
  617. }
  618. }
  619. StringArray EnabledModuleList::getExtraDependenciesNeeded (const String& moduleID) const
  620. {
  621. StringArray dependencies, extraDepsNeeded;
  622. getDependencies (project, moduleID, dependencies);
  623. for (int i = 0; i < dependencies.size(); ++i)
  624. if ((! project.getModules().isModuleEnabled (dependencies[i])) && dependencies[i] != moduleID)
  625. extraDepsNeeded.add (dependencies[i]);
  626. return extraDepsNeeded;
  627. }
  628. bool EnabledModuleList::areMostModulesCopiedLocally() const
  629. {
  630. int numYes = 0, numNo = 0;
  631. for (int i = project.getModules().getNumModules(); --i >= 0;)
  632. {
  633. if (project.getModules().shouldCopyModuleFilesLocally (project.getModules().getModuleID (i)).getValue())
  634. ++numYes;
  635. else
  636. ++numNo;
  637. }
  638. return numYes > numNo;
  639. }
  640. File EnabledModuleList::findDefaultModulesFolder (Project& project)
  641. {
  642. ModuleList available;
  643. available.scanAllKnownFolders (project);
  644. for (int i = available.modules.size(); --i >= 0;)
  645. {
  646. File f (available.modules.getUnchecked(i)->getFolder());
  647. if (f.isDirectory())
  648. return f.getParentDirectory();
  649. }
  650. return File::getCurrentWorkingDirectory();
  651. }
  652. void EnabledModuleList::addModuleFromUserSelectedFile()
  653. {
  654. static File lastLocation (findDefaultModulesFolder (project));
  655. FileChooser fc ("Select a module to add...", lastLocation, String::empty, false);
  656. if (fc.browseForDirectory())
  657. {
  658. lastLocation = fc.getResult();
  659. addModuleOfferingToCopy (lastLocation);
  660. }
  661. }
  662. void EnabledModuleList::addModuleInteractive (const String& moduleID)
  663. {
  664. ModuleList list;
  665. list.scanAllKnownFolders (project);
  666. if (const ModuleDescription* info = list.getModuleWithID (moduleID))
  667. addModule (info->manifestFile, areMostModulesCopiedLocally());
  668. else
  669. addModuleFromUserSelectedFile();
  670. }
  671. void EnabledModuleList::addModuleOfferingToCopy (const File& f)
  672. {
  673. ModuleDescription m (f);
  674. if (! m.isValid())
  675. m = ModuleDescription (f.getChildFile (ModuleDescription::getManifestFileName()));
  676. if (! m.isValid())
  677. {
  678. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  679. "Add Module", "This wasn't a valid module folder!");
  680. return;
  681. }
  682. if (isModuleEnabled (m.getID()))
  683. {
  684. AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
  685. "Add Module", "The project already contains this module!");
  686. return;
  687. }
  688. addModule (m.manifestFile, areMostModulesCopiedLocally());
  689. }