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.

790 lines
26KB

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