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