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.

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