From d677fd6264171473ffe953daa2c8a5514a6505dd Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 25 Aug 2020 08:51:13 +0100 Subject: [PATCH] Projucer: Added PCH support for Xcode and Visual Studio exporters --- .../Project/UI/Sidebar/jucer_FileTreeItems.h | 30 ++- .../Project/UI/jucer_ContentViewComponents.h | 2 +- .../UI/jucer_FileGroupInformationComponent.h | 39 +++- .../Projucer/Source/Project/jucer_Project.cpp | 8 + .../Projucer/Source/Project/jucer_Project.h | 12 +- .../jucer_ProjectExport_Android.h | 48 ++-- .../ProjectSaving/jucer_ProjectExport_CLion.h | 11 +- .../jucer_ProjectExport_CodeBlocks.h | 2 + .../ProjectSaving/jucer_ProjectExport_MSVC.h | 107 +++++++-- .../ProjectSaving/jucer_ProjectExport_Make.h | 13 +- .../ProjectSaving/jucer_ProjectExport_Xcode.h | 210 +++++++++++------- .../ProjectSaving/jucer_ProjectExporter.cpp | 65 +++++- .../ProjectSaving/jucer_ProjectExporter.h | 25 ++- .../ProjectSaving/jucer_ProjectSaver.cpp | 9 +- .../Utility/Helpers/jucer_MiscUtilities.cpp | 9 + .../Utility/Helpers/jucer_MiscUtilities.h | 2 + .../Source/Utility/Helpers/jucer_PresetIDs.h | 3 + .../jucer_FilePathPropertyComponent.h | 50 ++++- 18 files changed, 459 insertions(+), 186 deletions(-) diff --git a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h index cc17c535e0..8604d17d62 100644 --- a/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h +++ b/extras/Projucer/Source/Project/UI/Sidebar/jucer_FileTreeItems.h @@ -563,16 +563,11 @@ public: m.addItem (4, "Rename File..."); m.addSeparator(); - if (auto* group = dynamic_cast (getParentItem())) - { - if (group->isRoot()) - { - m.addItem (5, "Binary Resource", true, item.shouldBeAddedToBinaryResources()); - m.addItem (6, "Xcode Resource", true, item.shouldBeAddedToXcodeResources()); - m.addItem (7, "Compile", true, item.shouldBeCompiled()); - m.addSeparator(); - } - } + m.addItem (5, "Binary Resource", true, item.shouldBeAddedToBinaryResources()); + m.addItem (6, "Xcode Resource", true, item.shouldBeAddedToXcodeResources()); + m.addItem (7, "Compile", item.isSourceFile(), item.shouldBeCompiled()); + m.addItem (8, "Skip PCH", item.shouldBeCompiled(), item.shouldSkipPCH()); + m.addSeparator(); m.addItem (3, "Delete"); @@ -589,13 +584,14 @@ public: { switch (resultCode) { - case 1: getFile().startAsProcess(); break; - case 2: revealInFinder(); break; - case 3: deleteAllSelectedItems(); break; - case 4: triggerAsyncRename (item); break; - case 5: item.getShouldAddToBinaryResourcesValue().setValue (! item.shouldBeAddedToBinaryResources()); break; - case 6: item.getShouldAddToXcodeResourcesValue().setValue (! item.shouldBeAddedToXcodeResources()); break; - case 7: item.getShouldCompileValue().setValue (! item.shouldBeCompiled()); break; + case 1: getFile().startAsProcess(); break; + case 2: revealInFinder(); break; + case 3: deleteAllSelectedItems(); break; + case 4: triggerAsyncRename (item); break; + case 5: item.getShouldAddToBinaryResourcesValue().setValue (! item.shouldBeAddedToBinaryResources()); break; + case 6: item.getShouldAddToXcodeResourcesValue().setValue (! item.shouldBeAddedToXcodeResources()); break; + case 7: item.getShouldCompileValue().setValue (! item.shouldBeCompiled()); break; + case 8: item.getShouldSkipPCHValue().setValue (! item.shouldSkipPCH()); break; default: if (auto* parentGroup = dynamic_cast (getParentProjectItem())) diff --git a/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h b/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h index ae4f80ae48..7bc29f5429 100644 --- a/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h +++ b/extras/Projucer/Source/Project/UI/jucer_ContentViewComponents.h @@ -223,7 +223,7 @@ private: g.setColour (findColour (defaultTextColourId)); g.setFont (Font (14.0f)); - g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 10, 1.0f); + g.drawFittedText (stringToDisplay, getLocalBounds(), Justification::centred, 15, 0.75f); } String stringToDisplay; diff --git a/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h b/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h index 1b5eb58072..78bb426c35 100644 --- a/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h +++ b/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h @@ -36,8 +36,8 @@ public: : item (group), header (item.getName(), { getIcons().openFolder, Colours::transparentBlack }) { - list.setHeaderComponent (std::make_unique (Array { "File", "Binary Resource", "Xcode Resource", "Compile", "Compiler Flag Scheme" }, - Array { 0.3f, 0.15f, 0.15f, 0.15f, 0.25f })); + list.setHeaderComponent (std::make_unique (Array { "File", "Binary Resource", "Xcode Resource", "Compile", "Skip PCH", "Compiler Flag Scheme" }, + Array { 0.25f, 0.125f, 0.125f, 0.125f, 0.125f, 0.25f })); list.setModel (this); list.setColour (ListBox::backgroundColourId, Colours::transparentBlack); addAndMakeVisible (list); @@ -136,9 +136,14 @@ private: { if (item.isFile()) { - addAndMakeVisible (compileButton); - compileButton.getToggleStateValue().referTo (item.getShouldCompileValue()); - compileButton.onStateChange = [this] { compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); }; + auto isSourceFile = item.isSourceFile(); + + if (isSourceFile) + { + addAndMakeVisible (compileButton); + compileButton.getToggleStateValue().referTo (item.getShouldCompileValue()); + compileButton.onStateChange = [this] { compileEnablementChanged(); }; + } addAndMakeVisible (binaryResourceButton); binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue()); @@ -146,8 +151,15 @@ private: addAndMakeVisible (xcodeResourceButton); xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue()); - addChildComponent (compilerFlagSchemeSelector); - compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); + if (isSourceFile) + { + addChildComponent (skipPCHButton); + skipPCHButton.getToggleStateValue().referTo (item.getShouldSkipPCHValue()); + + addChildComponent (compilerFlagSchemeSelector); + + compileEnablementChanged(); + } } } @@ -182,7 +194,8 @@ private: binaryResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (1) * width))); xcodeResourceButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (2) * width))); compileButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (3) * width))); - compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width))); + skipPCHButton.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (4) * width))); + compilerFlagSchemeSelector.setBounds (bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (5) * width))); } } @@ -331,10 +344,18 @@ private: Label newSchemeLabel; }; + void compileEnablementChanged() + { + auto shouldBeCompiled = compileButton.getToggleState(); + + skipPCHButton.setVisible (shouldBeCompiled); + compilerFlagSchemeSelector.setVisible (shouldBeCompiled); + } + //============================================================================== ListBoxHeader* header; - ToggleButton compileButton, binaryResourceButton, xcodeResourceButton; + ToggleButton compileButton, binaryResourceButton, xcodeResourceButton, skipPCHButton; CompilerFlagSchemeSelector compilerFlagSchemeSelector; }; diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 40c14b1672..b5f2c99629 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -1510,6 +1510,11 @@ bool Project::Item::isImageFile() const || getFile().hasFileExtension ("svg")); } +bool Project::Item::isSourceFile() const +{ + return isFile() && getFile().hasFileExtension (sourceFileExtensions); +} + Project::Item Project::Item::findItemWithID (const String& targetId) const { if (state [Ids::ID] == targetId) @@ -1565,6 +1570,9 @@ bool Project::Item::shouldInhibitWarnings() const { return state [Ids: bool Project::Item::isModuleCode() const { return belongsToModule; } +Value Project::Item::getShouldSkipPCHValue() { return state.getPropertyAsValue (Ids::skipPCH, getUndoManager()); } +bool Project::Item::shouldSkipPCH() const { return isModuleCode() || state [Ids::skipPCH]; } + Value Project::Item::getCompilerFlagSchemeValue() { return state.getPropertyAsValue (Ids::compilerFlagScheme, getUndoManager()); } String Project::Item::getCompilerFlagSchemeString() const { return state [Ids::compilerFlagScheme]; } diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index ff12581974..3e220c657f 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -214,11 +214,11 @@ public: void addCompilerFlagScheme (const String&); void removeCompilerFlagScheme (const String&); - String getPostExportShellCommandPosixString() const { return postExportShellCommandPosixValue.get(); } - String getPostExportShellCommandWinString() const { return postExportShellCommandWinValue.get(); } + String getPostExportShellCommandPosixString() const { return postExportShellCommandPosixValue.get(); } + String getPostExportShellCommandWinString() const { return postExportShellCommandWinValue.get(); } - bool shouldUseAppConfig() const { return useAppConfigValue.get(); } - bool shouldAddUsingNamespaceToJuceHeader() const { return addUsingNamespaceToJuceHeader.get(); } + bool shouldUseAppConfig() const { return useAppConfigValue.get(); } + bool shouldAddUsingNamespaceToJuceHeader() const { return addUsingNamespaceToJuceHeader.get(); } //============================================================================== String getPluginNameString() const { return pluginNameValue.get(); } @@ -340,6 +340,7 @@ public: bool isGroup() const; bool isMainGroup() const; bool isImageFile() const; + bool isSourceFile() const; String getID() const; void setID (const String& newID); @@ -374,6 +375,9 @@ public: bool isModuleCode() const; + Value getShouldSkipPCHValue(); + bool shouldSkipPCH() const; + Value getCompilerFlagSchemeValue(); String getCompilerFlagSchemeString() const; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h index b688f50946..66be43e178 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h @@ -48,6 +48,8 @@ public: bool canCopeWithDuplicateFiles() override { return false; } bool supportsUserDefinedConfigurations() const override { return true; } + String getNewLineString() const override { return "\n"; } + bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override { return type == build_tools::ProjectType::Target::GUIApp || type == build_tools::ProjectType::Target::StaticLibrary @@ -238,7 +240,7 @@ public: { build_tools::writeStreamToFile (gradleProjectFolder.getChildFile (filePath), [&] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << fileContent; }); } @@ -247,7 +249,7 @@ public: { build_tools::writeStreamToFile (gradleProjectFolder.getChildFile (filePath), [&] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo.write (binaryData, static_cast (binarySize)); }); } @@ -340,7 +342,7 @@ private: { build_tools::writeStreamToFile (file, [&] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "# Automatically generated makefile, created by the Projucer" << newLine << "# Don't edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine @@ -578,7 +580,7 @@ private: String getGradleSettingsFileContent() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "rootProject.name = " << "\'" << projectName << "\'" << newLine; mo << (isLibrary() ? "include ':lib'" : "include ':app'"); @@ -594,7 +596,7 @@ private: String getProjectBuildGradleFileContent() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "buildscript {" << newLine; mo << " repositories {" << newLine; @@ -621,7 +623,7 @@ private: String getAppBuildGradleFileContent (const OwnedArray& modules) const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "apply plugin: 'com.android." << (isLibrary() ? "library" : "application") << "'" << newLine << newLine; @@ -653,7 +655,7 @@ private: String getAndroidProductFlavours() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " flavorDimensions \"default\"" << newLine; mo << " productFlavors {" << newLine; @@ -702,7 +704,7 @@ private: String getAndroidSigningConfig() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); auto keyStoreFilePath = androidKeyStore.get().toString().replace ("${user.home}", "${System.properties['user.home']}") .replace ("/", "${File.separator}"); @@ -728,7 +730,7 @@ private: auto targetSdkVersion = static_cast (androidTargetSDK.get()); MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " defaultConfig {" << newLine; @@ -753,7 +755,7 @@ private: String getAndroidBuildTypes() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " buildTypes {" << newLine; @@ -784,7 +786,7 @@ private: String getAndroidVariantFilter() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " variantFilter { variant ->" << newLine; mo << " def names = variant.flavors*.name" << newLine; @@ -807,7 +809,7 @@ private: String getAndroidProjectRepositories() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); auto repositories = StringArray::fromLines (androidProjectRepositories.get().toString()); @@ -827,7 +829,7 @@ private: String getAndroidRepositories() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); auto repositories = StringArray::fromLines (androidRepositories.get().toString()); @@ -844,7 +846,7 @@ private: String getAndroidDependencies() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " dependencies {" << newLine; @@ -871,7 +873,7 @@ private: String getApplyPlugins() const { MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); if (areRemoteNotificationsEnabled()) mo << "apply plugin: 'com.google.gms.google-services'" << newLine; @@ -931,12 +933,12 @@ private: addOptJavaFolderToSourceSetsForModule (javaSourceSets, modules, "juce_product_unlocking"); MemoryOutputStream mo; - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << " sourceSets {" << newLine; - mo << getSourceSetStringFor ("main.java.srcDirs", javaSourceSets); + mo << getSourceSetStringFor ("main.java.srcDirs", javaSourceSets, getNewLineString()); mo << newLine; - mo << getSourceSetStringFor ("main.res.srcDirs", resourceSets); + mo << getSourceSetStringFor ("main.res.srcDirs", resourceSets, getNewLineString()); mo << " }" << newLine; return mo.toString(); @@ -967,7 +969,7 @@ private: return sourceSets; } - static String getSourceSetStringFor (const String& type, const StringArray& srcDirs) + static String getSourceSetStringFor (const String& type, const StringArray& srcDirs, const String& newLineString) { String s; @@ -987,7 +989,7 @@ private: s << "]" << newLine; - return replaceLineFeeds (s, "\n"); + return replaceLineFeeds (s, newLineString); } //============================================================================== @@ -998,7 +1000,7 @@ private: props << "ndk.dir=" << sanitisePath (getAppSettings().getStoredPath (Ids::androidNDKPath, TargetOS::getThisOS()).get().toString()) << newLine << "sdk.dir=" << sanitisePath (getAppSettings().getStoredPath (Ids::androidSDKPath, TargetOS::getThisOS()).get().toString()) << newLine; - return replaceLineFeeds (props, "\n"); + return replaceLineFeeds (props, getNewLineString()); } String getGradleWrapperPropertiesFileContent() const @@ -1302,7 +1304,7 @@ private: build_tools::writeStreamToFile (file, [&] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); PNGImageFormat png; @@ -1355,7 +1357,7 @@ private: if (projectItem.isGroup()) { for (int i = 0; i < projectItem.getNumChildren(); ++i) - addCompileUnits (projectItem.getChild(i), mo, excludeFromBuild, extraCompilerFlags); + addCompileUnits (projectItem.getChild (i), mo, excludeFromBuild, extraCompilerFlags); } else if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (*this)) { diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h index 74c94f2abb..30ef537b5c 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h @@ -97,6 +97,8 @@ public: bool isOSX() const override { return false; } bool isiOS() const override { return false; } + String getNewLineString() const override { return "\n"; } + bool supportsTargetType (build_tools::ProjectType::Target::Type) const override { return true; } void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType&) override {} @@ -168,9 +170,9 @@ public: void create (const OwnedArray&) const override { // We'll append to this later. - build_tools::writeStreamToFile (getTargetFolder().getChildFile ("CMakeLists.txt"), [] (MemoryOutputStream& mo) + build_tools::writeStreamToFile (getTargetFolder().getChildFile ("CMakeLists.txt"), [this] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "# Automatically generated CMakeLists, created by the Projucer" << newLine << "# Do not edit this file! Your changes will be overwritten when you re-save the Projucer project!" << newLine @@ -199,7 +201,7 @@ public: getTargetFolder().getChildFile ("CMakeLists.txt").loadFileAsData (existingContent); MemoryOutputStream out (existingContent, true); - out.setNewLineString ("\n"); + out.setNewLineString (getNewLineString()); out << "###############################################################################" << newLine << "# " << exporter->getUniqueName() << newLine @@ -314,7 +316,8 @@ private: { auto path = build_tools::RelativePath (projectItem.getFile(), exporter.getTargetFolder(), build_tools::RelativePath::buildTargetFolder).toUnixStyle(); - fileInfoList.push_back (std::make_tuple (path, projectItem.shouldBeCompiled(), + fileInfoList.push_back (std::make_tuple (path, + projectItem.shouldBeCompiled(), exporter.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString())); } } diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h index a04824e4c6..c6af8a08f2 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h @@ -98,6 +98,8 @@ public: bool isOSX() const override { return false; } bool isiOS() const override { return false; } + String getNewLineString() const override { return isWindows() ? "\r\n" : "\n"; } + bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override { switch (type) diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h index dfd68cdf64..b22f3366bb 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h @@ -478,7 +478,7 @@ public: } { - auto* intdir = props->createNewChildElement("IntDir"); + auto* intdir = props->createNewChildElement ("IntDir"); setConditionAttribute (*intdir, config); auto intermediatesPath = getIntermediatesPath (config); @@ -556,7 +556,7 @@ public: cl->createNewChildElement ("RuntimeLibrary")->addTextElement (config.isUsingRuntimeLibDLL() ? (isDebug ? "MultiThreadedDebugDLL" : "MultiThreadedDLL") : (isDebug ? "MultiThreadedDebug" : "MultiThreaded")); cl->createNewChildElement ("RuntimeTypeInfo")->addTextElement ("true"); - cl->createNewChildElement ("PrecompiledHeader"); + cl->createNewChildElement ("PrecompiledHeader")->addTextElement ("NotUsing"); cl->createNewChildElement ("AssemblerListingLocation")->addTextElement ("$(IntDir)\\"); cl->createNewChildElement ("ObjectFileName")->addTextElement ("$(IntDir)\\"); cl->createNewChildElement ("ProgramDataBaseFileName")->addTextElement ("$(IntDir)\\"); @@ -698,6 +698,8 @@ public: auto* cppFiles = projectXml.createNewChildElement ("ItemGroup"); auto* headerFiles = projectXml.createNewChildElement ("ItemGroup"); + writePrecompiledHeaderFiles (*cppFiles); + for (int i = 0; i < getOwner().getAllGroups().size(); ++i) { auto& group = getOwner().getAllGroups().getReference (i); @@ -761,6 +763,59 @@ public: } //============================================================================== + static void setSourceFilePCHSettings (XmlElement& element, const File& pchFile, const String& option, const BuildConfiguration& config) + { + auto setConfigConditionAttribute = [&config] (XmlElement* elementToSet) -> XmlElement* + { + setConditionAttribute (*elementToSet, config); + return elementToSet; + }; + + setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeader"))->addTextElement (option); + setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeaderFile"))->addTextElement (pchFile.getFileName()); + setConfigConditionAttribute (element.createNewChildElement ("PrecompiledHeaderOutputFile"))->addTextElement ("$(Platform)\\$(Configuration)\\JucePrecompiledHeader.pch"); + setConfigConditionAttribute (element.createNewChildElement ("ForcedIncludeFiles"))->addTextElement (pchFile.getFileName()); + } + + void writePrecompiledHeaderFiles (XmlElement& cpps) const + { + for (ConstConfigIterator config (owner); config.next();) + { + if (config->shouldUsePrecompiledHeaderFile()) + { + auto pchFileContent = config->getPrecompiledHeaderFileContent(); + + if (pchFileContent.isNotEmpty()) + { + auto pchFile = owner.getTargetFolder().getChildFile (config->getPrecompiledHeaderFilename()).withFileExtension (".h"); + + build_tools::writeStreamToFile (pchFile, [&] (MemoryOutputStream& mo) + { + mo << pchFileContent; + }); + + auto pchSourceFile = pchFile.withFileExtension (".cpp"); + + build_tools::writeStreamToFile (pchSourceFile, [this] (MemoryOutputStream& mo) + { + mo.setNewLineString (owner.getNewLineString()); + + writeAutoGenWarningComment (mo); + + mo << " This is an empty source file generated by JUCE required for Visual Studio PCH." << newLine + << newLine + << "*/" << newLine + << newLine; + }); + + auto* pchSourceElement = cpps.createNewChildElement ("ClCompile"); + pchSourceElement->setAttribute ("Include", prependDot (pchSourceFile.getFileName())); + setSourceFilePCHSettings (*pchSourceElement, pchFile, "Create", *config); + } + } + } + } + void addFilesToCompile (const Project::Item& projectItem, XmlElement& cpps, XmlElement& headers, XmlElement& otherFiles) const { auto targetType = (getOwner().getProject().isAudioPluginProject() ? type : SharedCodeTarget); @@ -791,6 +846,20 @@ public: if (extraCompilerFlags.isNotEmpty()) e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)"); + + if (! projectItem.shouldSkipPCH()) + { + for (ConstConfigIterator i (owner); i.next();) + { + if (i->shouldUsePrecompiledHeaderFile()) + { + auto pchFile = owner.getTargetFolder().getChildFile (i->getPrecompiledHeaderFilename()).withFileExtension (".h"); + + if (pchFile.existsAsFile()) + setSourceFilePCHSettings (*e, pchFile, "Use", *i); + } + } + } } else { @@ -808,7 +877,7 @@ public: } } - void setConditionAttribute (XmlElement& xml, const BuildConfiguration& config) const + static void setConditionAttribute (XmlElement& xml, const BuildConfiguration& config) { auto& msvcConfig = dynamic_cast (config); xml.setAttribute ("Condition", "'$(Configuration)|$(Platform)'=='" + msvcConfig.createMSVCConfigName() + "'"); @@ -1314,22 +1383,26 @@ public: }; //============================================================================== - bool usesMMFiles() const override { return false; } - bool canCopeWithDuplicateFiles() override { return false; } - bool supportsUserDefinedConfigurations() const override { return true; } + bool usesMMFiles() const override { return false; } + bool canCopeWithDuplicateFiles() override { return false; } + bool supportsUserDefinedConfigurations() const override { return true; } - bool isXcode() const override { return false; } - bool isVisualStudio() const override { return true; } - bool isCodeBlocks() const override { return false; } - bool isMakefile() const override { return false; } - bool isAndroidStudio() const override { return false; } - bool isCLion() const override { return false; } + bool isXcode() const override { return false; } + bool isVisualStudio() const override { return true; } + bool isCodeBlocks() const override { return false; } + bool isMakefile() const override { return false; } + bool isAndroidStudio() const override { return false; } + bool isCLion() const override { return false; } - bool isAndroid() const override { return false; } - bool isWindows() const override { return true; } - bool isLinux() const override { return false; } - bool isOSX() const override { return false; } - bool isiOS() const override { return false; } + bool isAndroid() const override { return false; } + bool isWindows() const override { return true; } + bool isLinux() const override { return false; } + bool isOSX() const override { return false; } + bool isiOS() const override { return false; } + + bool supportsPrecompiledHeaders() const override { return true; } + + String getNewLineString() const override { return "\r\n"; } bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override { diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h index e7fdbb184a..86c33e794b 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h @@ -255,7 +255,8 @@ public: out << "OBJECTS_" + getTargetVarName() + String (" := \\") << newLine; for (auto& f : filesToCompile) - out << " $(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor ({ f.first, owner.getTargetFolder(), build_tools::RelativePath::buildTargetFolder })) << " \\" << newLine; + out << " $(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor ({ f.first, owner.getTargetFolder(), build_tools::RelativePath::buildTargetFolder })) + << " \\" << newLine; out << newLine; } @@ -274,7 +275,7 @@ public: << "\t@echo \"Compiling " << relativePath.getFileName() << "\"" << newLine << (relativePath.hasFileExtension ("c;s;S") ? "\t$(V_AT)$(CC) $(JUCE_CFLAGS) " : "\t$(V_AT)$(CXX) $(JUCE_CXXFLAGS) ") << "$(" << cppflagsVarName << ") $(" << cflagsVarName << ")" - << (f.second.isNotEmpty() ? " $(" + owner.getCompilerFlagSchemeVariableName (f.second) + ")" : "") << " -o \"$@\" -c \"$<\"" << newLine + << (f.second.isNotEmpty() ? " $(" + owner.getCompilerFlagSchemeVariableName (f.second) + ")" : "") << " -o \"$@\" -c \"$<\"" << newLine << newLine; } } @@ -415,6 +416,8 @@ public: bool isOSX() const override { return false; } bool isiOS() const override { return false; } + String getNewLineString() const override { return "\n"; } + bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override { switch (type) @@ -474,7 +477,7 @@ public: { build_tools::writeStreamToFile (getTargetFolder().getChildFile ("Makefile"), [&] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); writeMakefile (mo); }); @@ -955,7 +958,9 @@ private: writeCompilerFlagSchemes (out, filesToCompile); - auto getFilesForTarget = [] (const Array>& files, MakefileTarget* target, const Project& p) -> Array> + auto getFilesForTarget = [] (const Array>& files, + MakefileTarget* target, + const Project& p) -> Array> { Array> targetFiles; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 15fe99fe9d..15ae033c77 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -295,6 +295,10 @@ public: bool isOSX() const override { return ! iOS; } bool isiOS() const override { return iOS; } + bool supportsPrecompiledHeaders() const override { return true; } + + String getNewLineString() const override { return "\n"; } + bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override { switch (type) @@ -1387,6 +1391,23 @@ public: s.set ("GCC_OPTIMIZATION_LEVEL", config.getGCCOptimisationFlag()); + if (config.shouldUsePrecompiledHeaderFile()) + { + s.set ("GCC_PRECOMPILE_PREFIX_HEADER", "YES"); + + auto pchFileContent = config.getPrecompiledHeaderFileContent(); + + if (pchFileContent.isNotEmpty()) + { + auto pchFilename = config.getPrecompiledHeaderFilename() + ".h"; + + build_tools::writeStreamToFile (owner.getTargetFolder().getChildFile (pchFilename), + [&] (MemoryOutputStream& mo) { mo << pchFileContent; }); + + s.set ("GCC_PREFIX_HEADER", pchFilename); + } + } + if (shouldCreatePList()) { s.set ("INFOPLIST_FILE", infoPlistFile.getFileName()); @@ -1975,8 +1996,7 @@ private: writeDefaultLaunchStoryboardFile(); else if (getProject().getProjectFolder().getChildFile (customLaunchStoryboard).existsAsFile()) addLaunchStoryboardFileReference (build_tools::RelativePath (customLaunchStoryboard, build_tools::RelativePath::projectFolder) - .rebased (getProject().getProjectFolder(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder) - .toUnixStyle()); + .rebased (getProject().getProjectFolder(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder)); } } else @@ -2053,7 +2073,7 @@ private: build_tools::RelativePath menuNibPath (menuNibFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); addFileReference (menuNibPath.toUnixStyle()); - resourceIDs.add (addBuildFile (menuNibPath, false, false)); + resourceIDs.add (addBuildFile (FileOptions().withRelativePath (menuNibPath))); resourceFileRefs.add (createFileRefID (menuNibPath)); } @@ -2063,7 +2083,7 @@ private: { build_tools::RelativePath iconPath (iconFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); addFileReference (iconPath.toUnixStyle()); - resourceIDs.add (addBuildFile (iconPath, false, false)); + resourceIDs.add (addBuildFile (FileOptions().withRelativePath (iconPath))); resourceFileRefs.add (createFileRefID (iconPath)); } } @@ -2216,7 +2236,8 @@ private: auto path = scriptPath.toUnixStyle(); auto refID = addFileReference (path); - auto fileID = addBuildFile (path, refID, false, false); + auto fileID = addBuildFile (FileOptions().withPath (path) + .withFileRefID (refID)); resourceIDs.add (fileID); resourceFileRefs.add (refID); @@ -2281,9 +2302,9 @@ private: .getChildFile ("xcshareddata") .getChildFile ("WorkspaceSettings.xcsettings"); - build_tools::writeStreamToFile (settingsFile, [] (MemoryOutputStream& mo) + build_tools::writeStreamToFile (settingsFile, [this] (MemoryOutputStream& mo) { - mo.setNewLineString ("\n"); + mo.setNewLineString (getNewLineString()); mo << "" << newLine << "" << newLine @@ -2586,7 +2607,7 @@ private: if (availableBuildProducts.empty()) continue; - auto subprojectFileType = getFileType (build_tools::RelativePath (subprojectFile.getFullPathName(), build_tools::RelativePath::buildTargetFolder)); + auto subprojectFileType = getFileType (subprojectFile.getFullPathName()); auto subprojectFileID = addFileOrFolderReference (subprojectFile.getFullPathName(), "", subprojectFileType); subprojectFileIDs.add (subprojectFileID); @@ -2594,7 +2615,7 @@ private: for (auto& buildProduct : availableBuildProducts) { - auto buildProductFileType = getFileType (build_tools::RelativePath (buildProduct.second, build_tools::RelativePath::projectFolder)); + auto buildProductFileType = getFileType (buildProduct.second); auto containerID = addContainerItemProxy (subprojectFileID, buildProduct.first); auto proxyID = addReferenceProxy (containerID, buildProduct.second, buildProductFileType); @@ -2602,7 +2623,9 @@ private: if (buildProductFileType == "archive.ar" || buildProductFileType == "wrapper.framework") { - auto buildFileID = addBuildFile (buildProduct.second, proxyID, false, true); + auto buildFileID = addBuildFile (FileOptions().withPath (buildProduct.second) + .withFileRefID (proxyID) + .withInhibitWarningsEnabled (true)); for (auto& target : targets) target->frameworkIDs.add (buildFileID); @@ -2649,7 +2672,9 @@ private: addFileOrFolderReference (folderPath, "", fileType); - resourceIDs.add (addBuildFile (folderPath, fileRefID, false, false)); + resourceIDs.add (addBuildFile (FileOptions().withPath (folderPath) + .withFileRefID (fileRefID))); + resourceFileRefs.add (createFileRefID (folderPath)); } @@ -2694,38 +2719,6 @@ private: output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n"; } - String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, - XcodeTarget* xcodeTarget = nullptr, String compilerFlags = {}) const - { - auto fileID = createID (path + "buildref"); - - if (addToSourceBuildPhase) - { - if (xcodeTarget != nullptr) - xcodeTarget->sourceIDs.add (fileID); - else - sourceIDs.add (fileID); - } - - auto* v = new ValueTree (fileID); - v->setProperty ("isa", "PBXBuildFile", nullptr); - v->setProperty ("fileRef", fileRefID, nullptr); - - if (inhibitWarnings) - compilerFlags += " -w"; - - if (compilerFlags.isNotEmpty()) - v->setProperty ("settings", "{ COMPILER_FLAGS = \"" + compilerFlags.trim() + "\"; }", nullptr); - - pbxBuildFiles.add (v); - return fileID; - } - - String addBuildFile (const build_tools::RelativePath& path, bool addToSourceBuildPhase, bool inhibitWarnings, XcodeTarget* xcodeTarget = nullptr) const - { - return addBuildFile (path.toUnixStyle(), createFileRefID (path), addToSourceBuildPhase, inhibitWarnings, xcodeTarget); - } - String addFileReference (String pathString) const { String sourceTree ("SOURCE_ROOT"); @@ -2741,9 +2734,7 @@ private: sourceTree = ""; } - auto fileType = getFileType (path); - - return addFileOrFolderReference (pathString, sourceTree, fileType); + return addFileOrFolderReference (pathString, sourceTree, getFileType (pathString)); } void checkAndAddFileReference (std::unique_ptr v) const @@ -2817,8 +2808,34 @@ public: } private: - static String getFileType (const build_tools::RelativePath& file) + struct FileOptions { + FileOptions& withPath (const String& p) { path = p; return *this; } + FileOptions& withRelativePath (const build_tools::RelativePath& p) { path = p.toUnixStyle(); return *this; } + FileOptions& withFileRefID (const String& fid) { fileRefID = fid; return *this; } + FileOptions& withCompilerFlags (const String& f) { compilerFlags = f; return *this; } + FileOptions& withCompilationEnabled (bool e) { compile = e; return *this; } + FileOptions& withAddToBinaryResourcesEnabled (bool e) { addToBinaryResources = e; return *this; } + FileOptions& withAddToXcodeResourcesEnabled (bool e) { addToXcodeResources = e; return *this; } + FileOptions& withInhibitWarningsEnabled (bool e) { inhibitWarnings = e; return *this; } + FileOptions& withSkipPCHEnabled (bool e) { skipPCH = e; return *this; } + FileOptions& withXcodeTarget (XcodeTarget* t) { xcodeTarget = t; return *this; } + + String path; + String fileRefID; + String compilerFlags; + bool compile = false; + bool addToBinaryResources = false; + bool addToXcodeResources = false; + bool inhibitWarnings = false; + bool skipPCH = false; + XcodeTarget* xcodeTarget = nullptr; + }; + + static String getFileType (const String& filePath) + { + auto file = File::createFileWithoutCheckingPath (filePath); + if (file.hasFileExtension (cppFileExtensions)) return "sourcecode.cpp.cpp"; if (file.hasFileExtension (".mm")) return "sourcecode.cpp.objcpp"; if (file.hasFileExtension (".m")) return "sourcecode.c.objc"; @@ -2842,23 +2859,17 @@ private: return "file" + file.getFileExtension(); } - String addFile (const build_tools::RelativePath& path, bool shouldBeCompiled, bool shouldBeAddedToBinaryResources, - bool shouldBeAddedToXcodeResources, bool inhibitWarnings, XcodeTarget* xcodeTarget, const String& compilerFlags) const + String addFile (const FileOptions& opts) const { - auto pathAsString = path.toUnixStyle(); - auto refID = addFileReference (path.toUnixStyle()); + auto refID = addFileReference (opts.path); - if (shouldBeCompiled) + if (opts.compile || opts.addToXcodeResources) { - addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget, compilerFlags); - } - else if (! shouldBeAddedToBinaryResources || shouldBeAddedToXcodeResources) - { - auto fileType = getFileType (path); + auto fileID = addBuildFile (FileOptions (opts).withFileRefID (refID)); - if (shouldBeAddedToXcodeResources) + if (opts.addToXcodeResources) { - resourceIDs.add (addBuildFile (pathAsString, refID, false, false)); + resourceIDs.add (fileID); resourceFileRefs.add (refID); } } @@ -2866,16 +2877,50 @@ private: return refID; } + String addBuildFile (const FileOptions& opts) const + { + auto fileID = createID (opts.path + "buildref"); + + if (opts.compile) + { + if (opts.xcodeTarget != nullptr) + opts.xcodeTarget->sourceIDs.add (fileID); + else + sourceIDs.add (fileID); + } + + auto* v = new ValueTree (fileID); + v->setProperty ("isa", "PBXBuildFile", nullptr); + v->setProperty ("fileRef", opts.fileRefID.isEmpty() ? createFileRefID (opts.path) + : opts.fileRefID, + nullptr); + + auto compilerFlags = [&opts] + { + return (opts.compilerFlags + + (opts.inhibitWarnings ? " -w" : String()) + + (opts.skipPCH ? " -D" + BuildConfiguration::getSkipPrecompiledHeaderDefine() : String())).trim(); + }(); + + if (compilerFlags.isNotEmpty()) + v->setProperty ("settings", "{ COMPILER_FLAGS = \"" + compilerFlags + "\"; }", nullptr); + + pbxBuildFiles.add (v); + return fileID; + } + String addRezFile (const Project::Item& projectItem, const build_tools::RelativePath& path) const { - auto pathAsString = path.toUnixStyle(); auto refID = addFileReference (path.toUnixStyle()); if (projectItem.isModuleCode()) { if (auto* xcodeTarget = getTargetOfType (getProject().getTargetTypeFromFilePath (projectItem.getFile(), false))) { - auto rezFileID = addBuildFile (pathAsString, refID, false, false, xcodeTarget); + auto rezFileID = addBuildFile (FileOptions().withRelativePath (path) + .withFileRefID (refID) + .withXcodeTarget (xcodeTarget)); + xcodeTarget->rezFileIDs.add (rezFileID); return refID; @@ -2907,7 +2952,7 @@ private: build_tools::overwriteFileIfDifferentOrThrow (entitlementsFile, options.getEntitlementsFileContent()); build_tools::RelativePath entitlementsPath (entitlementsFile, getTargetFolder(), build_tools::RelativePath::buildTargetFolder); - addFile (entitlementsPath, false, false, false, false, nullptr, {}); + addFile (FileOptions().withRelativePath (entitlementsPath)); } String addProjectItem (const Project::Item& projectItem) const @@ -2951,12 +2996,14 @@ private: if (projectItem.isModuleCode() && projectItem.shouldBeCompiled()) xcodeTarget = getTargetOfType (project.getTargetTypeFromFilePath (projectItem.getFile(), false)); - return addFile (path, projectItem.shouldBeCompiled(), - projectItem.shouldBeAddedToBinaryResources(), - projectItem.shouldBeAddedToXcodeResources(), - projectItem.shouldInhibitWarnings(), - xcodeTarget, - compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get()); + return addFile (FileOptions().withRelativePath (path) + .withCompilerFlags (compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get()) + .withCompilationEnabled (projectItem.shouldBeCompiled()) + .withAddToBinaryResourcesEnabled (projectItem.shouldBeAddedToBinaryResources()) + .withAddToXcodeResourcesEnabled (projectItem.shouldBeAddedToXcodeResources()) + .withInhibitWarningsEnabled (projectItem.shouldInhibitWarnings()) + .withSkipPCHEnabled (isPCHEnabledForAnyConfigurations() && projectItem.shouldSkipPCH()) + .withXcodeTarget (xcodeTarget)); } return {}; @@ -2978,7 +3025,8 @@ private: addFileReference (((File::isAbsolutePath (frameworkName) || isRelativePath) ? "" : "${SDKROOT}/") + path); frameworkFileIDs.add (fileRefID); - return addBuildFile (path, fileRefID, false, false); + return addBuildFile (FileOptions().withPath (path) + .withFileRefID (fileRefID)); } String addCustomFramework (String frameworkPath) const @@ -2988,19 +3036,20 @@ private: auto fileRefID = createFileRefID (frameworkPath); - auto fileType = getFileType (build_tools::RelativePath (frameworkPath, build_tools::RelativePath::projectFolder)); + auto fileType = getFileType (frameworkPath); addFileOrFolderReference (frameworkPath, "", fileType); frameworkFileIDs.add (fileRefID); - return addBuildFile (frameworkPath, fileRefID, false, false); + return addBuildFile (FileOptions().withPath (frameworkPath) + .withFileRefID (fileRefID)); } String addEmbeddedFramework (const String& path) const { auto fileRefID = createFileRefID (path); - auto fileType = getFileType (build_tools::RelativePath (path, build_tools::RelativePath::projectFolder)); + auto fileType = getFileType (path); addFileOrFolderReference (path, "", fileType); auto fileID = createID (path + "buildref"); @@ -3213,13 +3262,16 @@ private: addLaunchStoryboardFileReference (build_tools::RelativePath (storyboardFile, getTargetFolder(), - build_tools::RelativePath::buildTargetFolder).toUnixStyle()); + build_tools::RelativePath::buildTargetFolder)); } - void addLaunchStoryboardFileReference (const String& relativePath) const + void addLaunchStoryboardFileReference (const build_tools::RelativePath& relativePath) const { - auto refID = addFileReference (relativePath); - auto fileID = addBuildFile (relativePath, refID, false, false); + auto path = relativePath.toUnixStyle(); + + auto refID = addFileReference (path); + auto fileID = addBuildFile (FileOptions().withPath (path) + .withFileRefID (refID)); resourceIDs.add (fileID); resourceFileRefs.add (refID); @@ -3231,7 +3283,7 @@ private: getTargetFolder(), project.getProjectFilenameRootString()); addFileReference (assetsPath.toUnixStyle()); - resourceIDs.add (addBuildFile (assetsPath, false, false)); + resourceIDs.add (addBuildFile (FileOptions().withRelativePath (assetsPath))); resourceFileRefs.add (createFileRefID (assetsPath)); } @@ -3264,8 +3316,8 @@ private: } String createFileRefID (const build_tools::RelativePath& path) const { return createFileRefID (path.toUnixStyle()); } - String createFileRefID (const String& path) const { return createID ("__fileref_" + path); } - String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); } + String createFileRefID (const String& path) const { return createID ("__fileref_" + path); } + String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); } bool shouldFileBeCompiledByDefault (const File& file) const override { diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp index d1ae71aa9c..cea411a568 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp @@ -845,17 +845,19 @@ bool ProjectExporter::ConstConfigIterator::next() //============================================================================== ProjectExporter::BuildConfiguration::BuildConfiguration (Project& p, const ValueTree& configNode, const ProjectExporter& e) : config (configNode), project (p), exporter (e), - isDebugValue (config, Ids::isDebug, getUndoManager(), getValue (Ids::isDebug)), - configNameValue (config, Ids::name, getUndoManager(), "Build Configuration"), - targetNameValue (config, Ids::targetName, getUndoManager(), project.getProjectFilenameRootString()), - targetBinaryPathValue (config, Ids::binaryPath, getUndoManager()), - recommendedWarningsValue (config, Ids::recommendedWarnings, getUndoManager()), - optimisationLevelValue (config, Ids::optimisation, getUndoManager()), - linkTimeOptimisationValue (config, Ids::linkTimeOptimisation, getUndoManager(), ! isDebug()), - ppDefinesValue (config, Ids::defines, getUndoManager()), - headerSearchPathValue (config, Ids::headerPath, getUndoManager()), - librarySearchPathValue (config, Ids::libraryPath, getUndoManager()), - userNotesValue (config, Ids::userNotes, getUndoManager()) + isDebugValue (config, Ids::isDebug, getUndoManager(), getValue (Ids::isDebug)), + configNameValue (config, Ids::name, getUndoManager(), "Build Configuration"), + targetNameValue (config, Ids::targetName, getUndoManager(), project.getProjectFilenameRootString()), + targetBinaryPathValue (config, Ids::binaryPath, getUndoManager()), + recommendedWarningsValue (config, Ids::recommendedWarnings, getUndoManager()), + optimisationLevelValue (config, Ids::optimisation, getUndoManager()), + linkTimeOptimisationValue (config, Ids::linkTimeOptimisation, getUndoManager(), ! isDebug()), + ppDefinesValue (config, Ids::defines, getUndoManager()), + headerSearchPathValue (config, Ids::headerPath, getUndoManager()), + librarySearchPathValue (config, Ids::libraryPath, getUndoManager()), + userNotesValue (config, Ids::userNotes, getUndoManager()), + usePrecompiledHeaderFileValue (config, Ids::usePrecompiledHeaderFile, getUndoManager(), false), + precompiledHeaderFileValue (config, Ids::precompiledHeaderFile, getUndoManager()) { recommendedCompilerWarningFlags["LLVM"] = { "-Wall", "-Wshadow-all", "-Wshorten-64-to-32", "-Wstrict-aliasing", "-Wuninitialized", "-Wunused-parameter", "-Wconversion", "-Wsign-compare", "-Wint-conversion", "-Wconditional-uninitialized", "-Woverloaded-virtual", @@ -960,6 +962,22 @@ void ProjectExporter::BuildConfiguration::createPropertyEditors (PropertyListBui props.add (new ChoicePropertyComponent (linkTimeOptimisationValue, "Link-Time Optimisation"), "Enable this to perform link-time code optimisation. This is recommended for release builds."); + if (exporter.supportsPrecompiledHeaders()) + { + props.add (new ChoicePropertyComponent (usePrecompiledHeaderFileValue, "Use Precompiled Header"), + "Enable this to turn on precompiled header support for this configuration. Use the setting " + "below to specify the header file to use."); + + auto quotedHeaderFileName = (getPrecompiledHeaderFilename() + ".h").quoted(); + + props.add (new FilePathPropertyComponentWithEnablement (precompiledHeaderFileValue, usePrecompiledHeaderFileValue, + "Precompiled Header File", false, true, "*", project.getProjectFolder()), + "Specify an input header file that will be used to generate a file named " + quotedHeaderFileName + " which is used to generate the " + "PCH file artifact for this exporter configuration. This file can be an absolute path, or relative to the jucer project folder. " + "The " + quotedHeaderFileName + " file will be force included to all source files unless the \"Skip PCH\" setting has been enabled. " + "The generated header will be written on project save and placed in the target folder for this exporter."); + } + createConfigProperties (props); props.add (new TextPropertyComponent (userNotesValue, "Notes", 32768, true), @@ -1010,6 +1028,31 @@ StringArray ProjectExporter::BuildConfiguration::getLibrarySearchPaths() const return s; } +String ProjectExporter::BuildConfiguration::getPrecompiledHeaderFileContent() const +{ + if (shouldUsePrecompiledHeaderFile()) + { + auto f = project.getProjectFolder().getChildFile (precompiledHeaderFileValue.get().toString()); + + if (f.existsAsFile() && f.hasFileExtension (headerFileExtensions)) + { + MemoryOutputStream content; + content.setNewLineString (exporter.getNewLineString()); + + writeAutoGenWarningComment (content); + + content << "*/" << newLine << newLine + << "#ifndef " << getSkipPrecompiledHeaderDefine() << newLine << newLine + << f.loadFileAsString() << newLine + << "#endif" << newLine; + + return content.toString(); + } + } + + return {}; +} + String ProjectExporter::getExternalLibraryFlags (const BuildConfiguration& config) const { auto libraries = StringArray::fromTokens (getExternalLibrariesString(), ";\n", "\"'"); diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h index f24ff59c94..5ba198e588 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h @@ -87,7 +87,10 @@ public: virtual bool isOSX() const = 0; virtual bool isiOS() const = 0; - virtual String getDescription() { return {}; } + virtual String getNewLineString() const = 0; + virtual String getDescription() { return {}; } + + virtual bool supportsPrecompiledHeaders() const { return false; } //============================================================================== // cross-platform audio plug-ins supported by exporter @@ -246,7 +249,12 @@ public: String getLibrarySearchPathString() const { return librarySearchPathValue.get(); } StringArray getLibrarySearchPaths() const; - String getGCCLibraryPathFlags() const; + + String getPrecompiledHeaderFilename() const { return "JucePrecompiledHeader_" + getName(); } + static String getSkipPrecompiledHeaderDefine() { return "JUCE_SKIP_PRECOMPILED_HEADER"; } + + bool shouldUsePrecompiledHeaderFile() const { return usePrecompiledHeaderFileValue.get(); } + String getPrecompiledHeaderFileContent() const; //============================================================================== Value getValue (const Identifier& nm) { return config.getPropertyAsValue (nm, getUndoManager()); } @@ -267,7 +275,8 @@ public: protected: ValueWithDefault isDebugValue, configNameValue, targetNameValue, targetBinaryPathValue, recommendedWarningsValue, optimisationLevelValue, - linkTimeOptimisationValue, ppDefinesValue, headerSearchPathValue, librarySearchPathValue, userNotesValue; + linkTimeOptimisationValue, ppDefinesValue, headerSearchPathValue, librarySearchPathValue, userNotesValue, + usePrecompiledHeaderFileValue, precompiledHeaderFileValue; private: std::map recommendedCompilerWarningFlags; @@ -349,6 +358,16 @@ public: gccOfast = 6 }; + bool isPCHEnabledForAnyConfigurations() const + { + if (supportsPrecompiledHeaders()) + for (ConstConfigIterator config (*this); config.next();) + if (config->shouldUsePrecompiledHeaderFile()) + return true; + + return false; + } + protected: //============================================================================== String name; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp b/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp index 63a409af94..70411b261f 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp @@ -313,14 +313,6 @@ Result ProjectSaver::saveProject (ProjectExporter* specifiedExporterToSave) } //============================================================================== -static void writeAutoGenWarningComment (OutputStream& out) -{ - out << "/*" << newLine << newLine - << " IMPORTANT! This file is auto-generated each time you save your" << newLine - << " project - if you alter its contents, your changes may be overwritten!" << newLine - << newLine; -} - void ProjectSaver::writePluginDefines (MemoryOutputStream& out) const { const auto pluginDefines = getAudioPluginDefines(); @@ -329,6 +321,7 @@ void ProjectSaver::writePluginDefines (MemoryOutputStream& out) const return; writeAutoGenWarningComment (out); + out << "*/" << newLine << newLine << "#pragma once" << newLine << newLine << pluginDefines << newLine; diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp index 3b48ba262c..bc56b55530 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp +++ b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.cpp @@ -248,6 +248,15 @@ bool fileNeedsCppSyntaxHighlighting (const File& file) && String (fileStart).trimStart().startsWith ("// -*- C++ -*-"); } +//============================================================================== +void writeAutoGenWarningComment (OutputStream& outStream) +{ + outStream << "/*" << newLine << newLine + << " IMPORTANT! This file is auto-generated each time you save your" << newLine + << " project - if you alter its contents, your changes may be overwritten!" << newLine + << newLine; +} + //============================================================================== StringArray getJUCEModules() noexcept { diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.h b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.h index 6fe13535a8..81e7ee0d11 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_MiscUtilities.h @@ -54,6 +54,8 @@ void setValueIfVoid (Value value, const var& defaultValue); bool fileNeedsCppSyntaxHighlighting (const File& file); +void writeAutoGenWarningComment (OutputStream& outStream); + StringArray getJUCEModules() noexcept; bool isJUCEModule (const String& moduleID) noexcept; diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h index 578ce1cd07..8a761ea1aa 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h @@ -49,6 +49,8 @@ namespace Ids DECLARE_ID (companyEmail); DECLARE_ID (useAppConfig); DECLARE_ID (addUsingNamespaceToJuceHeader); + DECLARE_ID (usePrecompiledHeaderFile); + DECLARE_ID (precompiledHeaderFile); DECLARE_ID (displaySplashScreen); DECLARE_ID (splashScreenColour); DECLARE_ID (position); @@ -160,6 +162,7 @@ namespace Ids DECLARE_ID (aaxFolder); DECLARE_ID (compile); DECLARE_ID (noWarnings); + DECLARE_ID (skipPCH); DECLARE_ID (resource); DECLARE_ID (xcodeResource); DECLARE_ID (xcodeValidArchs); diff --git a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h b/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h index d0bbbc1a7b..8a0b24ebe2 100644 --- a/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h +++ b/extras/Projucer/Source/Utility/UI/PropertyComponents/jucer_FilePathPropertyComponent.h @@ -34,7 +34,7 @@ */ class FilePathPropertyComponent : public PropertyComponent, public FileDragAndDropTarget, - private Value::Listener + protected Value::Listener { public: FilePathPropertyComponent (Value valueToControl, const String& propertyName, bool isDir, bool thisOS = true, @@ -92,6 +92,12 @@ public: repaint(); } +protected: + void valueChanged (Value&) override + { + updateEditorColour(); + } + private: //============================================================================== void init() @@ -165,11 +171,6 @@ private: } } - void valueChanged (Value&) override - { - updateEditorColour(); - } - void lookAndFeelChanged() override { browseButton.setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId)); @@ -191,3 +192,40 @@ private: //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePathPropertyComponent) }; + +//============================================================================== +class FilePathPropertyComponentWithEnablement : public FilePathPropertyComponent +{ +public: + FilePathPropertyComponentWithEnablement (ValueWithDefault& valueToControl, + ValueWithDefault valueToListenTo, + const String& propertyName, + bool isDir, + bool thisOS = true, + const String& wildcardsToUse = "*", + const File& relativeRoot = File()) + : FilePathPropertyComponent (valueToControl, + propertyName, + isDir, + thisOS, + wildcardsToUse, + relativeRoot), + valueWithDefault (valueToListenTo), + value (valueToListenTo.getPropertyAsValue()) + { + value.addListener (this); + valueChanged (value); + } + + ~FilePathPropertyComponentWithEnablement() override { value.removeListener (this); } + +private: + void valueChanged (Value& v) override + { + FilePathPropertyComponent::valueChanged (v); + setEnabled (valueWithDefault.get()); + } + + ValueWithDefault valueWithDefault; + Value value; +};