1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 #include "../../Application/jucer_Headers.h"
27 #include "../../ProjectSaving/jucer_ProjectSaver.h"
28 #include "../../ProjectSaving/jucer_ProjectExport_Xcode.h"
29 #include "../../Application/jucer_Application.h"
30
31 //==============================================================================
LibraryModule(const ModuleDescription & d)32 LibraryModule::LibraryModule (const ModuleDescription& d)
33 : moduleInfo (d)
34 {
35 }
36
writeIncludes(ProjectSaver & projectSaver,OutputStream & out)37 void LibraryModule::writeIncludes (ProjectSaver& projectSaver, OutputStream& out)
38 {
39 auto& project = projectSaver.getProject();
40 auto& modules = project.getEnabledModules();
41
42 auto moduleID = getID();
43
44 if (modules.shouldCopyModuleFilesLocally (moduleID))
45 {
46 auto juceModuleFolder = moduleInfo.getFolder();
47
48 auto localModuleFolder = project.getLocalModuleFolder (moduleID);
49 localModuleFolder.createDirectory();
50 projectSaver.copyFolder (juceModuleFolder, localModuleFolder);
51 }
52
53 out << "#include <" << moduleInfo.getModuleFolder().getFileName() << "/"
54 << moduleInfo.getHeader().getFileName()
55 << ">" << newLine;
56 }
57
addSearchPathsToExporter(ProjectExporter & exporter) const58 void LibraryModule::addSearchPathsToExporter (ProjectExporter& exporter) const
59 {
60 auto moduleRelativePath = exporter.getModuleFolderRelativeToProject (getID());
61
62 exporter.addToExtraSearchPaths (moduleRelativePath.getParentDirectory());
63
64 String libDirPlatform;
65
66 if (exporter.isLinux())
67 libDirPlatform = "Linux";
68 else if (exporter.isCodeBlocks() && exporter.isWindows())
69 libDirPlatform = "MinGW";
70 else
71 libDirPlatform = exporter.getTargetFolder().getFileName();
72
73 auto libSubdirPath = moduleRelativePath.toUnixStyle() + "/libs/" + libDirPlatform;
74 auto moduleLibDir = File (exporter.getProject().getProjectFolder().getFullPathName() + "/" + libSubdirPath);
75
76 if (moduleLibDir.exists())
77 exporter.addToModuleLibPaths ({ libSubdirPath, moduleRelativePath.getRoot() });
78
79 auto extraInternalSearchPaths = moduleInfo.getExtraSearchPaths().trim();
80
81 if (extraInternalSearchPaths.isNotEmpty())
82 {
83 auto paths = StringArray::fromTokens (extraInternalSearchPaths, true);
84
85 for (auto& path : paths)
86 exporter.addToExtraSearchPaths (moduleRelativePath.getChildFile (path.unquoted()));
87 }
88 }
89
addDefinesToExporter(ProjectExporter & exporter) const90 void LibraryModule::addDefinesToExporter (ProjectExporter& exporter) const
91 {
92 auto extraDefs = moduleInfo.getPreprocessorDefs().trim();
93
94 if (extraDefs.isNotEmpty())
95 exporter.getExporterPreprocessorDefsValue() = exporter.getExporterPreprocessorDefsString() + "\n" + extraDefs;
96 }
97
addCompileUnitsToExporter(ProjectExporter & exporter,ProjectSaver & projectSaver) const98 void LibraryModule::addCompileUnitsToExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
99 {
100 auto& project = exporter.getProject();
101 auto& modules = project.getEnabledModules();
102
103 auto moduleID = getID();
104
105 auto localModuleFolder = modules.shouldCopyModuleFilesLocally (moduleID) ? project.getLocalModuleFolder (moduleID)
106 : moduleInfo.getFolder();
107
108 Array<File> compiled;
109 findAndAddCompiledUnits (exporter, &projectSaver, compiled);
110
111 if (modules.shouldShowAllModuleFilesInProject (moduleID))
112 addBrowseableCode (exporter, compiled, localModuleFolder);
113 }
114
addLibsToExporter(ProjectExporter & exporter) const115 void LibraryModule::addLibsToExporter (ProjectExporter& exporter) const
116 {
117 auto parseAndAddLibsToList = [] (StringArray& libList, const String& libs)
118 {
119 libList.addTokens (libs, ", ", {});
120 libList.trim();
121 libList.removeDuplicates (false);
122 };
123
124 auto& project = exporter.getProject();
125
126 if (exporter.isXcode())
127 {
128 auto& xcodeExporter = dynamic_cast<XcodeProjectExporter&> (exporter);
129
130 if (project.isAUPluginHost())
131 {
132 xcodeExporter.xcodeFrameworks.add ("CoreAudioKit");
133
134 if (xcodeExporter.isOSX())
135 xcodeExporter.xcodeFrameworks.add ("AudioUnit");
136 }
137
138 auto frameworks = moduleInfo.getModuleInfo() [xcodeExporter.isOSX() ? "OSXFrameworks" : "iOSFrameworks"].toString();
139 xcodeExporter.xcodeFrameworks.addTokens (frameworks, ", ", {});
140
141 parseAndAddLibsToList (xcodeExporter.xcodeLibs, moduleInfo.getModuleInfo() [exporter.isOSX() ? "OSXLibs" : "iOSLibs"].toString());
142 }
143 else if (exporter.isLinux())
144 {
145 parseAndAddLibsToList (exporter.linuxLibs, moduleInfo.getModuleInfo() ["linuxLibs"].toString());
146 parseAndAddLibsToList (exporter.linuxPackages, moduleInfo.getModuleInfo() ["linuxPackages"].toString());
147 }
148 else if (exporter.isWindows())
149 {
150 if (exporter.isCodeBlocks())
151 parseAndAddLibsToList (exporter.mingwLibs, moduleInfo.getModuleInfo() ["mingwLibs"].toString());
152 else
153 parseAndAddLibsToList (exporter.windowsLibs, moduleInfo.getModuleInfo() ["windowsLibs"].toString());
154 }
155 else if (exporter.isAndroid())
156 {
157 parseAndAddLibsToList (exporter.androidLibs, moduleInfo.getModuleInfo() ["androidLibs"].toString());
158 }
159 }
160
addSettingsForModuleToExporter(ProjectExporter & exporter,ProjectSaver & projectSaver) const161 void LibraryModule::addSettingsForModuleToExporter (ProjectExporter& exporter, ProjectSaver& projectSaver) const
162 {
163 addSearchPathsToExporter (exporter);
164 addDefinesToExporter (exporter);
165 addCompileUnitsToExporter (exporter, projectSaver);
166 addLibsToExporter (exporter);
167 }
168
getConfigFlags(Project & project,OwnedArray<Project::ConfigFlag> & flags) const169 void LibraryModule::getConfigFlags (Project& project, OwnedArray<Project::ConfigFlag>& flags) const
170 {
171 auto header = moduleInfo.getHeader();
172 jassert (header.exists());
173
174 StringArray lines;
175 header.readLines (lines);
176
177 for (int i = 0; i < lines.size(); ++i)
178 {
179 auto line = lines[i].trim();
180
181 if (line.startsWith ("/**") && line.containsIgnoreCase ("Config:"))
182 {
183 auto config = std::make_unique<Project::ConfigFlag>();
184 config->sourceModuleID = getID();
185 config->symbol = line.fromFirstOccurrenceOf (":", false, false).trim();
186
187 if (config->symbol.length() > 2)
188 {
189 ++i;
190
191 while (! (lines[i].contains ("*/") || lines[i].contains ("@see")))
192 {
193 if (lines[i].trim().isNotEmpty())
194 config->description = config->description.trim() + " " + lines[i].trim();
195
196 ++i;
197 }
198
199 config->description = config->description.upToFirstOccurrenceOf ("*/", false, false);
200 config->value = project.getConfigFlag (config->symbol);
201
202 i += 2;
203
204 if (lines[i].contains ("#define " + config->symbol))
205 {
206 auto value = lines[i].fromFirstOccurrenceOf ("#define " + config->symbol, false, true).trim();
207 config->value.setDefault (value == "0" ? false : true);
208 }
209
210 auto currentValue = config->value.get().toString();
211
212 if (currentValue == "enabled") config->value = true;
213 else if (currentValue == "disabled") config->value = false;
214
215 flags.add (std::move (config));
216 }
217 }
218 }
219 }
220
addFileWithGroups(Project::Item & group,const build_tools::RelativePath & file,const String & path)221 static void addFileWithGroups (Project::Item& group, const build_tools::RelativePath& file, const String& path)
222 {
223 auto slash = path.indexOfChar (File::getSeparatorChar());
224
225 if (slash >= 0)
226 {
227 auto topLevelGroup = path.substring (0, slash);
228 auto remainingPath = path.substring (slash + 1);
229
230 auto newGroup = group.getOrCreateSubGroup (topLevelGroup);
231 addFileWithGroups (newGroup, file, remainingPath);
232 }
233 else
234 {
235 if (! group.containsChildForFile (file))
236 group.addRelativeFile (file, -1, false);
237 }
238 }
239
240 struct FileSorter
241 {
compareElementsFileSorter242 static int compareElements (const File& f1, const File& f2)
243 {
244 return f1.getFileName().compareNatural (f2.getFileName());
245 }
246 };
247
findBrowseableFiles(const File & folder,Array<File> & filesFound) const248 void LibraryModule::findBrowseableFiles (const File& folder, Array<File>& filesFound) const
249 {
250 Array<File> tempList;
251 FileSorter sorter;
252
253 for (const auto& iter : RangedDirectoryIterator (folder, true, "*", File::findFiles))
254 if (! iter.isHidden() && iter.getFile().hasFileExtension (browseableFileExtensions))
255 tempList.addSorted (sorter, iter.getFile());
256
257 filesFound.addArray (tempList);
258 }
259
isNeededForExporter(ProjectExporter & exporter) const260 bool LibraryModule::CompileUnit::isNeededForExporter (ProjectExporter& exporter) const
261 {
262 if ((hasSuffix (file, "_OSX") && ! exporter.isOSX())
263 || (hasSuffix (file, "_iOS") && ! exporter.isiOS())
264 || (hasSuffix (file, "_Windows") && ! exporter.isWindows())
265 || (hasSuffix (file, "_Linux") && ! exporter.isLinux())
266 || (hasSuffix (file, "_Android") && ! exporter.isAndroid()))
267 return false;
268
269 auto targetType = Project::getTargetTypeFromFilePath (file, false);
270
271 if (targetType != build_tools::ProjectType::Target::unspecified && ! exporter.shouldBuildTargetType (targetType))
272 return false;
273
274 return exporter.usesMMFiles() ? isCompiledForObjC
275 : isCompiledForNonObjC;
276 }
277
getFilenameForProxyFile() const278 String LibraryModule::CompileUnit::getFilenameForProxyFile() const
279 {
280 return "include_" + file.getFileName();
281 }
282
hasSuffix(const File & f,const char * suffix)283 bool LibraryModule::CompileUnit::hasSuffix (const File& f, const char* suffix)
284 {
285 auto fileWithoutSuffix = f.getFileNameWithoutExtension() + ".";
286
287 return fileWithoutSuffix.containsIgnoreCase (suffix + String ("."))
288 || fileWithoutSuffix.containsIgnoreCase (suffix + String ("_"));
289 }
290
getAllCompileUnits(build_tools::ProjectType::Target::Type forTarget) const291 Array<LibraryModule::CompileUnit> LibraryModule::getAllCompileUnits (build_tools::ProjectType::Target::Type forTarget) const
292 {
293 auto files = getFolder().findChildFiles (File::findFiles, false);
294
295 FileSorter sorter;
296 files.sort (sorter);
297
298 Array<LibraryModule::CompileUnit> units;
299
300 for (auto& file : files)
301 {
302 if (file.getFileName().startsWithIgnoreCase (getID())
303 && file.hasFileExtension (sourceFileExtensions))
304 {
305 if (forTarget == build_tools::ProjectType::Target::unspecified
306 || forTarget == Project::getTargetTypeFromFilePath (file, true))
307 {
308 CompileUnit cu;
309 cu.file = file;
310 units.add (std::move (cu));
311 }
312 }
313 }
314
315 for (auto& cu : units)
316 {
317 cu.isCompiledForObjC = true;
318 cu.isCompiledForNonObjC = ! cu.file.hasFileExtension ("mm;m;metal");
319
320 if (cu.isCompiledForNonObjC)
321 if (cu.file.withFileExtension ("mm").existsAsFile())
322 cu.isCompiledForObjC = false;
323
324 jassert (cu.isCompiledForObjC || cu.isCompiledForNonObjC);
325 }
326
327 return units;
328 }
329
findAndAddCompiledUnits(ProjectExporter & exporter,ProjectSaver * projectSaver,Array<File> & result,build_tools::ProjectType::Target::Type forTarget) const330 void LibraryModule::findAndAddCompiledUnits (ProjectExporter& exporter,
331 ProjectSaver* projectSaver,
332 Array<File>& result,
333 build_tools::ProjectType::Target::Type forTarget) const
334 {
335 for (auto& cu : getAllCompileUnits (forTarget))
336 {
337 if (cu.isNeededForExporter (exporter))
338 {
339 auto localFile = exporter.getProject().getGeneratedCodeFolder()
340 .getChildFile (cu.getFilenameForProxyFile());
341 result.add (localFile);
342
343 if (projectSaver != nullptr)
344 projectSaver->addFileToGeneratedGroup (localFile);
345 }
346 }
347 }
348
addBrowseableCode(ProjectExporter & exporter,const Array<File> & compiled,const File & localModuleFolder) const349 void LibraryModule::addBrowseableCode (ProjectExporter& exporter, const Array<File>& compiled, const File& localModuleFolder) const
350 {
351 if (sourceFiles.isEmpty())
352 findBrowseableFiles (localModuleFolder, sourceFiles);
353
354 auto sourceGroup = Project::Item::createGroup (exporter.getProject(), getID(), "__mainsourcegroup" + getID(), false);
355 auto moduleFromProject = exporter.getModuleFolderRelativeToProject (getID());
356 auto moduleHeader = moduleInfo.getHeader();
357
358 auto& project = exporter.getProject();
359
360 if (project.getEnabledModules().shouldCopyModuleFilesLocally (getID()))
361 moduleHeader = project.getLocalModuleFolder (getID()).getChildFile (moduleHeader.getFileName());
362
363 auto isModuleHeader = [&] (const File& f) { return f.getFileName() == moduleHeader.getFileName(); };
364
365 for (auto& sourceFile : sourceFiles)
366 {
367 auto pathWithinModule = build_tools::getRelativePathFrom (sourceFile, localModuleFolder);
368
369 // (Note: in exporters like MSVC we have to avoid adding the same file twice, even if one of those instances
370 // is flagged as being excluded from the build, because this overrides the other and it fails to compile)
371 if ((exporter.canCopeWithDuplicateFiles() || ! compiled.contains (sourceFile)) && ! isModuleHeader (sourceFile))
372 addFileWithGroups (sourceGroup, moduleFromProject.getChildFile (pathWithinModule), pathWithinModule);
373 }
374
375 sourceGroup.sortAlphabetically (true, true);
376 sourceGroup.addFileAtIndex (moduleHeader, -1, false);
377
378 exporter.getModulesGroup().state.appendChild (sourceGroup.state.createCopy(), nullptr);
379 }
380
381 //==============================================================================
EnabledModulesList(Project & p,const ValueTree & s)382 EnabledModulesList::EnabledModulesList (Project& p, const ValueTree& s)
383 : project (p), state (s)
384 {
385 }
386
getAllModules() const387 StringArray EnabledModulesList::getAllModules() const
388 {
389 StringArray moduleIDs;
390
391 for (int i = 0; i < getNumModules(); ++i)
392 moduleIDs.add (getModuleID (i));
393
394 return moduleIDs;
395 }
396
createRequiredModules(OwnedArray<LibraryModule> & modules)397 void EnabledModulesList::createRequiredModules (OwnedArray<LibraryModule>& modules)
398 {
399 for (int i = 0; i < getNumModules(); ++i)
400 modules.add (new LibraryModule (getModuleInfo (getModuleID (i))));
401 }
402
sortAlphabetically()403 void EnabledModulesList::sortAlphabetically()
404 {
405 struct ModuleTreeSorter
406 {
407 static int compareElements (const ValueTree& m1, const ValueTree& m2)
408 {
409 return m1[Ids::ID].toString().compareIgnoreCase (m2[Ids::ID]);
410 }
411 };
412
413 ModuleTreeSorter sorter;
414
415 const ScopedLock sl (stateLock);
416 state.sort (sorter, getUndoManager(), false);
417 }
418
getDefaultModulesFolder() const419 File EnabledModulesList::getDefaultModulesFolder() const
420 {
421 File globalPath (getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString());
422
423 if (globalPath.exists())
424 return globalPath;
425
426 for (auto& exporterPathModule : project.getExporterPathsModulesList().getAllModules())
427 {
428 auto f = exporterPathModule.second;
429
430 if (f.isDirectory())
431 return f.getParentDirectory();
432 }
433
434 return File::getCurrentWorkingDirectory();
435 }
436
getModuleInfo(const String & moduleID) const437 ModuleDescription EnabledModulesList::getModuleInfo (const String& moduleID) const
438 {
439 return ModuleDescription (project.getModuleWithID (moduleID).second);
440 }
441
isModuleEnabled(const String & moduleID) const442 bool EnabledModulesList::isModuleEnabled (const String& moduleID) const
443 {
444 const ScopedLock sl (stateLock);
445 return state.getChildWithProperty (Ids::ID, moduleID).isValid();
446 }
447
getDependencies(Project & project,const String & moduleID,StringArray & dependencies)448 static void getDependencies (Project& project, const String& moduleID, StringArray& dependencies)
449 {
450 auto info = project.getEnabledModules().getModuleInfo (moduleID);
451
452 for (auto uid : info.getDependencies())
453 {
454 if (! dependencies.contains (uid, true))
455 {
456 dependencies.add (uid);
457 getDependencies (project, uid, dependencies);
458 }
459 }
460 }
461
getExtraDependenciesNeeded(const String & moduleID) const462 StringArray EnabledModulesList::getExtraDependenciesNeeded (const String& moduleID) const
463 {
464 StringArray dependencies, extraDepsNeeded;
465 getDependencies (project, moduleID, dependencies);
466
467 for (auto dep : dependencies)
468 if (dep != moduleID && ! isModuleEnabled (dep))
469 extraDepsNeeded.add (dep);
470
471 return extraDepsNeeded;
472 }
473
tryToFixMissingDependencies(const String & moduleID)474 bool EnabledModulesList::tryToFixMissingDependencies (const String& moduleID)
475 {
476 auto copyLocally = areMostModulesCopiedLocally();
477 auto useGlobalPath = areMostModulesUsingGlobalPath();
478
479 StringArray missing;
480
481 for (auto missingModule : getExtraDependenciesNeeded (moduleID))
482 {
483 auto mod = project.getModuleWithID (missingModule);
484
485 if (mod.second != File())
486 addModule (mod.second, copyLocally, useGlobalPath);
487 else
488 missing.add (missingModule);
489 }
490
491 return (missing.size() == 0);
492 }
493
doesModuleHaveHigherCppStandardThanProject(const String & moduleID) const494 bool EnabledModulesList::doesModuleHaveHigherCppStandardThanProject (const String& moduleID) const
495 {
496 auto projectCppStandard = project.getCppStandardString();
497
498 if (projectCppStandard == Project::getCppStandardVars().getLast().toString())
499 return false;
500
501 auto moduleCppStandard = getModuleInfo (moduleID).getMinimumCppStandard();
502
503 return (moduleCppStandard.getIntValue() > projectCppStandard.getIntValue());
504 }
505
shouldUseGlobalPath(const String & moduleID) const506 bool EnabledModulesList::shouldUseGlobalPath (const String& moduleID) const
507 {
508 const ScopedLock sl (stateLock);
509 return (bool) shouldUseGlobalPathValue (moduleID).getValue();
510 }
511
shouldUseGlobalPathValue(const String & moduleID) const512 Value EnabledModulesList::shouldUseGlobalPathValue (const String& moduleID) const
513 {
514 const ScopedLock sl (stateLock);
515 return state.getChildWithProperty (Ids::ID, moduleID)
516 .getPropertyAsValue (Ids::useGlobalPath, getUndoManager());
517 }
518
shouldShowAllModuleFilesInProject(const String & moduleID) const519 bool EnabledModulesList::shouldShowAllModuleFilesInProject (const String& moduleID) const
520 {
521 return (bool) shouldShowAllModuleFilesInProjectValue (moduleID).getValue();
522 }
523
shouldShowAllModuleFilesInProjectValue(const String & moduleID) const524 Value EnabledModulesList::shouldShowAllModuleFilesInProjectValue (const String& moduleID) const
525 {
526 const ScopedLock sl (stateLock);
527 return state.getChildWithProperty (Ids::ID, moduleID)
528 .getPropertyAsValue (Ids::showAllCode, getUndoManager());
529 }
530
shouldCopyModuleFilesLocally(const String & moduleID) const531 bool EnabledModulesList::shouldCopyModuleFilesLocally (const String& moduleID) const
532 {
533 return (bool) shouldCopyModuleFilesLocallyValue (moduleID).getValue();
534 }
535
shouldCopyModuleFilesLocallyValue(const String & moduleID) const536 Value EnabledModulesList::shouldCopyModuleFilesLocallyValue (const String& moduleID) const
537 {
538 const ScopedLock sl (stateLock);
539 return state.getChildWithProperty (Ids::ID, moduleID)
540 .getPropertyAsValue (Ids::useLocalCopy, getUndoManager());
541 }
542
areMostModulesUsingGlobalPath() const543 bool EnabledModulesList::areMostModulesUsingGlobalPath() const
544 {
545 int numYes = 0, numNo = 0;
546
547 for (auto i = getNumModules(); --i >= 0;)
548 {
549 if (shouldUseGlobalPath (getModuleID (i)))
550 ++numYes;
551 else
552 ++numNo;
553 }
554
555 return numYes > numNo;
556 }
557
areMostModulesCopiedLocally() const558 bool EnabledModulesList::areMostModulesCopiedLocally() const
559 {
560 int numYes = 0, numNo = 0;
561
562 for (auto i = getNumModules(); --i >= 0;)
563 {
564 if (shouldCopyModuleFilesLocally (getModuleID (i)))
565 ++numYes;
566 else
567 ++numNo;
568 }
569
570 return numYes > numNo;
571 }
572
getModulesWithHigherCppStandardThanProject() const573 StringArray EnabledModulesList::getModulesWithHigherCppStandardThanProject() const
574 {
575 StringArray list;
576
577 for (auto& module : getAllModules())
578 if (doesModuleHaveHigherCppStandardThanProject (module))
579 list.add (module);
580
581 return list;
582 }
583
getModulesWithMissingDependencies() const584 StringArray EnabledModulesList::getModulesWithMissingDependencies() const
585 {
586 StringArray list;
587
588 for (auto& module : getAllModules())
589 if (getExtraDependenciesNeeded (module).size() > 0)
590 list.add (module);
591
592 return list;
593 }
594
getHighestModuleCppStandard() const595 String EnabledModulesList::getHighestModuleCppStandard() const
596 {
597 auto highestCppStandard = Project::getCppStandardVars()[0].toString();
598
599 for (auto& mod : getAllModules())
600 {
601 auto moduleCppStandard = getModuleInfo (mod).getMinimumCppStandard();
602
603 if (moduleCppStandard == "latest")
604 return moduleCppStandard;
605
606 if (moduleCppStandard.getIntValue() > highestCppStandard.getIntValue())
607 highestCppStandard = moduleCppStandard;
608 }
609
610 return highestCppStandard;
611 }
612
addModule(const File & moduleFolder,bool copyLocally,bool useGlobalPath)613 void EnabledModulesList::addModule (const File& moduleFolder, bool copyLocally, bool useGlobalPath)
614 {
615 ModuleDescription info (moduleFolder);
616
617 if (info.isValid())
618 {
619 auto moduleID = info.getID();
620
621 if (! isModuleEnabled (moduleID))
622 {
623 ValueTree module (Ids::MODULE);
624 module.setProperty (Ids::ID, moduleID, getUndoManager());
625
626 {
627 const ScopedLock sl (stateLock);
628 state.appendChild (module, getUndoManager());
629 }
630
631 sortAlphabetically();
632
633 shouldShowAllModuleFilesInProjectValue (moduleID) = true;
634 shouldCopyModuleFilesLocallyValue (moduleID) = copyLocally;
635 shouldUseGlobalPathValue (moduleID) = useGlobalPath;
636
637 build_tools::RelativePath path (moduleFolder.getParentDirectory(),
638 project.getProjectFolder(),
639 build_tools::RelativePath::projectFolder);
640
641 for (Project::ExporterIterator exporter (project); exporter.next();)
642 exporter->getPathForModuleValue (moduleID) = path.toUnixStyle();
643
644 if (! useGlobalPath)
645 project.rescanExporterPathModules (false);
646 }
647 }
648 }
649
addModuleInteractive(const String & moduleID)650 void EnabledModulesList::addModuleInteractive (const String& moduleID)
651 {
652 auto f = project.getModuleWithID (moduleID).second;
653
654 if (f != File())
655 {
656 addModule (f, areMostModulesCopiedLocally(), areMostModulesUsingGlobalPath());
657 return;
658 }
659
660 addModuleFromUserSelectedFile();
661 }
662
addModuleFromUserSelectedFile()663 void EnabledModulesList::addModuleFromUserSelectedFile()
664 {
665 auto lastLocation = getDefaultModulesFolder();
666
667 FileChooser fc ("Select a module to add...", lastLocation, {});
668
669 if (fc.browseForDirectory())
670 {
671 lastLocation = fc.getResult();
672 addModuleOfferingToCopy (lastLocation, true);
673 }
674 }
675
addModuleOfferingToCopy(const File & f,bool isFromUserSpecifiedFolder)676 void EnabledModulesList::addModuleOfferingToCopy (const File& f, bool isFromUserSpecifiedFolder)
677 {
678 ModuleDescription m (f);
679
680 if (! m.isValid())
681 {
682 AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
683 "Add Module", "This wasn't a valid module folder!");
684 return;
685 }
686
687 if (isModuleEnabled (m.getID()))
688 {
689 AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
690 "Add Module", "The project already contains this module!");
691 return;
692 }
693
694 addModule (m.getModuleFolder(), areMostModulesCopiedLocally(),
695 isFromUserSpecifiedFolder ? false : areMostModulesUsingGlobalPath());
696 }
697
removeModule(String moduleID)698 void EnabledModulesList::removeModule (String moduleID) // must be pass-by-value, and not a const ref!
699 {
700 {
701 const ScopedLock sl (stateLock);
702
703 for (auto i = state.getNumChildren(); --i >= 0;)
704 if (state.getChild(i) [Ids::ID] == moduleID)
705 state.removeChild (i, getUndoManager());
706 }
707
708 for (Project::ExporterIterator exporter (project); exporter.next();)
709 exporter->removePathForModule (moduleID);
710 }
711