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.

787 lines
26KB

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