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.

750 lines
25KB

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