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.

885 lines
30KB

  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. //==============================================================================
  23. AvailableModuleList::AvailableModuleList()
  24. {
  25. }
  26. AvailableModuleList::AvailableModuleList (const AvailableModuleList& other)
  27. : moduleFolder (other.moduleFolder)
  28. {
  29. modules.addCopiesOf (other.modules);
  30. }
  31. AvailableModuleList& AvailableModuleList::operator= (const AvailableModuleList& other)
  32. {
  33. moduleFolder = other.moduleFolder;
  34. modules.clear();
  35. modules.addCopiesOf (other.modules);
  36. return *this;
  37. }
  38. bool AvailableModuleList::operator== (const AvailableModuleList& other) const
  39. {
  40. if (modules.size() != other.modules.size())
  41. return false;
  42. for (int i = modules.size(); --i >= 0;)
  43. {
  44. const Module* m1 = modules.getUnchecked(i);
  45. const Module* m2 = other.findModuleInfo (m1->uid);
  46. if (m2 == nullptr || *m1 != *m2)
  47. return false;
  48. }
  49. return true;
  50. }
  51. bool AvailableModuleList::isLocalModulesFolderValid()
  52. {
  53. return isModulesFolder (getModulesFolderForJuceOrModulesFolder (getLocalModulesFolder (nullptr)));
  54. }
  55. static int getVersionElement (const String& v, int index)
  56. {
  57. StringArray parts;
  58. parts.addTokens (v, "., ", String::empty);
  59. return parts [parts.size() - index - 1].getIntValue();
  60. }
  61. static int getJuceVersion (const String& v)
  62. {
  63. return getVersionElement (v, 2) * 100000
  64. + getVersionElement (v, 1) * 1000
  65. + getVersionElement (v, 0);
  66. }
  67. static int getBuiltJuceVersion()
  68. {
  69. return JUCE_MAJOR_VERSION * 100000
  70. + JUCE_MINOR_VERSION * 1000
  71. + JUCE_BUILDNUMBER;
  72. }
  73. bool AvailableModuleList::isLibraryNewerThanIntrojucer()
  74. {
  75. AvailableModuleList list;
  76. list.rescan (getModulesFolderForJuceOrModulesFolder (getLocalModulesFolder (nullptr)));
  77. for (int i = list.modules.size(); --i >= 0;)
  78. {
  79. const Module* m = list.modules.getUnchecked(i);
  80. if (m->uid.startsWith ("juce_")
  81. && getJuceVersion (m->version) > getBuiltJuceVersion())
  82. return true;
  83. }
  84. return false;
  85. }
  86. bool AvailableModuleList::isJuceFolder (const File& folder)
  87. {
  88. return folder.getFileName().containsIgnoreCase ("juce")
  89. && isModulesFolder (folder.getChildFile ("modules"));
  90. }
  91. bool AvailableModuleList::isModulesFolder (const File& folder)
  92. {
  93. return folder.getFileName().equalsIgnoreCase ("modules")
  94. && folder.isDirectory();
  95. }
  96. bool AvailableModuleList::isJuceOrModulesFolder (const File& folder)
  97. {
  98. return isJuceFolder (folder) || isModulesFolder (folder);
  99. }
  100. File AvailableModuleList::getModulesFolderForJuceOrModulesFolder (const File& f)
  101. {
  102. if (f.getFileName() != "modules" && f.isDirectory() && f.getChildFile ("modules").isDirectory())
  103. return f.getChildFile ("modules");
  104. return f;
  105. }
  106. File AvailableModuleList::getModulesFolderForExporter (const ProjectExporter& exporter)
  107. {
  108. File f (exporter.getProject().resolveFilename (exporter.getJuceFolderString()));
  109. return getModulesFolderForJuceOrModulesFolder (f);
  110. }
  111. File AvailableModuleList::getDefaultModulesFolder (Project* project)
  112. {
  113. if (project != nullptr)
  114. {
  115. for (Project::ExporterIterator exporter (*project); exporter.next();)
  116. {
  117. const File f (getModulesFolderForExporter (*exporter));
  118. if (AvailableModuleList::isModulesFolder (f))
  119. return f;
  120. }
  121. }
  122. // Fall back to a default..
  123. #if JUCE_WINDOWS
  124. return File::getSpecialLocation (File::userDocumentsDirectory)
  125. #else
  126. return File::getSpecialLocation (File::userHomeDirectory)
  127. #endif
  128. .getChildFile ("juce")
  129. .getChildFile ("modules");
  130. }
  131. File AvailableModuleList::getLocalModulesFolder (Project* project)
  132. {
  133. File defaultJuceFolder (getDefaultModulesFolder (project));
  134. File f (getGlobalProperties().getValue ("lastJuceFolder", defaultJuceFolder.getFullPathName()));
  135. f = getModulesFolderForJuceOrModulesFolder (f);
  136. if ((! AvailableModuleList::isModulesFolder (f)) && AvailableModuleList::isModulesFolder (defaultJuceFolder))
  137. f = defaultJuceFolder;
  138. return f;
  139. }
  140. void AvailableModuleList::setLocalModulesFolder (const File& file)
  141. {
  142. //jassert (FileHelpers::isJuceFolder (file));
  143. getGlobalProperties().setValue ("lastJuceFolder", file.getFullPathName());
  144. }
  145. struct ModuleSorter
  146. {
  147. static int compareElements (const AvailableModuleList::Module* m1, const AvailableModuleList::Module* m2)
  148. {
  149. return m1->uid.compareIgnoreCase (m2->uid);
  150. }
  151. };
  152. void AvailableModuleList::sort()
  153. {
  154. ModuleSorter sorter;
  155. modules.sort (sorter);
  156. }
  157. void AvailableModuleList::rescan()
  158. {
  159. rescan (moduleFolder);
  160. }
  161. Result AvailableModuleList::rescan (const File& newModulesFolder)
  162. {
  163. modules.clear();
  164. moduleFolder = getModulesFolderForJuceOrModulesFolder (newModulesFolder);
  165. if (moduleFolder.isDirectory())
  166. {
  167. DirectoryIterator iter (moduleFolder, false, "*", File::findDirectories);
  168. while (iter.next())
  169. {
  170. const File moduleDef (iter.getFile().getLinkedTarget()
  171. .getChildFile (LibraryModule::getInfoFileName()));
  172. if (moduleDef.exists())
  173. {
  174. LibraryModule m (moduleDef);
  175. if (! m.isValid())
  176. return Result::fail ("Failed to load module manifest: " + moduleDef.getFullPathName());
  177. Module* info = new Module();
  178. modules.add (info);
  179. info->uid = m.getID();
  180. info->version = m.getVersion();
  181. info->name = m.moduleInfo ["name"];
  182. info->description = m.moduleInfo ["description"];
  183. info->license = m.moduleInfo ["license"];
  184. info->file = moduleDef;
  185. }
  186. }
  187. }
  188. sort();
  189. return Result::ok();
  190. }
  191. bool AvailableModuleList::loadFromWebsite()
  192. {
  193. modules.clear();
  194. URL baseURL ("http://www.juce.com/juce/modules");
  195. URL url (baseURL.getChildURL ("modulelist.php"));
  196. var infoList (JSON::parse (url.readEntireTextStream (false)));
  197. if (infoList.isArray())
  198. {
  199. const Array<var>* moduleList = infoList.getArray();
  200. for (int i = 0; i < moduleList->size(); ++i)
  201. {
  202. const var& m = moduleList->getReference(i);
  203. const String file (m ["file"].toString());
  204. if (file.isNotEmpty())
  205. {
  206. var moduleInfo (m ["info"]);
  207. LibraryModule lm (moduleInfo);
  208. if (lm.isValid())
  209. {
  210. Module* info = new Module();
  211. modules.add (info);
  212. info->uid = lm.getID();
  213. info->version = lm.getVersion();
  214. info->name = lm.getName();
  215. info->description = lm.getDescription();
  216. info->license = lm.getLicense();
  217. info->url = baseURL.getChildURL (file);
  218. }
  219. }
  220. }
  221. }
  222. sort();
  223. return infoList.isArray();
  224. }
  225. LibraryModule* AvailableModuleList::Module::create() const
  226. {
  227. return new LibraryModule (file);
  228. }
  229. bool AvailableModuleList::Module::operator== (const Module& other) const
  230. {
  231. return uid == other.uid
  232. && version == other.version
  233. && name == other.name
  234. && description == other.description
  235. && license == other.license
  236. && file == other.file
  237. && url == other.url;
  238. }
  239. bool AvailableModuleList::Module::operator!= (const Module& other) const
  240. {
  241. return ! operator== (other);
  242. }
  243. LibraryModule* AvailableModuleList::loadModule (const String& uid) const
  244. {
  245. if (const Module* const m = findModuleInfo (uid))
  246. return m->create();
  247. return nullptr;
  248. }
  249. const AvailableModuleList::Module* AvailableModuleList::findModuleInfo (const String& uid) const
  250. {
  251. for (int i = modules.size(); --i >= 0;)
  252. if (modules.getUnchecked(i)->uid == uid)
  253. return modules.getUnchecked(i);
  254. return nullptr;
  255. }
  256. void AvailableModuleList::getDependencies (const String& moduleID, StringArray& dependencies) const
  257. {
  258. ScopedPointer<LibraryModule> m (loadModule (moduleID));
  259. if (m != nullptr)
  260. {
  261. const var depsArray (m->moduleInfo ["dependencies"]);
  262. if (const Array<var>* const deps = depsArray.getArray())
  263. {
  264. for (int i = 0; i < deps->size(); ++i)
  265. {
  266. const var& d = deps->getReference(i);
  267. String uid (d ["id"].toString());
  268. String version (d ["version"].toString());
  269. if (! dependencies.contains (uid, true))
  270. {
  271. dependencies.add (uid);
  272. getDependencies (uid, dependencies);
  273. }
  274. }
  275. }
  276. }
  277. }
  278. void AvailableModuleList::createDependencies (const String& moduleID, OwnedArray<LibraryModule>&) const
  279. {
  280. ScopedPointer<LibraryModule> m (loadModule (moduleID));
  281. if (m != nullptr)
  282. {
  283. const var depsArray (m->moduleInfo ["dependencies"]);
  284. if (const Array<var>* const deps = depsArray.getArray())
  285. {
  286. for (int i = 0; i < deps->size(); ++i)
  287. {
  288. const var& d = deps->getReference(i);
  289. String uid (d ["id"].toString());
  290. String version (d ["version"].toString());
  291. //xxx to do - also need to find version conflicts
  292. jassertfalse;
  293. }
  294. }
  295. }
  296. }
  297. StringArray AvailableModuleList::getExtraDependenciesNeeded (Project& project, const AvailableModuleList::Module& m)
  298. {
  299. StringArray dependencies, extraDepsNeeded;
  300. getDependencies (m.uid, dependencies);
  301. for (int i = 0; i < dependencies.size(); ++i)
  302. if ((! project.getModules().isModuleEnabled (dependencies[i])) && dependencies[i] != m.uid)
  303. extraDepsNeeded.add (dependencies[i]);
  304. return extraDepsNeeded;
  305. }
  306. //==============================================================================
  307. LibraryModule::LibraryModule (const File& file)
  308. : moduleInfo (JSON::parse (file)),
  309. moduleFile (file),
  310. moduleFolder (file.getParentDirectory())
  311. {
  312. }
  313. LibraryModule::LibraryModule (const var& info)
  314. : moduleInfo (info)
  315. {
  316. }
  317. bool LibraryModule::isValid() const { return getID().isNotEmpty(); }
  318. bool LibraryModule::isPluginClient() const { return getID() == "juce_audio_plugin_client"; }
  319. bool LibraryModule::isAUPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_AU"); }
  320. bool LibraryModule::isVSTPluginHost (const Project& project) const { return getID() == "juce_audio_processors" && project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"); }
  321. File LibraryModule::getInclude (const File& folder) const
  322. {
  323. return folder.getChildFile (moduleInfo ["include"].toString());
  324. }
  325. RelativePath LibraryModule::getModuleRelativeToProject (ProjectExporter& exporter) const
  326. {
  327. RelativePath p (exporter.getJuceFolderString(), RelativePath::projectFolder);
  328. if (p.getFileName() != "modules")
  329. p = p.getChildFile ("modules");
  330. return p.getChildFile (getID());
  331. }
  332. RelativePath LibraryModule::getModuleOrLocalCopyRelativeToProject (ProjectExporter& exporter, const File& localModuleFolder) const
  333. {
  334. if (exporter.getProject().getModules().shouldCopyModuleFilesLocally (getID()).getValue())
  335. return RelativePath (exporter.getProject().getRelativePathForFile (localModuleFolder), RelativePath::projectFolder);
  336. return getModuleRelativeToProject (exporter);
  337. }
  338. //==============================================================================
  339. void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
  340. {
  341. const File localModuleFolder (projectSaver.getLocalModuleFolder (*this));
  342. const File localHeader (getInclude (localModuleFolder));
  343. if (projectSaver.getProject().getModules().shouldCopyModuleFilesLocally (getID()).getValue())
  344. {
  345. projectSaver.copyFolder (moduleFolder, localModuleFolder);
  346. }
  347. else
  348. {
  349. localModuleFolder.createDirectory();
  350. createLocalHeaderWrapper (projectSaver, getInclude (moduleFolder), localHeader);
  351. }
  352. out << CodeHelpers::createIncludeStatement (localHeader, projectSaver.getGeneratedCodeFolder().getChildFile ("AppConfig.h")) << newLine;
  353. }
  354. static void writeGuardedInclude (OutputStream& out, StringArray paths, StringArray guards)
  355. {
  356. StringArray uniquePaths (paths);
  357. uniquePaths.removeDuplicates (false);
  358. if (uniquePaths.size() == 1)
  359. {
  360. out << "#include " << paths[0] << newLine;
  361. }
  362. else
  363. {
  364. for (int i = paths.size(); --i >= 0;)
  365. {
  366. for (int j = i; --j >= 0;)
  367. {
  368. if (paths[i] == paths[j] && guards[i] == guards[j])
  369. {
  370. paths.remove (i);
  371. guards.remove (i);
  372. }
  373. }
  374. }
  375. for (int i = 0; i < paths.size(); ++i)
  376. {
  377. out << (i == 0 ? "#if " : "#elif ") << guards[i] << newLine
  378. << " #include " << paths[i] << newLine;
  379. }
  380. out << "#else" << newLine
  381. << " #error \"This file is designed to be used in an Introjucer-generated project!\"" << newLine
  382. << "#endif" << newLine;
  383. }
  384. }
  385. void LibraryModule::createLocalHeaderWrapper (ProjectSaver& projectSaver, const File& originalHeader, const File& localHeader) const
  386. {
  387. Project& project = projectSaver.getProject();
  388. MemoryOutputStream out;
  389. out << "// This is an auto-generated file to redirect any included" << newLine
  390. << "// module headers to the correct external folder." << newLine
  391. << newLine;
  392. StringArray paths, guards;
  393. for (Project::ExporterIterator exporter (project); exporter.next();)
  394. {
  395. const RelativePath headerFromProject (getModuleRelativeToProject (*exporter)
  396. .getChildFile (originalHeader.getFileName()));
  397. const RelativePath fileFromHere (headerFromProject.rebased (project.getFile().getParentDirectory(),
  398. localHeader.getParentDirectory(), RelativePath::unknown));
  399. paths.add (fileFromHere.toUnixStyle().quoted());
  400. guards.add ("defined (" + exporter->getExporterIdentifierMacro() + ")");
  401. }
  402. writeGuardedInclude (out, paths, guards);
  403. out << newLine;
  404. projectSaver.replaceFileIfDifferent (localHeader, out);
  405. }
  406. //==============================================================================
  407. File LibraryModule::getLocalFolderFor (Project& project) const
  408. {
  409. if (project.getModules().shouldCopyModuleFilesLocally (getID()).getValue())
  410. return project.getGeneratedCodeFolder().getChildFile ("modules").getChildFile (getID());
  411. return moduleFolder;
  412. }
  413. void LibraryModule::prepareExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
  414. {
  415. Project& project = exporter.getProject();
  416. File localFolder (moduleFolder);
  417. if (project.getModules().shouldCopyModuleFilesLocally (getID()).getValue())
  418. localFolder = projectSaver.getLocalModuleFolder (*this);
  419. {
  420. Array<File> compiled;
  421. findAndAddCompiledCode (exporter, projectSaver, localFolder, compiled);
  422. if (project.getModules().shouldShowAllModuleFilesInProject (getID()).getValue())
  423. addBrowsableCode (exporter, compiled, localFolder);
  424. }
  425. if (isVSTPluginHost (project))
  426. VSTHelpers::addVSTFolderToPath (exporter, exporter.extraSearchPaths);
  427. if (exporter.isXcode())
  428. {
  429. if (isAUPluginHost (project))
  430. exporter.xcodeFrameworks.addTokens ("AudioUnit CoreAudioKit", false);
  431. const String frameworks (moduleInfo [exporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString());
  432. exporter.xcodeFrameworks.addTokens (frameworks, ", ", String::empty);
  433. }
  434. else if (exporter.isLinux())
  435. {
  436. const String libs (moduleInfo ["LinuxLibs"].toString());
  437. exporter.linuxLibs.addTokens (libs, ", ", String::empty);
  438. exporter.linuxLibs.trim();
  439. exporter.linuxLibs.sort (false);
  440. exporter.linuxLibs.removeDuplicates (false);
  441. }
  442. else if (exporter.isCodeBlocks())
  443. {
  444. const String libs (moduleInfo ["mingwLibs"].toString());
  445. exporter.mingwLibs.addTokens (libs, ", ", String::empty);
  446. exporter.mingwLibs.trim();
  447. exporter.mingwLibs.sort (false);
  448. exporter.mingwLibs.removeDuplicates (false);
  449. }
  450. if (isPluginClient())
  451. {
  452. if (shouldBuildVST (project).getValue()) VSTHelpers::prepareExporter (exporter, projectSaver);
  453. if (shouldBuildAU (project).getValue()) AUHelpers::prepareExporter (exporter, projectSaver);
  454. if (shouldBuildAAX (project).getValue()) AAXHelpers::prepareExporter (exporter, projectSaver, localFolder);
  455. if (shouldBuildRTAS (project).getValue()) RTASHelpers::prepareExporter (exporter, projectSaver, localFolder);
  456. }
  457. }
  458. void LibraryModule::createPropertyEditors (ProjectExporter& exporter, PropertyListBuilder& props) const
  459. {
  460. if (isVSTPluginHost (exporter.getProject())
  461. && ! (isPluginClient() && shouldBuildVST (exporter.getProject()).getValue()))
  462. VSTHelpers::createVSTPathEditor (exporter, props);
  463. if (isPluginClient())
  464. {
  465. if (shouldBuildVST (exporter.getProject()).getValue()) VSTHelpers::createPropertyEditors (exporter, props);
  466. if (shouldBuildRTAS (exporter.getProject()).getValue()) RTASHelpers::createPropertyEditors (exporter, props);
  467. if (shouldBuildAAX (exporter.getProject()).getValue()) AAXHelpers::createPropertyEditors (exporter, props);
  468. }
  469. }
  470. void LibraryModule::getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
  471. {
  472. const File header (getInclude (moduleFolder));
  473. jassert (header.exists());
  474. StringArray lines;
  475. header.readLines (lines);
  476. for (int i = 0; i < lines.size(); ++i)
  477. {
  478. String line (lines[i].trim());
  479. if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
  480. {
  481. ScopedPointer <Project::ConfigFlag> config (new Project::ConfigFlag());
  482. config->sourceModuleID = getID();
  483. config->symbol = line.fromFirstOccurrenceOf (":", false, false).trim();
  484. if (config->symbol.length() > 2)
  485. {
  486. ++i;
  487. while (! (lines[i].contains ("*/") || lines[i].contains ("@see")))
  488. {
  489. if (lines[i].trim().isNotEmpty())
  490. config->description = config->description.trim() + " " + lines[i].trim();
  491. ++i;
  492. }
  493. config->description = config->description.upToFirstOccurrenceOf ("*/", false, false);
  494. config->value.referTo (project.getConfigFlag (config->symbol));
  495. flags.add (config.release());
  496. }
  497. }
  498. }
  499. }
  500. //==============================================================================
  501. static bool exporterTargetMatches (const String& test, String target)
  502. {
  503. StringArray validTargets;
  504. validTargets.addTokens (target, ",;", "");
  505. validTargets.trim();
  506. validTargets.removeEmptyStrings();
  507. if (validTargets.size() == 0)
  508. return true;
  509. for (int i = validTargets.size(); --i >= 0;)
  510. {
  511. const String& targetName = validTargets[i];
  512. if (targetName == test
  513. || (targetName.startsWithChar ('!') && test != targetName.substring (1).trimStart()))
  514. return true;
  515. }
  516. return false;
  517. }
  518. bool LibraryModule::fileTargetMatches (ProjectExporter& exporter, const String& target)
  519. {
  520. if (exporter.isXcode()) return exporterTargetMatches ("xcode", target);
  521. if (exporter.isWindows()) return exporterTargetMatches ("msvc", target);
  522. if (exporter.isLinux()) return exporterTargetMatches ("linux", target);
  523. if (exporter.isAndroid()) return exporterTargetMatches ("android", target);
  524. if (exporter.isCodeBlocks()) return exporterTargetMatches ("mingw", target);
  525. return target.isEmpty();
  526. }
  527. void LibraryModule::findWildcardMatches (const File& localModuleFolder, const String& wildcardPath, Array<File>& result) const
  528. {
  529. String path (wildcardPath.upToLastOccurrenceOf ("/", false, false));
  530. String wildCard (wildcardPath.fromLastOccurrenceOf ("/", false, false));
  531. Array<File> tempList;
  532. FileSorter sorter;
  533. DirectoryIterator iter (localModuleFolder.getChildFile (path), false, wildCard);
  534. bool isHiddenFile;
  535. while (iter.next (nullptr, &isHiddenFile, nullptr, nullptr, nullptr, nullptr))
  536. if (! isHiddenFile)
  537. tempList.addSorted (sorter, iter.getFile());
  538. result.addArray (tempList);
  539. }
  540. void LibraryModule::findAndAddCompiledCode (ProjectExporter& exporter, ProjectSaver& projectSaver,
  541. const File& localModuleFolder, Array<File>& result) const
  542. {
  543. const var compileArray (moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
  544. if (const Array<var>* const files = compileArray.getArray())
  545. {
  546. for (int i = 0; i < files->size(); ++i)
  547. {
  548. const var& file = files->getReference(i);
  549. const String filename (file ["file"].toString());
  550. if (filename.isNotEmpty()
  551. && fileTargetMatches (exporter, file ["target"].toString()))
  552. {
  553. const File compiledFile (localModuleFolder.getChildFile (filename));
  554. result.add (compiledFile);
  555. Project::Item item (projectSaver.addFileToGeneratedGroup (compiledFile));
  556. if (file ["warnings"].toString().equalsIgnoreCase ("disabled"))
  557. item.getShouldInhibitWarningsValue() = true;
  558. if (file ["stdcall"])
  559. item.getShouldUseStdCallValue() = true;
  560. }
  561. }
  562. }
  563. }
  564. void LibraryModule::getLocalCompiledFiles (const File& localModuleFolder, Array<File>& result) const
  565. {
  566. const var compileArray (moduleInfo ["compile"]); // careful to keep this alive while the array is in use!
  567. if (const Array<var>* const files = compileArray.getArray())
  568. {
  569. for (int i = 0; i < files->size(); ++i)
  570. {
  571. const var& file = files->getReference(i);
  572. const String filename (file ["file"].toString());
  573. if (filename.isNotEmpty()
  574. #if JUCE_MAC
  575. && exporterTargetMatches ("xcode", file ["target"].toString())
  576. #elif JUCE_WINDOWS
  577. && exporterTargetMatches ("msvc", file ["target"].toString())
  578. #elif JUCE_LINUX
  579. && exporterTargetMatches ("linux", file ["target"].toString())
  580. #endif
  581. )
  582. {
  583. result.add (localModuleFolder.getChildFile (filename));
  584. }
  585. }
  586. }
  587. }
  588. static void addFileWithGroups (Project::Item& group, const RelativePath& file, const String& path)
  589. {
  590. const int slash = path.indexOfChar (File::separator);
  591. if (slash >= 0)
  592. {
  593. const String topLevelGroup (path.substring (0, slash));
  594. const String remainingPath (path.substring (slash + 1));
  595. Project::Item newGroup (group.getOrCreateSubGroup (topLevelGroup));
  596. addFileWithGroups (newGroup, file, remainingPath);
  597. }
  598. else
  599. {
  600. if (! group.containsChildForFile (file))
  601. group.addRelativeFile (file, -1, false);
  602. }
  603. }
  604. void LibraryModule::findBrowseableFiles (const File& localModuleFolder, Array<File>& filesFound) const
  605. {
  606. const var filesArray (moduleInfo ["browse"]);
  607. if (const Array<var>* const files = filesArray.getArray())
  608. for (int i = 0; i < files->size(); ++i)
  609. findWildcardMatches (localModuleFolder, files->getReference(i), filesFound);
  610. }
  611. void LibraryModule::addBrowsableCode (ProjectExporter& exporter, const Array<File>& compiled, const File& localModuleFolder) const
  612. {
  613. if (sourceFiles.size() == 0)
  614. findBrowseableFiles (localModuleFolder, sourceFiles);
  615. Project::Item sourceGroup (Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID()));
  616. const RelativePath moduleFromProject (getModuleOrLocalCopyRelativeToProject (exporter, localModuleFolder));
  617. for (int i = 0; i < sourceFiles.size(); ++i)
  618. {
  619. const String pathWithinModule (FileHelpers::getRelativePathFrom (sourceFiles.getReference(i), localModuleFolder));
  620. // (Note: in exporters like MSVC we have to avoid adding the same file twice, even if one of those instances
  621. // is flagged as being excluded from the build, because this overrides the other and it fails to compile)
  622. if (exporter.canCopeWithDuplicateFiles() || ! compiled.contains (sourceFiles.getReference(i)))
  623. addFileWithGroups (sourceGroup,
  624. moduleFromProject.getChildFile (pathWithinModule),
  625. pathWithinModule);
  626. }
  627. sourceGroup.addFile (localModuleFolder.getChildFile (FileHelpers::getRelativePathFrom (moduleFile, moduleFolder)), -1, false);
  628. sourceGroup.addFile (getInclude (localModuleFolder), -1, false);
  629. exporter.getModulesGroup().state.addChild (sourceGroup.state.createCopy(), -1, nullptr);
  630. }
  631. //==============================================================================
  632. EnabledModuleList::EnabledModuleList (Project& p, const ValueTree& s)
  633. : project (p), state (s)
  634. {
  635. }
  636. EnabledModuleList::EnabledModuleList (const EnabledModuleList& other)
  637. : project (other.project), state (other.state)
  638. {
  639. }
  640. const Identifier EnabledModuleList::modulesGroupTag ("MODULES");
  641. const Identifier EnabledModuleList::moduleTag ("MODULE");
  642. void EnabledModuleList::addDefaultModules (bool shouldCopyFilesLocally)
  643. {
  644. const char* mods[] =
  645. {
  646. "juce_core",
  647. "juce_events",
  648. "juce_graphics",
  649. "juce_data_structures",
  650. "juce_gui_basics",
  651. "juce_gui_extra",
  652. "juce_gui_audio",
  653. "juce_cryptography",
  654. "juce_video",
  655. "juce_opengl",
  656. "juce_audio_basics",
  657. "juce_audio_devices",
  658. "juce_audio_formats",
  659. "juce_audio_processors"
  660. };
  661. for (int i = 0; i < numElementsInArray (mods); ++i)
  662. addModule (mods[i], shouldCopyFilesLocally);
  663. }
  664. bool EnabledModuleList::isModuleEnabled (const String& moduleID) const
  665. {
  666. for (int i = 0; i < state.getNumChildren(); ++i)
  667. if (state.getChild(i) [Ids::ID] == moduleID)
  668. return true;
  669. return false;
  670. }
  671. bool EnabledModuleList::isAudioPluginModuleMissing() const
  672. {
  673. return project.getProjectType().isAudioPlugin()
  674. && ! isModuleEnabled ("juce_audio_plugin_client");
  675. }
  676. Value EnabledModuleList::shouldShowAllModuleFilesInProject (const String& moduleID)
  677. {
  678. return state.getChildWithProperty (Ids::ID, moduleID)
  679. .getPropertyAsValue (Ids::showAllCode, getUndoManager());
  680. }
  681. Value EnabledModuleList::shouldCopyModuleFilesLocally (const String& moduleID)
  682. {
  683. return state.getChildWithProperty (Ids::ID, moduleID)
  684. .getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
  685. }
  686. void EnabledModuleList::addModule (const String& moduleID, bool shouldCopyFilesLocally)
  687. {
  688. if (! isModuleEnabled (moduleID))
  689. {
  690. ValueTree module (moduleTag);
  691. module.setProperty (Ids::ID, moduleID, nullptr);
  692. state.addChild (module, -1, getUndoManager());
  693. shouldShowAllModuleFilesInProject (moduleID) = true;
  694. }
  695. if (shouldCopyFilesLocally)
  696. shouldCopyModuleFilesLocally (moduleID) = true;
  697. }
  698. void EnabledModuleList::removeModule (const String& moduleID)
  699. {
  700. for (int i = 0; i < state.getNumChildren(); ++i)
  701. if (state.getChild(i) [Ids::ID] == moduleID)
  702. state.removeChild (i, getUndoManager());
  703. }
  704. void EnabledModuleList::createRequiredModules (const AvailableModuleList& availableModules,
  705. OwnedArray<LibraryModule>& modules) const
  706. {
  707. for (int i = 0; i < availableModules.modules.size(); ++i)
  708. if (isModuleEnabled (availableModules.modules.getUnchecked(i)->uid))
  709. modules.add (availableModules.modules.getUnchecked(i)->create());
  710. }