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.

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