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 "jucer_ProjectExporter.h"
28 #include "jucer_ProjectSaver.h"
29 
30 #include "jucer_ProjectExport_Make.h"
31 #include "jucer_ProjectExport_MSVC.h"
32 #include "jucer_ProjectExport_Xcode.h"
33 #include "jucer_ProjectExport_Android.h"
34 #include "jucer_ProjectExport_CodeBlocks.h"
35 
36 #include "jucer_ProjectExport_CLion.h"
37 
38 #include "../Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h"
39 
40 //==============================================================================
getExporterTypeInfos()41 std::vector<ProjectExporter::ExporterTypeInfo> ProjectExporter::getExporterTypeInfos()
42 {
43     auto createIcon = [] (const void* iconData, size_t iconDataSize)
44     {
45         Image image (Image::ARGB, 200, 200, true);
46         Graphics g (image);
47 
48         std::unique_ptr<Drawable> svgDrawable (Drawable::createFromImageData (iconData, iconDataSize));
49 
50         svgDrawable->drawWithin (g, image.getBounds().toFloat(), RectanglePlacement::fillDestination, 1.0f);
51 
52         return image;
53     };
54 
55     using namespace BinaryData;
56 
57     static std::vector<ProjectExporter::ExporterTypeInfo> infos
58     {
59         { XcodeProjectExporter::getValueTreeTypeNameMac(),
60           XcodeProjectExporter::getDisplayNameMac(),
61           XcodeProjectExporter::getTargetFolderNameMac(),
62           createIcon (export_xcode_svg, (size_t) export_xcode_svgSize) },
63         { XcodeProjectExporter::getValueTreeTypeNameiOS(),
64           XcodeProjectExporter::getDisplayNameiOS(),
65           XcodeProjectExporter::getTargetFolderNameiOS(),
66           createIcon (export_xcode_svg, (size_t) export_xcode_svgSize) },
67 
68         { MSVCProjectExporterVC2019::getValueTreeTypeName(),
69           MSVCProjectExporterVC2019::getDisplayName(),
70           MSVCProjectExporterVC2019::getTargetFolderName(),
71           createIcon (export_visualStudio_svg, export_visualStudio_svgSize) },
72         { MSVCProjectExporterVC2017::getValueTreeTypeName(),
73           MSVCProjectExporterVC2017::getDisplayName(),
74           MSVCProjectExporterVC2017::getTargetFolderName(),
75           createIcon (export_visualStudio_svg, export_visualStudio_svgSize) },
76         { MSVCProjectExporterVC2015::getValueTreeTypeName(),
77           MSVCProjectExporterVC2015::getDisplayName(),
78           MSVCProjectExporterVC2015::getTargetFolderName(),
79           createIcon (export_visualStudio_svg, export_visualStudio_svgSize) },
80 
81         { MakefileProjectExporter::getValueTreeTypeName(),
82           MakefileProjectExporter::getDisplayName(),
83           MakefileProjectExporter::getTargetFolderName(),
84           createIcon (export_linux_svg, export_linux_svgSize) },
85 
86         { AndroidProjectExporter::getValueTreeTypeName(),
87           AndroidProjectExporter::getDisplayName(),
88           AndroidProjectExporter::getTargetFolderName(),
89           createIcon (export_android_svg, export_android_svgSize) },
90 
91         { CodeBlocksProjectExporter::getValueTreeTypeNameWindows(),
92           CodeBlocksProjectExporter::getDisplayNameWindows(),
93           CodeBlocksProjectExporter::getTargetFolderNameWindows(),
94           createIcon (export_codeBlocks_svg, export_codeBlocks_svgSize) },
95         { CodeBlocksProjectExporter::getValueTreeTypeNameLinux(),
96           CodeBlocksProjectExporter::getDisplayNameLinux(),
97           CodeBlocksProjectExporter::getTargetFolderNameLinux(),
98           createIcon (export_codeBlocks_svg, export_codeBlocks_svgSize) },
99 
100         { CLionProjectExporter::getValueTreeTypeName(),
101           CLionProjectExporter::getDisplayName(),
102           CLionProjectExporter::getTargetFolderName(),
103           createIcon (export_clion_svg, export_clion_svgSize) }
104     };
105 
106     return infos;
107 }
108 
getTypeInfoForExporter(const Identifier & exporterIdentifier)109 ProjectExporter::ExporterTypeInfo ProjectExporter::getTypeInfoForExporter (const Identifier& exporterIdentifier)
110 {
111     auto typeInfos = getExporterTypeInfos();
112 
113     auto predicate = [exporterIdentifier] (const ProjectExporter::ExporterTypeInfo& info)
114     {
115         return info.identifier == exporterIdentifier;
116     };
117 
118     auto iter = std::find_if (typeInfos.begin(), typeInfos.end(),
119                               std::move (predicate));
120 
121     if (iter != typeInfos.end())
122         return *iter;
123 
124     return {};
125 }
126 
getCurrentPlatformExporterTypeInfo()127 ProjectExporter::ExporterTypeInfo ProjectExporter::getCurrentPlatformExporterTypeInfo()
128 {
129     #if JUCE_MAC
130      return ProjectExporter::getTypeInfoForExporter (XcodeProjectExporter::getValueTreeTypeNameMac());
131     #elif JUCE_WINDOWS
132      return ProjectExporter::getTypeInfoForExporter (MSVCProjectExporterVC2019::getValueTreeTypeName());
133     #elif JUCE_LINUX || JUCE_BSD
134      return ProjectExporter::getTypeInfoForExporter (MakefileProjectExporter::getValueTreeTypeName());
135     #else
136      #error "unknown platform!"
137     #endif
138 }
139 
createNewExporter(Project & project,const Identifier & exporterIdentifier)140 std::unique_ptr<ProjectExporter> ProjectExporter::createNewExporter (Project& project, const Identifier& exporterIdentifier)
141 {
142     auto exporter = createExporterFromSettings (project, ValueTree (exporterIdentifier));
143     jassert (exporter != nullptr);
144 
145     exporter->createDefaultConfigs();
146     exporter->createDefaultModulePaths();
147 
148     return exporter;
149 }
150 
createExporterFromSettings(Project & project,const ValueTree & settings)151 std::unique_ptr<ProjectExporter> ProjectExporter::createExporterFromSettings (Project& project, const ValueTree& settings)
152 {
153     std::unique_ptr<ProjectExporter> exporter;
154 
155     exporter.reset (XcodeProjectExporter::createForSettings (project, settings));
156     if (exporter == nullptr) exporter.reset (MSVCProjectExporterVC2019::createForSettings (project, settings));
157     if (exporter == nullptr) exporter.reset (MSVCProjectExporterVC2017::createForSettings (project, settings));
158     if (exporter == nullptr) exporter.reset (MSVCProjectExporterVC2015::createForSettings (project, settings));
159     if (exporter == nullptr) exporter.reset (MakefileProjectExporter::createForSettings (project, settings));
160     if (exporter == nullptr) exporter.reset (AndroidProjectExporter::createForSettings (project, settings));
161     if (exporter == nullptr) exporter.reset (CodeBlocksProjectExporter::createForSettings (project, settings));
162     if (exporter == nullptr) exporter.reset (CLionProjectExporter::createForSettings (project, settings));
163 
164     jassert (exporter != nullptr);
165     return exporter;
166 }
167 
canProjectBeLaunched(Project * project)168 bool ProjectExporter::canProjectBeLaunched (Project* project)
169 {
170     if (project != nullptr)
171     {
172         static Identifier types[]
173         {
174             #if JUCE_MAC
175              XcodeProjectExporter::getValueTreeTypeNameMac(),
176              XcodeProjectExporter::getValueTreeTypeNameiOS(),
177             #elif JUCE_WINDOWS
178              MSVCProjectExporterVC2019::getValueTreeTypeName(),
179              MSVCProjectExporterVC2017::getValueTreeTypeName(),
180              MSVCProjectExporterVC2015::getValueTreeTypeName(),
181             #endif
182              AndroidProjectExporter::getValueTreeTypeName()
183         };
184 
185         for (auto& exporterIdentifier : types)
186             if (project->getExporters().getChildWithName (exporterIdentifier).isValid())
187                 return true;
188     }
189 
190     return false;
191 }
192 
193 //==============================================================================
ProjectExporter(Project & p,const ValueTree & state)194 ProjectExporter::ProjectExporter (Project& p, const ValueTree& state)
195     : settings (state),
196       project (p),
197       projectType (p.getProjectType()),
198       projectName (p.getProjectNameString()),
199       projectFolder (p.getProjectFolder()),
200       targetLocationValue     (settings, Ids::targetFolder,        getUndoManager()),
201       extraCompilerFlagsValue (settings, Ids::extraCompilerFlags,  getUndoManager()),
202       extraLinkerFlagsValue   (settings, Ids::extraLinkerFlags,    getUndoManager()),
203       externalLibrariesValue  (settings, Ids::externalLibraries,   getUndoManager()),
204       userNotesValue          (settings, Ids::userNotes,           getUndoManager()),
205       gnuExtensionsValue      (settings, Ids::enableGNUExtensions, getUndoManager()),
206       bigIconValue            (settings, Ids::bigIcon,             getUndoManager()),
207       smallIconValue          (settings, Ids::smallIcon,           getUndoManager()),
208       extraPPDefsValue        (settings, Ids::extraDefs,           getUndoManager())
209 {
210     projectCompilerFlagSchemesValue = project.getProjectValue (Ids::compilerFlagSchemes);
211     projectCompilerFlagSchemesValue.addListener (this);
212     updateCompilerFlagValues();
213 }
214 
getUniqueName() const215 String ProjectExporter::getUniqueName() const
216 {
217     auto targetLocationString = getTargetLocationString();
218     auto defaultBuildsRootFolder = getDefaultBuildsRootFolder();
219 
220     auto typeInfos = getExporterTypeInfos();
221 
222     auto predicate = [targetLocationString, defaultBuildsRootFolder] (const ProjectExporter::ExporterTypeInfo& info)
223     {
224         return defaultBuildsRootFolder + info.targetFolder == targetLocationString;
225     };
226 
227     auto iter = std::find_if (typeInfos.begin(), typeInfos.end(),
228                               std::move (predicate));
229 
230     if (iter == typeInfos.end())
231         return name + " - " + targetLocationString;
232 
233     return name;
234 }
235 
getTargetFolder() const236 File ProjectExporter::getTargetFolder() const
237 {
238     return project.resolveFilename (getTargetLocationString());
239 }
240 
rebaseFromProjectFolderToBuildTarget(const build_tools::RelativePath & path) const241 build_tools::RelativePath ProjectExporter::rebaseFromProjectFolderToBuildTarget (const build_tools::RelativePath& path) const
242 {
243     return path.rebased (project.getProjectFolder(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder);
244 }
245 
shouldFileBeCompiledByDefault(const File & file) const246 bool ProjectExporter::shouldFileBeCompiledByDefault (const File& file) const
247 {
248     return file.hasFileExtension (cOrCppFileExtensions)
249         || file.hasFileExtension (asmFileExtensions);
250 }
251 
updateCompilerFlagValues()252 void ProjectExporter::updateCompilerFlagValues()
253 {
254     compilerFlagSchemesMap.clear();
255 
256     for (auto& scheme : project.getCompilerFlagSchemes())
257         compilerFlagSchemesMap.set (scheme, { settings, scheme, getUndoManager() });
258 }
259 
260 //==============================================================================
createPropertyEditors(PropertyListBuilder & props)261 void ProjectExporter::createPropertyEditors (PropertyListBuilder& props)
262 {
263     if (! isCLion())
264     {
265         props.add (new TextPropertyComponent (targetLocationValue, "Target Project Folder", 2048, false),
266                    "The location of the folder in which the " + name + " project will be created. "
267                    "This path can be absolute, but it's much more sensible to make it relative to the jucer project directory.");
268 
269         if ((shouldBuildTargetType (build_tools::ProjectType::Target::VSTPlugIn) && project.shouldBuildVST()) || (project.isVSTPluginHost() && supportsTargetType (build_tools::ProjectType::Target::VSTPlugIn)))
270         {
271             props.add (new FilePathPropertyComponent (vstLegacyPathValueWrapper.getWrappedValueWithDefault(), "VST (Legacy) SDK Folder", true,
272                                                       getTargetOSForExporter() == TargetOS::getThisOS(), "*", project.getProjectFolder()),
273                        "If you're building a VST plug-in or host, you can use this field to override the global VST (Legacy) SDK path with a project-specific path. "
274                        "This can be an absolute path, or a path relative to the Projucer project file.");
275         }
276 
277         if (shouldBuildTargetType (build_tools::ProjectType::Target::AAXPlugIn) && project.shouldBuildAAX())
278         {
279             props.add (new FilePathPropertyComponent (aaxPathValueWrapper.getWrappedValueWithDefault(), "AAX SDK Folder", true,
280                                                       getTargetOSForExporter() == TargetOS::getThisOS(), "*", project.getProjectFolder()),
281                        "If you're building an AAX plug-in, this must be the folder containing the AAX SDK. This can be an absolute path, or a path relative to the Projucer project file.");
282         }
283 
284         if (shouldBuildTargetType (build_tools::ProjectType::Target::RTASPlugIn) && project.shouldBuildRTAS())
285         {
286             props.add (new FilePathPropertyComponent (rtasPathValueWrapper.getWrappedValueWithDefault(), "RTAS SDK Folder", true,
287                                                       getTargetOSForExporter() == TargetOS::getThisOS(), "*", project.getProjectFolder()),
288                        "If you're building an RTAS plug-in, this must be the folder containing the RTAS SDK. This can be an absolute path, or a path relative to the Projucer project file.");
289         }
290 
291         props.add (new TextPropertyComponent (extraPPDefsValue, "Extra Preprocessor Definitions", 32768, true),
292                    "Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace, commas, "
293                    "or new-lines to separate the items - to include a space or comma in a definition, precede it with a backslash.");
294 
295         props.add (new TextPropertyComponent (extraCompilerFlagsValue, "Extra Compiler Flags", 8192, true),
296                    "Extra command-line flags to be passed to the compiler. This string can contain references to preprocessor definitions in the "
297                    "form ${NAME_OF_DEFINITION}, which will be replaced with their values.");
298 
299         for (HashMap<String, ValueWithDefault>::Iterator i (compilerFlagSchemesMap); i.next();)
300             props.add (new TextPropertyComponent (compilerFlagSchemesMap.getReference (i.getKey()), "Compiler Flags for " + i.getKey().quoted(), 8192, false),
301                        "The exporter-specific compiler flags that will be added to files using this scheme.");
302 
303         props.add (new TextPropertyComponent (extraLinkerFlagsValue, "Extra Linker Flags", 8192, true),
304                    "Extra command-line flags to be passed to the linker. You might want to use this for adding additional libraries. "
305                    "This string can contain references to preprocessor definitions in the form ${NAME_OF_VALUE}, which will be replaced with their values.");
306 
307         props.add (new TextPropertyComponent (externalLibrariesValue, "External Libraries to Link", 8192, true),
308                    "Additional libraries to link (one per line). You should not add any platform specific decoration to these names. "
309                    "This string can contain references to preprocessor definitions in the form ${NAME_OF_VALUE}, which will be replaced with their values.");
310 
311         if (! isVisualStudio())
312             props.add (new ChoicePropertyComponent (gnuExtensionsValue, "GNU Compiler Extensions"),
313                        "Enabling this will use the GNU C++ language standard variant for compilation.");
314 
315         createIconProperties (props);
316     }
317 
318     createExporterProperties (props);
319 
320     props.add (new TextPropertyComponent (userNotesValue, "Notes", 32768, true),
321                "Extra comments: This field is not used for code or project generation, it's just a space where you can express your thoughts.");
322 }
323 
createIconProperties(PropertyListBuilder & props)324 void ProjectExporter::createIconProperties (PropertyListBuilder& props)
325 {
326     OwnedArray<Project::Item> images;
327     project.findAllImageItems (images);
328 
329     StringArray choices;
330     Array<var> ids;
331 
332     choices.add ("<None>");
333     ids.add (var());
334 
335     for (int i = 0; i < images.size(); ++i)
336     {
337         choices.add (images.getUnchecked(i)->getName());
338         ids.add (images.getUnchecked(i)->getID());
339     }
340 
341     props.add (new ChoicePropertyComponent (smallIconValue, "Icon (Small)", choices, ids),
342                "Sets an icon to use for the executable.");
343 
344     props.add (new ChoicePropertyComponent (bigIconValue, "Icon (Large)", choices, ids),
345                "Sets an icon to use for the executable.");
346 }
347 
348 //==============================================================================
addSettingsForProjectType(const build_tools::ProjectType & type)349 void ProjectExporter::addSettingsForProjectType (const build_tools::ProjectType& type)
350 {
351     addVSTPathsIfPluginOrHost();
352 
353     if (type.isAudioPlugin())
354         addCommonAudioPluginSettings();
355 
356     addPlatformSpecificSettingsForProjectType (type);
357 }
358 
addVSTPathsIfPluginOrHost()359 void ProjectExporter::addVSTPathsIfPluginOrHost()
360 {
361     if (((shouldBuildTargetType (build_tools::ProjectType::Target::VSTPlugIn) && project.shouldBuildVST()) || project.isVSTPluginHost())
362          || ((shouldBuildTargetType (build_tools::ProjectType::Target::VST3PlugIn) && project.shouldBuildVST3()) || project.isVST3PluginHost()))
363     {
364         addLegacyVSTFolderToPathIfSpecified();
365 
366         if (! project.isConfigFlagEnabled ("JUCE_CUSTOM_VST3_SDK"))
367             addToExtraSearchPaths (getInternalVST3SDKPath(), 0);
368     }
369 }
370 
addCommonAudioPluginSettings()371 void ProjectExporter::addCommonAudioPluginSettings()
372 {
373     if (shouldBuildTargetType (build_tools::ProjectType::Target::AAXPlugIn))
374         addAAXFoldersToPath();
375 
376     // Note: RTAS paths are platform-dependent, impl -> addPlatformSpecificSettingsForProjectType
377  }
378 
addLegacyVSTFolderToPathIfSpecified()379 void ProjectExporter::addLegacyVSTFolderToPathIfSpecified()
380 {
381     auto vstFolder = getVSTLegacyPathString();
382 
383     if (vstFolder.isNotEmpty())
384         addToExtraSearchPaths (build_tools::RelativePath (vstFolder, build_tools::RelativePath::projectFolder), 0);
385 }
386 
getInternalVST3SDKPath()387 build_tools::RelativePath ProjectExporter::getInternalVST3SDKPath()
388 {
389     return getModuleFolderRelativeToProject ("juce_audio_processors")
390                            .getChildFile ("format_types")
391                            .getChildFile ("VST3_SDK");
392 }
393 
addAAXFoldersToPath()394 void ProjectExporter::addAAXFoldersToPath()
395 {
396     auto aaxFolder = getAAXPathString();
397 
398     if (aaxFolder.isNotEmpty())
399     {
400         build_tools::RelativePath aaxFolderPath (aaxFolder, build_tools::RelativePath::projectFolder);
401 
402         addToExtraSearchPaths (aaxFolderPath);
403         addToExtraSearchPaths (aaxFolderPath.getChildFile ("Interfaces"));
404         addToExtraSearchPaths (aaxFolderPath.getChildFile ("Interfaces").getChildFile ("ACF"));
405     }
406 }
407 
408 //==============================================================================
getAllPreprocessorDefs(const BuildConfiguration & config,const build_tools::ProjectType::Target::Type targetType) const409 StringPairArray ProjectExporter::getAllPreprocessorDefs (const BuildConfiguration& config, const build_tools::ProjectType::Target::Type targetType) const
410 {
411     auto defs = mergePreprocessorDefs (config.getAllPreprocessorDefs(),
412                                        parsePreprocessorDefs (getExporterPreprocessorDefsString()));
413 
414     addDefaultPreprocessorDefs (defs);
415     addTargetSpecificPreprocessorDefs (defs, targetType);
416 
417     if (! project.shouldUseAppConfig())
418         defs = mergePreprocessorDefs (project.getAppConfigDefs(), defs);
419 
420     return defs;
421 }
422 
getAllPreprocessorDefs() const423 StringPairArray ProjectExporter::getAllPreprocessorDefs() const
424 {
425     auto defs = mergePreprocessorDefs (project.getPreprocessorDefs(),
426                                        parsePreprocessorDefs (getExporterPreprocessorDefsString()));
427     addDefaultPreprocessorDefs (defs);
428 
429     return defs;
430 }
431 
addTargetSpecificPreprocessorDefs(StringPairArray & defs,const build_tools::ProjectType::Target::Type targetType) const432 void ProjectExporter::addTargetSpecificPreprocessorDefs (StringPairArray& defs, const build_tools::ProjectType::Target::Type targetType) const
433 {
434     std::pair<String, build_tools::ProjectType::Target::Type> targetFlags[] = {
435         {"JucePlugin_Build_VST",        build_tools::ProjectType::Target::VSTPlugIn},
436         {"JucePlugin_Build_VST3",       build_tools::ProjectType::Target::VST3PlugIn},
437         {"JucePlugin_Build_AU",         build_tools::ProjectType::Target::AudioUnitPlugIn},
438         {"JucePlugin_Build_AUv3",       build_tools::ProjectType::Target::AudioUnitv3PlugIn},
439         {"JucePlugin_Build_RTAS",       build_tools::ProjectType::Target::RTASPlugIn},
440         {"JucePlugin_Build_AAX",        build_tools::ProjectType::Target::AAXPlugIn},
441         {"JucePlugin_Build_Standalone", build_tools::ProjectType::Target::StandalonePlugIn},
442         {"JucePlugin_Build_Unity",      build_tools::ProjectType::Target::UnityPlugIn}
443     };
444 
445     if (targetType == build_tools::ProjectType::Target::SharedCodeTarget)
446     {
447         for (auto& flag : targetFlags)
448             defs.set (flag.first, (shouldBuildTargetType (flag.second) ? "1" : "0"));
449 
450         defs.set ("JUCE_SHARED_CODE", "1");
451     }
452     else if (targetType != build_tools::ProjectType::Target::unspecified)
453     {
454         for (auto& flag : targetFlags)
455             defs.set (flag.first, (targetType == flag.second ? "1" : "0"));
456     }
457 }
458 
addDefaultPreprocessorDefs(StringPairArray & defs) const459 void ProjectExporter::addDefaultPreprocessorDefs (StringPairArray& defs) const
460 {
461     defs.set (getExporterIdentifierMacro(), "1");
462     defs.set ("JUCE_APP_VERSION", project.getVersionString());
463     defs.set ("JUCE_APP_VERSION_HEX", project.getVersionAsHex());
464 }
465 
replacePreprocessorTokens(const ProjectExporter::BuildConfiguration & config,const String & sourceString) const466 String ProjectExporter::replacePreprocessorTokens (const ProjectExporter::BuildConfiguration& config,
467                                                    const String& sourceString) const
468 {
469     return build_tools::replacePreprocessorDefs (getAllPreprocessorDefs (config, build_tools::ProjectType::Target::unspecified),
470                                                  sourceString);
471 }
472 
copyMainGroupFromProject()473 void ProjectExporter::copyMainGroupFromProject()
474 {
475     jassert (itemGroups.size() == 0);
476     itemGroups.add (project.getMainGroup().createCopy());
477 }
478 
getModulesGroup()479 Project::Item& ProjectExporter::getModulesGroup()
480 {
481     if (modulesGroup == nullptr)
482     {
483         jassert (itemGroups.size() > 0); // must call copyMainGroupFromProject before this.
484         itemGroups.add (Project::Item::createGroup (project, "JUCE Modules", "__modulesgroup__", true));
485         modulesGroup = &(itemGroups.getReference (itemGroups.size() - 1));
486     }
487 
488     return *modulesGroup;
489 }
490 
491 //==============================================================================
isWebBrowserComponentEnabled(Project & project)492 static bool isWebBrowserComponentEnabled (Project& project)
493 {
494     static String guiExtrasModule ("juce_gui_extra");
495 
496     return (project.getEnabledModules().isModuleEnabled (guiExtrasModule)
497             && project.isConfigFlagEnabled ("JUCE_WEB_BROWSER", true));
498 }
499 
isCurlEnabled(Project & project)500 static bool isCurlEnabled (Project& project)
501 {
502     static String juceCoreModule ("juce_core");
503 
504     return (project.getEnabledModules().isModuleEnabled (juceCoreModule)
505             && project.isConfigFlagEnabled ("JUCE_USE_CURL", true));
506 }
507 
isLoadCurlSymbolsLazilyEnabled(Project & project)508 static bool isLoadCurlSymbolsLazilyEnabled (Project& project)
509 {
510     static String juceCoreModule ("juce_core");
511 
512     return (project.getEnabledModules().isModuleEnabled (juceCoreModule)
513             && project.isConfigFlagEnabled ("JUCE_LOAD_CURL_SYMBOLS_LAZILY", false));
514 }
515 
getLinuxPackages(PackageDependencyType type) const516 StringArray ProjectExporter::getLinuxPackages (PackageDependencyType type) const
517 {
518     auto packages = linuxPackages;
519 
520     // don't add libcurl if curl symbols are loaded at runtime
521     if (isCurlEnabled (project) && ! isLoadCurlSymbolsLazilyEnabled (project))
522         packages.add ("libcurl");
523 
524     if (isWebBrowserComponentEnabled (project) && type == PackageDependencyType::compile)
525     {
526         packages.add ("webkit2gtk-4.0");
527         packages.add ("gtk+-x11-3.0");
528     }
529 
530     packages.removeEmptyStrings();
531     packages.removeDuplicates (false);
532 
533     return packages;
534 }
535 
addProjectPathToBuildPathList(StringArray & pathList,const build_tools::RelativePath & pathFromProjectFolder,int index) const536 void ProjectExporter::addProjectPathToBuildPathList (StringArray& pathList,
537                                                      const build_tools::RelativePath& pathFromProjectFolder,
538                                                      int index) const
539 {
540     auto localPath = build_tools::RelativePath (rebaseFromProjectFolderToBuildTarget (pathFromProjectFolder));
541 
542     auto path = isVisualStudio() ? localPath.toWindowsStyle() : localPath.toUnixStyle();
543 
544     if (! pathList.contains (path))
545         pathList.insert (index, path);
546 }
547 
addToModuleLibPaths(const build_tools::RelativePath & pathFromProjectFolder)548 void ProjectExporter::addToModuleLibPaths (const build_tools::RelativePath& pathFromProjectFolder)
549 {
550     addProjectPathToBuildPathList (moduleLibSearchPaths, pathFromProjectFolder);
551 }
552 
addToExtraSearchPaths(const build_tools::RelativePath & pathFromProjectFolder,int index)553 void ProjectExporter::addToExtraSearchPaths (const build_tools::RelativePath& pathFromProjectFolder, int index)
554 {
555     addProjectPathToBuildPathList (extraSearchPaths, pathFromProjectFolder, index);
556 }
557 
getStoredPathForModule(const String & id,const ProjectExporter & exp)558 static var getStoredPathForModule (const String& id, const ProjectExporter& exp)
559 {
560     return getAppSettings().getStoredPath (isJUCEModule (id) ? Ids::defaultJuceModulePath : Ids::defaultUserModulePath,
561                                            exp.getTargetOSForExporter()).get();
562 }
563 
getPathForModuleValue(const String & moduleID)564 ValueWithDefault ProjectExporter::getPathForModuleValue (const String& moduleID)
565 {
566     auto* um = getUndoManager();
567 
568     auto paths = settings.getOrCreateChildWithName (Ids::MODULEPATHS, um);
569     auto m = paths.getChildWithProperty (Ids::ID, moduleID);
570 
571     if (! m.isValid())
572     {
573         m = ValueTree (Ids::MODULEPATH);
574         m.setProperty (Ids::ID, moduleID, um);
575         paths.appendChild (m, um);
576     }
577 
578     return { m, Ids::path, um, getStoredPathForModule (moduleID, *this) };
579 }
580 
getPathForModuleString(const String & moduleID) const581 String ProjectExporter::getPathForModuleString (const String& moduleID) const
582 {
583     auto exporterPath = settings.getChildWithName (Ids::MODULEPATHS)
584                                 .getChildWithProperty (Ids::ID, moduleID) [Ids::path].toString();
585 
586     if (exporterPath.isEmpty() || project.getEnabledModules().shouldUseGlobalPath (moduleID))
587         return getStoredPathForModule (moduleID, *this);
588 
589     return exporterPath;
590 }
591 
removePathForModule(const String & moduleID)592 void ProjectExporter::removePathForModule (const String& moduleID)
593 {
594     auto paths = settings.getChildWithName (Ids::MODULEPATHS);
595     auto m = paths.getChildWithProperty (Ids::ID, moduleID);
596 
597     paths.removeChild (m, project.getUndoManagerFor (settings));
598 }
599 
getTargetOSForExporter() const600 TargetOS::OS ProjectExporter::getTargetOSForExporter() const
601 {
602     auto targetOS = TargetOS::unknown;
603 
604     if      (isWindows())                 targetOS = TargetOS::windows;
605     else if (isOSX() || isiOS())          targetOS = TargetOS::osx;
606     else if (isLinux())                   targetOS = TargetOS::linux;
607     else if (isAndroid() || isCLion())    targetOS = TargetOS::getThisOS();
608 
609     return targetOS;
610 }
611 
getModuleFolderRelativeToProject(const String & moduleID) const612 build_tools::RelativePath ProjectExporter::getModuleFolderRelativeToProject (const String& moduleID) const
613 {
614     if (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID))
615         return build_tools::RelativePath (project.getRelativePathForFile (project.getLocalModuleFolder (moduleID)),
616                              build_tools::RelativePath::projectFolder);
617 
618     auto path = getPathForModuleString (moduleID);
619 
620     if (path.isEmpty())
621         return getLegacyModulePath (moduleID).getChildFile (moduleID);
622 
623     return build_tools::RelativePath (path, build_tools::RelativePath::projectFolder).getChildFile (moduleID);
624 }
625 
getLegacyModulePath() const626 String ProjectExporter::getLegacyModulePath() const
627 {
628     return getSettingString ("juceFolder");
629 }
630 
getLegacyModulePath(const String & moduleID) const631 build_tools::RelativePath ProjectExporter::getLegacyModulePath (const String& moduleID) const
632 {
633     if (project.getEnabledModules().shouldCopyModuleFilesLocally (moduleID))
634         return build_tools::RelativePath (project.getRelativePathForFile (project.getGeneratedCodeFolder()
635                                                                            .getChildFile ("modules")
636                                                                            .getChildFile (moduleID)), build_tools::RelativePath::projectFolder);
637 
638     auto oldJucePath = getLegacyModulePath();
639 
640     if (oldJucePath.isEmpty())
641         return build_tools::RelativePath();
642 
643     build_tools::RelativePath p (oldJucePath, build_tools::RelativePath::projectFolder);
644     if (p.getFileName() != "modules")
645         p = p.getChildFile ("modules");
646 
647     return p.getChildFile (moduleID);
648 }
649 
updateOldModulePaths()650 void ProjectExporter::updateOldModulePaths()
651 {
652     auto oldPath = getLegacyModulePath();
653 
654     if (oldPath.isNotEmpty())
655     {
656         for (int i = project.getEnabledModules().getNumModules(); --i >= 0;)
657         {
658             auto modID = project.getEnabledModules().getModuleID (i);
659             getPathForModuleValue (modID) = getLegacyModulePath (modID).getParentDirectory().toUnixStyle();
660         }
661 
662         settings.removeProperty ("juceFolder", nullptr);
663     }
664 }
665 
areCompatibleExporters(const ProjectExporter & p1,const ProjectExporter & p2)666 static bool areCompatibleExporters (const ProjectExporter& p1, const ProjectExporter& p2)
667 {
668     return (p1.isVisualStudio() && p2.isVisualStudio())
669         || (p1.isXcode() && p2.isXcode())
670         || (p1.isMakefile() && p2.isMakefile())
671         || (p1.isAndroidStudio() && p2.isAndroidStudio())
672         || (p1.isCodeBlocks() && p2.isCodeBlocks() && p1.isWindows() != p2.isLinux());
673 }
674 
createDefaultModulePaths()675 void ProjectExporter::createDefaultModulePaths()
676 {
677     for (Project::ExporterIterator exporter (project); exporter.next();)
678     {
679         if (areCompatibleExporters (*this, *exporter))
680         {
681             for (int i = project.getEnabledModules().getNumModules(); --i >= 0;)
682             {
683                 auto modID = project.getEnabledModules().getModuleID (i);
684                 getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID);
685             }
686 
687             return;
688         }
689     }
690 
691     for (Project::ExporterIterator exporter (project); exporter.next();)
692     {
693         if (exporter->canLaunchProject())
694         {
695             for (int i = project.getEnabledModules().getNumModules(); --i >= 0;)
696             {
697                 auto modID = project.getEnabledModules().getModuleID (i);
698                 getPathForModuleValue (modID) = exporter->getPathForModuleValue (modID);
699             }
700 
701             return;
702         }
703     }
704 
705     for (int i = project.getEnabledModules().getNumModules(); --i >= 0;)
706     {
707         auto modID = project.getEnabledModules().getModuleID (i);
708         getPathForModuleValue (modID) = "../../juce";
709     }
710 }
711 
712 //==============================================================================
getConfigurations() const713 ValueTree ProjectExporter::getConfigurations() const
714 {
715     return settings.getChildWithName (Ids::CONFIGURATIONS);
716 }
717 
getNumConfigurations() const718 int ProjectExporter::getNumConfigurations() const
719 {
720     return getConfigurations().getNumChildren();
721 }
722 
getConfiguration(int index) const723 ProjectExporter::BuildConfiguration::Ptr ProjectExporter::getConfiguration (int index) const
724 {
725     return createBuildConfig (getConfigurations().getChild (index));
726 }
727 
hasConfigurationNamed(const String & nameToFind) const728 bool ProjectExporter::hasConfigurationNamed (const String& nameToFind) const
729 {
730     auto configs = getConfigurations();
731     for (int i = configs.getNumChildren(); --i >= 0;)
732         if (configs.getChild(i) [Ids::name].toString() == nameToFind)
733             return true;
734 
735     return false;
736 }
737 
getUniqueConfigName(String nm) const738 String ProjectExporter::getUniqueConfigName (String nm) const
739 {
740     auto nameRoot = nm;
741     while (CharacterFunctions::isDigit (nameRoot.getLastCharacter()))
742         nameRoot = nameRoot.dropLastCharacters (1);
743 
744     nameRoot = nameRoot.trim();
745 
746     int suffix = 2;
747     while (hasConfigurationNamed (name))
748         nm = nameRoot + " " + String (suffix++);
749 
750     return nm;
751 }
752 
addNewConfigurationFromExisting(const BuildConfiguration & configToCopy)753 void ProjectExporter::addNewConfigurationFromExisting (const BuildConfiguration& configToCopy)
754 {
755     auto configs = getConfigurations();
756 
757     if (! configs.isValid())
758     {
759         settings.addChild (ValueTree (Ids::CONFIGURATIONS), 0, project.getUndoManagerFor (settings));
760         configs = getConfigurations();
761     }
762 
763     ValueTree newConfig (Ids::CONFIGURATION);
764     newConfig = configToCopy.config.createCopy();
765 
766     newConfig.setProperty (Ids::name, configToCopy.getName(), nullptr);
767 
768     configs.appendChild (newConfig, project.getUndoManagerFor (configs));
769 }
770 
addNewConfiguration(bool isDebugConfig)771 void ProjectExporter::addNewConfiguration (bool isDebugConfig)
772 {
773     auto configs = getConfigurations();
774 
775     if (! configs.isValid())
776     {
777         settings.addChild (ValueTree (Ids::CONFIGURATIONS), 0, project.getUndoManagerFor (settings));
778         configs = getConfigurations();
779     }
780 
781     ValueTree newConfig (Ids::CONFIGURATION);
782     newConfig.setProperty (Ids::isDebug, isDebugConfig, project.getUndoManagerFor (settings));
783 
784     configs.appendChild (newConfig, project.getUndoManagerFor (settings));
785 }
786 
removeFromExporter()787 void ProjectExporter::BuildConfiguration::removeFromExporter()
788 {
789     ValueTree configs (config.getParent());
790     configs.removeChild (config, project.getUndoManagerFor (configs));
791 }
792 
createDefaultConfigs()793 void ProjectExporter::createDefaultConfigs()
794 {
795     settings.getOrCreateChildWithName (Ids::CONFIGURATIONS, nullptr);
796 
797     for (int i = 0; i < 2; ++i)
798     {
799         auto isDebug = i == 0;
800 
801         addNewConfiguration (isDebug);
802         BuildConfiguration::Ptr config (getConfiguration (i));
803         config->getValue (Ids::name) = (isDebug ? "Debug" : "Release");
804     }
805 }
806 
getBigIcon() const807 std::unique_ptr<Drawable> ProjectExporter::getBigIcon() const
808 {
809     return project.getMainGroup().findItemWithID (settings [Ids::bigIcon]).loadAsImageFile();
810 }
811 
getSmallIcon() const812 std::unique_ptr<Drawable> ProjectExporter::getSmallIcon() const
813 {
814     return project.getMainGroup().findItemWithID (settings [Ids::smallIcon]).loadAsImageFile();
815 }
816 
817 //==============================================================================
ConfigIterator(ProjectExporter & e)818 ProjectExporter::ConfigIterator::ConfigIterator (ProjectExporter& e)
819     : index (-1), exporter (e)
820 {
821 }
822 
next()823 bool ProjectExporter::ConfigIterator::next()
824 {
825     if (++index >= exporter.getNumConfigurations())
826         return false;
827 
828     config = exporter.getConfiguration (index);
829     return true;
830 }
831 
ConstConfigIterator(const ProjectExporter & exporter_)832 ProjectExporter::ConstConfigIterator::ConstConfigIterator (const ProjectExporter& exporter_)
833     : index (-1), exporter (exporter_)
834 {
835 }
836 
next()837 bool ProjectExporter::ConstConfigIterator::next()
838 {
839     if (++index >= exporter.getNumConfigurations())
840         return false;
841 
842     config = exporter.getConfiguration (index);
843     return true;
844 }
845 
846 //==============================================================================
BuildConfiguration(Project & p,const ValueTree & configNode,const ProjectExporter & e)847 ProjectExporter::BuildConfiguration::BuildConfiguration (Project& p, const ValueTree& configNode, const ProjectExporter& e)
848    : config (configNode), project (p), exporter (e),
849      isDebugValue                  (config, Ids::isDebug,                  getUndoManager(), getValue (Ids::isDebug)),
850      configNameValue               (config, Ids::name,                     getUndoManager(), "Build Configuration"),
851      targetNameValue               (config, Ids::targetName,               getUndoManager(), project.getProjectFilenameRootString()),
852      targetBinaryPathValue         (config, Ids::binaryPath,               getUndoManager()),
853      recommendedWarningsValue      (config, Ids::recommendedWarnings,      getUndoManager()),
854      optimisationLevelValue        (config, Ids::optimisation,             getUndoManager()),
855      linkTimeOptimisationValue     (config, Ids::linkTimeOptimisation,     getUndoManager(), ! isDebug()),
856      ppDefinesValue                (config, Ids::defines,                  getUndoManager()),
857      headerSearchPathValue         (config, Ids::headerPath,               getUndoManager()),
858      librarySearchPathValue        (config, Ids::libraryPath,              getUndoManager()),
859      userNotesValue                (config, Ids::userNotes,                getUndoManager()),
860      usePrecompiledHeaderFileValue (config, Ids::usePrecompiledHeaderFile, getUndoManager(), false),
861      precompiledHeaderFileValue    (config, Ids::precompiledHeaderFile,    getUndoManager())
862 {
863     recommendedCompilerWarningFlags["LLVM"] = { "-Wall", "-Wshadow-all", "-Wshorten-64-to-32", "-Wstrict-aliasing", "-Wuninitialized", "-Wunused-parameter",
864         "-Wconversion", "-Wsign-compare", "-Wint-conversion", "-Wconditional-uninitialized", "-Woverloaded-virtual",
865         "-Wreorder", "-Wconstant-conversion", "-Wsign-conversion", "-Wunused-private-field", "-Wbool-conversion",
866         "-Wextra-semi", "-Wunreachable-code", "-Wzero-as-null-pointer-constant", "-Wcast-align",
867         "-Winconsistent-missing-destructor-override", "-Wshift-sign-overflow", "-Wnullable-to-nonnull-conversion",
868         "-Wno-missing-field-initializers", "-Wno-ignored-qualifiers",
869         "-Wswitch-enum"
870     };
871     recommendedCompilerWarningFlags["GCC"] = { "-Wall", "-Wextra", "-Wstrict-aliasing", "-Wuninitialized", "-Wunused-parameter", "-Wsign-compare",
872         "-Woverloaded-virtual", "-Wreorder", "-Wsign-conversion", "-Wunreachable-code",
873         "-Wzero-as-null-pointer-constant", "-Wcast-align", "-Wno-implicit-fallthrough",
874         "-Wno-maybe-uninitialized", "-Wno-missing-field-initializers", "-Wno-ignored-qualifiers",
875         "-Wswitch-enum", "-Wredundant-decls"
876     };
877     recommendedCompilerWarningFlags["GCC-7"] = recommendedCompilerWarningFlags["GCC"];
878     recommendedCompilerWarningFlags["GCC-7"].add ("-Wno-strict-overflow");
879 }
880 
~BuildConfiguration()881 ProjectExporter::BuildConfiguration::~BuildConfiguration()
882 {
883 }
884 
getGCCOptimisationFlag() const885 String ProjectExporter::BuildConfiguration::getGCCOptimisationFlag() const
886 {
887     switch (getOptimisationLevelInt())
888     {
889         case gccO0:     return "0";
890         case gccO1:     return "1";
891         case gccO2:     return "2";
892         case gccO3:     return "3";
893         case gccOs:     return "s";
894         case gccOfast:  return "fast";
895         default:        break;
896     }
897 
898     return "0";
899 }
900 
addGCCOptimisationProperty(PropertyListBuilder & props)901 void ProjectExporter::BuildConfiguration::addGCCOptimisationProperty (PropertyListBuilder& props)
902 {
903     props.add (new ChoicePropertyComponent (optimisationLevelValue, "Optimisation",
904                                             { "-O0 (no optimisation)", "-Os (minimise code size)", "-O1 (fast)", "-O2 (faster)",
905                                               "-O3 (fastest with safe optimisations)", "-Ofast (uses aggressive optimisations)" },
906                                             { gccO0, gccOs, gccO1, gccO2, gccO3, gccOfast }),
907                "The optimisation level for this configuration");
908 }
909 
addRecommendedLinuxCompilerWarningsProperty(PropertyListBuilder & props)910 void ProjectExporter::BuildConfiguration::addRecommendedLinuxCompilerWarningsProperty (PropertyListBuilder& props)
911 {
912     props.add (new ChoicePropertyComponent (recommendedWarningsValue, "Add Recommended Compiler Warning Flags",
913                                             { "GCC", "GCC 7 and below", "LLVM", "Disabled" },
914                                             { "GCC", "GCC-7", "LLVM", "" }),
915                "Enable this to add a set of recommended compiler warning flags.");
916     recommendedWarningsValue.setDefault ("");
917 }
918 
addRecommendedLLVMCompilerWarningsProperty(PropertyListBuilder & props)919 void ProjectExporter::BuildConfiguration::addRecommendedLLVMCompilerWarningsProperty (PropertyListBuilder& props)
920 {
921     props.add (new ChoicePropertyComponent (recommendedWarningsValue, "Add Recommended Compiler Warning Flags",
922                                             { "Enabled", "Disabled" },
923                                             { "LLVM", "" }),
924                "Enable this to add a set of recommended compiler warning flags.");
925     recommendedWarningsValue.setDefault ("");
926 }
927 
getRecommendedCompilerWarningFlags() const928 StringArray ProjectExporter::BuildConfiguration::getRecommendedCompilerWarningFlags() const
929 {
930     auto label = recommendedWarningsValue.get().toString();
931     auto it = recommendedCompilerWarningFlags.find (label);
932 
933     if (it != recommendedCompilerWarningFlags.end())
934         return it->second;
935 
936     return {};
937 }
938 
createPropertyEditors(PropertyListBuilder & props)939 void ProjectExporter::BuildConfiguration::createPropertyEditors (PropertyListBuilder& props)
940 {
941     if (exporter.supportsUserDefinedConfigurations())
942         props.add (new TextPropertyComponent (configNameValue, "Name", 96, false),
943                    "The name of this configuration.");
944 
945     props.add (new ChoicePropertyComponent (isDebugValue, "Debug Mode"),
946                "If enabled, this means that the configuration should be built with debug symbols.");
947 
948     props.add (new TextPropertyComponent (targetNameValue, "Binary Name", 256, false),
949                "The filename to use for the destination binary executable file. If you don't add a suffix to this name, "
950                "a suitable platform-specific suffix will be added automatically.");
951 
952     props.add (new TextPropertyComponent (targetBinaryPathValue, "Binary Location", 1024, false),
953                "The folder in which the finished binary should be placed. Leave this blank to cause the binary to be placed "
954                "in its default location in the build folder.");
955 
956     props.addSearchPathProperty (headerSearchPathValue, "Header Search Paths", "Extra header search paths.");
957     props.addSearchPathProperty (librarySearchPathValue, "Extra Library Search Paths", "Extra library search paths.");
958 
959     props.add (new TextPropertyComponent (ppDefinesValue, "Preprocessor Definitions", 32768, true),
960                "Extra preprocessor definitions. Use the form \"NAME1=value NAME2=value\", using whitespace, commas, or "
961                "new-lines to separate the items - to include a space or comma in a definition, precede it with a backslash.");
962 
963     props.add (new ChoicePropertyComponent (linkTimeOptimisationValue, "Link-Time Optimisation"),
964                "Enable this to perform link-time code optimisation. This is recommended for release builds.");
965 
966     if (exporter.supportsPrecompiledHeaders())
967     {
968         props.add (new ChoicePropertyComponent (usePrecompiledHeaderFileValue, "Use Precompiled Header"),
969                    "Enable this to turn on precompiled header support for this configuration. Use the setting "
970                    "below to specify the header file to use.");
971 
972         auto quotedHeaderFileName = (getPrecompiledHeaderFilename() + ".h").quoted();
973 
974         props.add (new FilePathPropertyComponentWithEnablement (precompiledHeaderFileValue, usePrecompiledHeaderFileValue,
975                                                                 "Precompiled Header File", false, true, "*", project.getProjectFolder()),
976                    "Specify an input header file that will be used to generate a file named " + quotedHeaderFileName + " which is used to generate the "
977                    "PCH file artefact for this exporter configuration. This file can be an absolute path, or relative to the jucer project folder. "
978                    "The " + quotedHeaderFileName + " file will be force included to all source files unless the \"Skip PCH\" setting has been enabled. "
979                    "The generated header will be written on project save and placed in the target folder for this exporter.");
980     }
981 
982     createConfigProperties (props);
983 
984     props.add (new TextPropertyComponent (userNotesValue, "Notes", 32768, true),
985                "Extra comments: This field is not used for code or project generation, it's just a space where you can express your thoughts.");
986 }
987 
getAllPreprocessorDefs() const988 StringPairArray ProjectExporter::BuildConfiguration::getAllPreprocessorDefs() const
989 {
990     return mergePreprocessorDefs (project.getPreprocessorDefs(),
991                                   parsePreprocessorDefs (getBuildConfigPreprocessorDefsString()));
992 }
993 
getUniquePreprocessorDefs() const994 StringPairArray ProjectExporter::BuildConfiguration::getUniquePreprocessorDefs() const
995 {
996     auto perConfigurationDefs = parsePreprocessorDefs (getBuildConfigPreprocessorDefsString());
997     auto globalDefs = project.getPreprocessorDefs();
998 
999     for (int i = 0; i < globalDefs.size(); ++i)
1000     {
1001         auto globalKey = globalDefs.getAllKeys()[i];
1002 
1003         int idx = perConfigurationDefs.getAllKeys().indexOf (globalKey);
1004         if (idx >= 0)
1005         {
1006             auto globalValue = globalDefs.getAllValues()[i];
1007 
1008             if (globalValue == perConfigurationDefs.getAllValues()[idx])
1009                 perConfigurationDefs.remove (idx);
1010         }
1011     }
1012 
1013     return perConfigurationDefs;
1014 }
1015 
getHeaderSearchPaths() const1016 StringArray ProjectExporter::BuildConfiguration::getHeaderSearchPaths() const
1017 {
1018     return getSearchPathsFromString (getHeaderSearchPathString() + ';' + project.getHeaderSearchPathsString());
1019 }
1020 
getLibrarySearchPaths() const1021 StringArray ProjectExporter::BuildConfiguration::getLibrarySearchPaths() const
1022 {
1023     auto separator = exporter.isVisualStudio() ? "\\" : "/";
1024     auto s = getSearchPathsFromString (getLibrarySearchPathString());
1025 
1026     for (auto path : exporter.moduleLibSearchPaths)
1027         s.add (path + separator + getModuleLibraryArchName());
1028 
1029     return s;
1030 }
1031 
getPrecompiledHeaderFileContent() const1032 String ProjectExporter::BuildConfiguration::getPrecompiledHeaderFileContent() const
1033 {
1034     if (shouldUsePrecompiledHeaderFile())
1035     {
1036         auto f = project.getProjectFolder().getChildFile (precompiledHeaderFileValue.get().toString());
1037 
1038         if (f.existsAsFile() && f.hasFileExtension (headerFileExtensions))
1039         {
1040             MemoryOutputStream content;
1041             content.setNewLineString (exporter.getNewLineString());
1042 
1043             writeAutoGenWarningComment (content);
1044 
1045             content << "*/" << newLine << newLine
1046                     << "#ifndef " << getSkipPrecompiledHeaderDefine() << newLine << newLine
1047                     << f.loadFileAsString() << newLine
1048                     << "#endif" << newLine;
1049 
1050             return content.toString();
1051         }
1052     }
1053 
1054     return {};
1055 }
1056 
getExternalLibraryFlags(const BuildConfiguration & config) const1057 String ProjectExporter::getExternalLibraryFlags (const BuildConfiguration& config) const
1058 {
1059     auto libraries = StringArray::fromTokens (getExternalLibrariesString(), ";\n", "\"'");
1060     libraries.removeEmptyStrings (true);
1061 
1062     if (libraries.size() != 0)
1063         return replacePreprocessorTokens (config, "-l" + libraries.joinIntoString (" -l")).trim();
1064 
1065     return {};
1066 }
1067