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 #pragma once 27 28 #include "../Project/jucer_Project.h" 29 #include "../Utility/UI/PropertyComponents/jucer_PropertyComponentsWithEnablement.h" 30 #include "../Utility/Helpers/jucer_ValueWithDefaultWrapper.h" 31 #include "../Project/Modules/jucer_Modules.h" 32 33 class ProjectSaver; 34 35 //============================================================================== 36 class ProjectExporter : private Value::Listener 37 { 38 public: 39 ProjectExporter (Project&, const ValueTree& settings); 40 virtual ~ProjectExporter() override = default; 41 42 //============================================================================== 43 struct ExporterTypeInfo 44 { 45 Identifier identifier; 46 String displayName; 47 String targetFolder; 48 49 Image icon; 50 }; 51 52 static std::vector<ExporterTypeInfo> getExporterTypeInfos(); 53 static ExporterTypeInfo getTypeInfoForExporter (const Identifier& exporterIdentifier); 54 static ExporterTypeInfo getCurrentPlatformExporterTypeInfo(); 55 56 static std::unique_ptr<ProjectExporter> createNewExporter (Project&, const Identifier& exporterIdentifier); 57 static std::unique_ptr<ProjectExporter> createExporterFromSettings (Project&, const ValueTree& settings); 58 59 static bool canProjectBeLaunched (Project*); 60 61 //============================================================================== 62 // capabilities of exporter 63 virtual bool usesMMFiles() const = 0; 64 virtual void createExporterProperties (PropertyListBuilder&) = 0; 65 virtual bool canLaunchProject() = 0; 66 virtual bool launchProject() = 0; 67 virtual void create (const OwnedArray<LibraryModule>&) const = 0; // may throw a SaveError 68 virtual bool shouldFileBeCompiledByDefault (const File& path) const; 69 virtual bool canCopeWithDuplicateFiles() = 0; 70 virtual bool supportsUserDefinedConfigurations() const = 0; // false if exporter only supports two configs Debug and Release updateDeprecatedSettings()71 virtual void updateDeprecatedSettings() {} updateDeprecatedSettingsInteractively()72 virtual void updateDeprecatedSettingsInteractively() {} initialiseDependencyPathValues()73 virtual void initialiseDependencyPathValues() {} 74 75 // IDE targeted by exporter 76 virtual bool isXcode() const = 0; 77 virtual bool isVisualStudio() const = 0; 78 virtual bool isCodeBlocks() const = 0; 79 virtual bool isMakefile() const = 0; 80 virtual bool isAndroidStudio() const = 0; 81 virtual bool isCLion() const = 0; 82 83 // operating system targeted by exporter 84 virtual bool isAndroid() const = 0; 85 virtual bool isWindows() const = 0; 86 virtual bool isLinux() const = 0; 87 virtual bool isOSX() const = 0; 88 virtual bool isiOS() const = 0; 89 90 virtual String getNewLineString() const = 0; getDescription()91 virtual String getDescription() { return {}; } 92 supportsPrecompiledHeaders()93 virtual bool supportsPrecompiledHeaders() const { return false; } 94 95 //============================================================================== 96 // cross-platform audio plug-ins supported by exporter 97 virtual bool supportsTargetType (build_tools::ProjectType::Target::Type type) const = 0; 98 shouldBuildTargetType(build_tools::ProjectType::Target::Type type)99 inline bool shouldBuildTargetType (build_tools::ProjectType::Target::Type type) const 100 { 101 return project.shouldBuildTargetType (type) && supportsTargetType (type); 102 } 103 callForAllSupportedTargets(std::function<void (build_tools::ProjectType::Target::Type)> callback)104 inline void callForAllSupportedTargets (std::function<void (build_tools::ProjectType::Target::Type)> callback) 105 { 106 for (int i = 0; i < build_tools::ProjectType::Target::unspecified; ++i) 107 if (shouldBuildTargetType (static_cast<build_tools::ProjectType::Target::Type> (i))) 108 callback (static_cast<build_tools::ProjectType::Target::Type> (i)); 109 } 110 111 //============================================================================== mayCompileOnCurrentOS()112 bool mayCompileOnCurrentOS() const 113 { 114 #if JUCE_MAC 115 return isOSX() || isAndroid() || isiOS(); 116 #elif JUCE_WINDOWS 117 return isWindows() || isAndroid(); 118 #elif JUCE_LINUX || JUCE_BSD 119 return isLinux() || isAndroid(); 120 #else 121 #error 122 #endif 123 } 124 125 //============================================================================== 126 String getUniqueName() const; 127 File getTargetFolder() const; 128 getProject()129 Project& getProject() noexcept { return project; } getProject()130 const Project& getProject() const noexcept { return project; } 131 getUndoManager()132 UndoManager* getUndoManager() const { return project.getUndoManagerFor (settings); } 133 getSetting(const Identifier & nm)134 Value getSetting (const Identifier& nm) { return settings.getPropertyAsValue (nm, project.getUndoManagerFor (settings)); } getSettingString(const Identifier & nm)135 String getSettingString (const Identifier& nm) const { return settings [nm]; } 136 getTargetLocationValue()137 Value getTargetLocationValue() { return targetLocationValue.getPropertyAsValue(); } getTargetLocationString()138 String getTargetLocationString() const { return targetLocationValue.get(); } 139 getExtraCompilerFlagsString()140 String getExtraCompilerFlagsString() const { return extraCompilerFlagsValue.get().toString().replaceCharacters ("\r\n", " "); } getExtraLinkerFlagsString()141 String getExtraLinkerFlagsString() const { return extraLinkerFlagsValue.get().toString().replaceCharacters ("\r\n", " "); } 142 getExternalLibrariesString()143 String getExternalLibrariesString() const { return getSearchPathsFromString (externalLibrariesValue.get().toString()).joinIntoString (";"); } 144 shouldUseGNUExtensions()145 bool shouldUseGNUExtensions() const { return gnuExtensionsValue.get(); } 146 getVSTLegacyPathString()147 String getVSTLegacyPathString() const { return vstLegacyPathValueWrapper.getCurrentValue(); } getAAXPathString()148 String getAAXPathString() const { return aaxPathValueWrapper.getCurrentValue(); } getRTASPathString()149 String getRTASPathString() const { return rtasPathValueWrapper.getCurrentValue(); } 150 151 // NB: this is the path to the parent "modules" folder that contains the named module, not the 152 // module folder itself. 153 ValueWithDefault getPathForModuleValue (const String& moduleID); 154 String getPathForModuleString (const String& moduleID) const; 155 void removePathForModule (const String& moduleID); 156 157 TargetOS::OS getTargetOSForExporter() const; 158 159 build_tools::RelativePath getLegacyModulePath (const String& moduleID) const; 160 String getLegacyModulePath() const; 161 162 // Returns a path to the actual module folder itself 163 build_tools::RelativePath getModuleFolderRelativeToProject (const String& moduleID) const; 164 void updateOldModulePaths(); 165 166 build_tools::RelativePath rebaseFromProjectFolderToBuildTarget (const build_tools::RelativePath& path) const; 167 void addToExtraSearchPaths (const build_tools::RelativePath& pathFromProjectFolder, int index = -1); 168 void addToModuleLibPaths (const build_tools::RelativePath& pathFromProjectFolder); 169 170 void addProjectPathToBuildPathList (StringArray&, const build_tools::RelativePath&, int index = -1) const; 171 172 std::unique_ptr<Drawable> getBigIcon() const; 173 std::unique_ptr<Drawable> getSmallIcon() const; getIcons()174 build_tools::Icons getIcons() const { return { getSmallIcon(), getBigIcon() }; } 175 getExporterIdentifierMacro()176 String getExporterIdentifierMacro() const 177 { 178 return "JUCER_" + settings.getType().toString() + "_" 179 + String::toHexString (getTargetLocationString().hashCode()).toUpperCase(); 180 } 181 182 // An exception that can be thrown by the create() method. 183 void createPropertyEditors (PropertyListBuilder&); 184 void addSettingsForProjectType (const build_tools::ProjectType&); 185 186 //============================================================================== 187 void copyMainGroupFromProject(); getAllGroups()188 Array<Project::Item>& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; } getAllGroups()189 const Array<Project::Item>& getAllGroups() const noexcept { jassert (itemGroups.size() > 0); return itemGroups; } 190 Project::Item& getModulesGroup(); 191 192 //============================================================================== 193 StringArray linuxLibs, linuxPackages, makefileExtraLinkerFlags; 194 195 enum class PackageDependencyType 196 { 197 compile, 198 link 199 }; 200 201 StringArray getLinuxPackages (PackageDependencyType type) const; 202 203 //============================================================================== 204 StringPairArray msvcExtraPreprocessorDefs; 205 String msvcDelayLoadedDLLs; 206 StringArray mingwLibs, windowsLibs; 207 208 //============================================================================== 209 StringArray androidLibs; 210 211 //============================================================================== 212 StringArray extraSearchPaths; 213 StringArray moduleLibSearchPaths; 214 215 //============================================================================== 216 class BuildConfiguration : public ReferenceCountedObject 217 { 218 public: 219 BuildConfiguration (Project& project, const ValueTree& configNode, const ProjectExporter&); 220 ~BuildConfiguration(); 221 222 using Ptr = ReferenceCountedObjectPtr<BuildConfiguration>; 223 224 //============================================================================== 225 virtual void createConfigProperties (PropertyListBuilder&) = 0; 226 virtual String getModuleLibraryArchName() const = 0; 227 228 //============================================================================== getName()229 String getName() const { return configNameValue.get(); } isDebug()230 bool isDebug() const { return isDebugValue.get(); } 231 getTargetBinaryRelativePathString()232 String getTargetBinaryRelativePathString() const { return targetBinaryPathValue.get(); } 233 String getTargetBinaryNameString (bool isUnityPlugin = false) const 234 { 235 return (isUnityPlugin ? Project::addUnityPluginPrefixIfNecessary (targetNameValue.get().toString()) 236 : targetNameValue.get().toString()); 237 } 238 getOptimisationLevelInt()239 int getOptimisationLevelInt() const { return optimisationLevelValue.get(); } 240 String getGCCOptimisationFlag() const; isLinkTimeOptimisationEnabled()241 bool isLinkTimeOptimisationEnabled() const { return linkTimeOptimisationValue.get(); } 242 getBuildConfigPreprocessorDefsString()243 String getBuildConfigPreprocessorDefsString() const { return ppDefinesValue.get(); } 244 StringPairArray getAllPreprocessorDefs() const; // includes inherited definitions 245 StringPairArray getUniquePreprocessorDefs() const; // returns pre-processor definitions that are not already in the project pre-processor defs 246 getHeaderSearchPathString()247 String getHeaderSearchPathString() const { return headerSearchPathValue.get(); } 248 StringArray getHeaderSearchPaths() const; 249 getLibrarySearchPathString()250 String getLibrarySearchPathString() const { return librarySearchPathValue.get(); } 251 StringArray getLibrarySearchPaths() const; 252 getPrecompiledHeaderFilename()253 String getPrecompiledHeaderFilename() const { return "JucePrecompiledHeader_" + getName(); } getSkipPrecompiledHeaderDefine()254 static String getSkipPrecompiledHeaderDefine() { return "JUCE_SKIP_PRECOMPILED_HEADER"; } 255 shouldUsePrecompiledHeaderFile()256 bool shouldUsePrecompiledHeaderFile() const { return usePrecompiledHeaderFileValue.get(); } 257 String getPrecompiledHeaderFileContent() const; 258 259 //============================================================================== getValue(const Identifier & nm)260 Value getValue (const Identifier& nm) { return config.getPropertyAsValue (nm, getUndoManager()); } getUndoManager()261 UndoManager* getUndoManager() const { return project.getUndoManagerFor (config); } 262 263 //============================================================================== 264 void createPropertyEditors (PropertyListBuilder&); 265 void addRecommendedLinuxCompilerWarningsProperty (PropertyListBuilder&); 266 void addRecommendedLLVMCompilerWarningsProperty (PropertyListBuilder&); 267 StringArray getRecommendedCompilerWarningFlags() const; 268 void addGCCOptimisationProperty (PropertyListBuilder&); 269 void removeFromExporter(); 270 271 //============================================================================== 272 ValueTree config; 273 Project& project; 274 const ProjectExporter& exporter; 275 276 protected: 277 ValueWithDefault isDebugValue, configNameValue, targetNameValue, targetBinaryPathValue, recommendedWarningsValue, optimisationLevelValue, 278 linkTimeOptimisationValue, ppDefinesValue, headerSearchPathValue, librarySearchPathValue, userNotesValue, 279 usePrecompiledHeaderFileValue, precompiledHeaderFileValue; 280 281 private: 282 std::map<String, StringArray> recommendedCompilerWarningFlags; 283 284 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BuildConfiguration) 285 }; 286 287 void addNewConfigurationFromExisting (const BuildConfiguration& configToCopy); 288 void addNewConfiguration (bool isDebugConfig); 289 bool hasConfigurationNamed (const String& name) const; 290 String getUniqueConfigName (String name) const; 291 292 String getExternalLibraryFlags (const BuildConfiguration& config) const; 293 294 //============================================================================== 295 struct ConfigIterator 296 { 297 ConfigIterator (ProjectExporter& exporter); 298 299 bool next(); 300 301 BuildConfiguration& operator*() const { return *config; } 302 BuildConfiguration* operator->() const { return config.get(); } 303 304 BuildConfiguration::Ptr config; 305 int index; 306 307 private: 308 ProjectExporter& exporter; 309 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigIterator) 310 }; 311 312 struct ConstConfigIterator 313 { 314 ConstConfigIterator (const ProjectExporter& exporter); 315 316 bool next(); 317 318 const BuildConfiguration& operator*() const { return *config; } 319 const BuildConfiguration* operator->() const { return config.get(); } 320 321 BuildConfiguration::Ptr config; 322 int index; 323 324 private: 325 const ProjectExporter& exporter; 326 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConstConfigIterator) 327 }; 328 329 int getNumConfigurations() const; 330 BuildConfiguration::Ptr getConfiguration (int index) const; 331 332 ValueTree getConfigurations() const; 333 virtual void createDefaultConfigs(); 334 void createDefaultModulePaths(); 335 336 //============================================================================== getExporterPreprocessorDefsValue()337 Value getExporterPreprocessorDefsValue() { return extraPPDefsValue.getPropertyAsValue(); } getExporterPreprocessorDefsString()338 String getExporterPreprocessorDefsString() const { return extraPPDefsValue.get(); } 339 340 // includes exporter, project + config defs 341 StringPairArray getAllPreprocessorDefs (const BuildConfiguration& config, const build_tools::ProjectType::Target::Type targetType) const; 342 // includes exporter + project defs 343 StringPairArray getAllPreprocessorDefs() const; 344 345 void addTargetSpecificPreprocessorDefs (StringPairArray& defs, const build_tools::ProjectType::Target::Type targetType) const; 346 347 String replacePreprocessorTokens (const BuildConfiguration&, const String& sourceString) const; 348 349 ValueTree settings; 350 351 enum GCCOptimisationLevel 352 { 353 gccO0 = 1, 354 gccO1 = 4, 355 gccO2 = 5, 356 gccO3 = 3, 357 gccOs = 2, 358 gccOfast = 6 359 }; 360 isPCHEnabledForAnyConfigurations()361 bool isPCHEnabledForAnyConfigurations() const 362 { 363 if (supportsPrecompiledHeaders()) 364 for (ConstConfigIterator config (*this); config.next();) 365 if (config->shouldUsePrecompiledHeaderFile()) 366 return true; 367 368 return false; 369 } 370 371 protected: 372 //============================================================================== 373 String name; 374 Project& project; 375 const build_tools::ProjectType& projectType; 376 const String projectName; 377 const File projectFolder; 378 379 //============================================================================== 380 ValueWithDefaultWrapper vstLegacyPathValueWrapper, rtasPathValueWrapper, aaxPathValueWrapper; 381 382 ValueWithDefault targetLocationValue, extraCompilerFlagsValue, extraLinkerFlagsValue, externalLibrariesValue, 383 userNotesValue, gnuExtensionsValue, bigIconValue, smallIconValue, extraPPDefsValue; 384 385 Value projectCompilerFlagSchemesValue; 386 HashMap<String, ValueWithDefault> compilerFlagSchemesMap; 387 388 mutable Array<Project::Item> itemGroups; 389 Project::Item* modulesGroup = nullptr; 390 391 virtual BuildConfiguration::Ptr createBuildConfig (const ValueTree&) const = 0; 392 393 void addDefaultPreprocessorDefs (StringPairArray&) const; 394 getDefaultBuildsRootFolder()395 static String getDefaultBuildsRootFolder() { return "Builds/"; } 396 getStaticLibbedFilename(String name)397 static String getStaticLibbedFilename (String name) { return addSuffix (addLibPrefix (name), ".a"); } getDynamicLibbedFilename(String name)398 static String getDynamicLibbedFilename (String name) { return addSuffix (addLibPrefix (name), ".so"); } 399 400 virtual void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType&) = 0; 401 402 //============================================================================== createDirectoryOrThrow(const File & dirToCreate)403 static void createDirectoryOrThrow (const File& dirToCreate) 404 { 405 if (! dirToCreate.createDirectory()) 406 throw build_tools::SaveError ("Can't create folder: " + dirToCreate.getFullPathName()); 407 } 408 409 static void writeXmlOrThrow (const XmlElement& xml, const File& file, const String& encoding, 410 int maxCharsPerLine, bool useUnixNewLines = false) 411 { 412 XmlElement::TextFormat format; 413 format.customEncoding = encoding; 414 format.lineWrapLength = maxCharsPerLine; 415 format.newLineChars = useUnixNewLines ? "\n" : "\r\n"; 416 417 MemoryOutputStream mo (8192); 418 xml.writeTo (mo, format); 419 build_tools::overwriteFileIfDifferentOrThrow (file, mo); 420 } 421 422 private: 423 //============================================================================== valueChanged(Value &)424 void valueChanged (Value&) override { updateCompilerFlagValues(); } 425 void updateCompilerFlagValues(); 426 427 //============================================================================== addLibPrefix(const String name)428 static String addLibPrefix (const String name) 429 { 430 return name.startsWith ("lib") ? name 431 : "lib" + name; 432 } 433 addSuffix(const String name,const String suffix)434 static String addSuffix (const String name, const String suffix) 435 { 436 return name.endsWithIgnoreCase (suffix) ? name 437 : name + suffix; 438 } 439 440 void createDependencyPathProperties (PropertyListBuilder&); 441 void createIconProperties (PropertyListBuilder&); 442 void addVSTPathsIfPluginOrHost(); 443 void addCommonAudioPluginSettings(); 444 void addLegacyVSTFolderToPathIfSpecified(); 445 build_tools::RelativePath getInternalVST3SDKPath(); 446 void addAAXFoldersToPath(); 447 448 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectExporter) 449 }; 450