From b2e2346745eeb0fcdf2263fd7fec2f318c27f567 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 22 Jan 2019 15:48:15 +0000 Subject: [PATCH] Projucer: Add an option to specify per-file compiler flags --- .../UI/jucer_FileGroupInformationComponent.h | 162 +++++++++++++++- .../Projucer/Source/Project/jucer_Project.cpp | 76 +++++++- .../Projucer/Source/Project/jucer_Project.h | 13 +- .../jucer_ProjectExport_Android.h | 38 +++- .../ProjectSaving/jucer_ProjectExport_CLion.h | 25 ++- .../jucer_ProjectExport_CodeBlocks.h | 15 +- .../ProjectSaving/jucer_ProjectExport_MSVC.h | 11 +- .../ProjectSaving/jucer_ProjectExport_Make.h | 179 +++++++++++------- .../ProjectSaving/jucer_ProjectExport_Xcode.h | 19 +- .../ProjectSaving/jucer_ProjectExporter.cpp | 17 +- .../ProjectSaving/jucer_ProjectExporter.h | 13 +- .../Source/Utility/Helpers/jucer_PresetIDs.h | 2 + 12 files changed, 459 insertions(+), 111 deletions(-) diff --git a/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h b/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h index d799bfb4a6..5678bf44a8 100644 --- a/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h +++ b/extras/Projucer/Source/Project/UI/jucer_FileGroupInformationComponent.h @@ -37,8 +37,8 @@ public: : item (group), header (item.getName(), { getIcons().openFolder, Colours::transparentBlack }) { - list.setHeaderComponent (new ListBoxHeader ( { "File", "Binary Resource", "Xcode Resource", "Compile" }, - { 0.4f, 0.2f, 0.2f, 0.2f } )); + list.setHeaderComponent (new ListBoxHeader ( { "File", "Binary Resource", "Xcode Resource", "Compile", "Compiler Flag Scheme" }, + { 0.3f, 0.15f, 0.15f, 0.15f, 0.25f } )); list.setModel (this); list.setColour (ListBox::backgroundColourId, Colours::transparentBlack); addAndMakeVisible (list); @@ -132,18 +132,23 @@ private: public: FileOptionComponent (const Project::Item& fileItem, ListBoxHeader* listBoxHeader) : item (fileItem), - header (listBoxHeader) + header (listBoxHeader), + compilerFlagSchemeSelector (item) { if (item.isFile()) { addAndMakeVisible (compileButton); compileButton.getToggleStateValue().referTo (item.getShouldCompileValue()); + compileButton.onStateChange = [this] { compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); }; addAndMakeVisible (binaryResourceButton); binaryResourceButton.getToggleStateValue().referTo (item.getShouldAddToBinaryResourcesValue()); addAndMakeVisible (xcodeResourceButton); xcodeResourceButton.getToggleStateValue().referTo (item.getShouldAddToXcodeResourcesValue()); + + addChildComponent (compilerFlagSchemeSelector); + compilerFlagSchemeSelector.setVisible (compileButton.getToggleState()); } } @@ -175,18 +180,163 @@ private: bounds.removeFromLeft (roundToInt (header->getProportionAtIndex (0) * width)); - 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))); + 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))); } } Project::Item item; private: + //============================================================================== + class CompilerFlagSchemeSelector : public Component, + private Value::Listener + { + public: + CompilerFlagSchemeSelector (Project::Item& it) + : item (it) + { + schemeBox.setTextWhenNothingSelected ("None"); + updateCompilerFlagSchemeComboBox(); + schemeBox.onChange = [this] { handleComboBoxSelection(); }; + + addAndMakeVisible (schemeBox); + addChildComponent (newSchemeLabel); + + newSchemeLabel.setEditable (true); + newSchemeLabel.setJustificationType (Justification::centredLeft); + newSchemeLabel.onEditorHide = [this] + { + newSchemeLabel.setVisible (false); + schemeBox.setVisible (true); + + auto newScheme = newSchemeLabel.getText(); + + item.project.addCompilerFlagScheme (newScheme); + + if (item.getCompilerFlagSchemeString().isEmpty()) + item.setCompilerFlagScheme (newScheme); + + updateCompilerFlagSchemeComboBox(); + }; + + selectScheme (item.getCompilerFlagSchemeString()); + + projectCompilerFlagSchemesValue = item.project.getProjectValue (Ids::compilerFlagSchemes); + projectCompilerFlagSchemesValue.addListener (this); + + lookAndFeelChanged(); + } + + void resized() override + { + auto b = getLocalBounds(); + + schemeBox.setBounds (b); + newSchemeLabel.setBounds (b); + } + + private: + void valueChanged (Value&) override { updateCompilerFlagSchemeComboBox(); } + + void lookAndFeelChanged() override + { + schemeBox.setColour (ComboBox::outlineColourId, Colours::transparentBlack); + schemeBox.setColour (ComboBox::textColourId, findColour (defaultTextColourId)); + } + + void updateCompilerFlagSchemeComboBox() + { + auto itemScheme = item.getCompilerFlagSchemeString(); + auto allSchemes = item.project.getCompilerFlagSchemes(); + + if (itemScheme.isNotEmpty() && ! allSchemes.contains (itemScheme)) + { + item.clearCurrentCompilerFlagScheme(); + itemScheme = {}; + } + + schemeBox.clear(); + + schemeBox.addItemList (allSchemes, 1); + schemeBox.addSeparator(); + schemeBox.addItem ("Add a new scheme...", -1); + schemeBox.addItem ("Delete selected scheme", -2); + schemeBox.addItem ("Clear", -3); + + selectScheme (itemScheme); + } + + void handleComboBoxSelection() + { + auto selectedID = schemeBox.getSelectedId(); + + if (selectedID > 0) + { + item.setCompilerFlagScheme (schemeBox.getItemText (selectedID - 1)); + } + else if (selectedID == -1) + { + newSchemeLabel.setText ("NewScheme", dontSendNotification); + + schemeBox.setVisible (false); + newSchemeLabel.setVisible (true); + + newSchemeLabel.showEditor(); + + if (auto* ed = newSchemeLabel.getCurrentTextEditor()) + ed->setInputRestrictions (64, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"); + } + else if (selectedID == -2) + { + auto currentScheme = item.getCompilerFlagSchemeString(); + + if (currentScheme.isNotEmpty()) + { + item.project.removeCompilerFlagScheme (currentScheme); + item.clearCurrentCompilerFlagScheme(); + } + + updateCompilerFlagSchemeComboBox(); + } + else if (selectedID == -3) + { + schemeBox.setSelectedId (0); + item.clearCurrentCompilerFlagScheme(); + } + } + + void selectScheme (const String& schemeToSelect) + { + if (schemeToSelect.isNotEmpty()) + { + for (int i = 0; i < schemeBox.getNumItems(); ++i) + { + if (schemeBox.getItemText (i) == schemeToSelect) + { + schemeBox.setSelectedItemIndex (i); + return; + } + } + } + + schemeBox.setSelectedId (0); + } + + Project::Item& item; + Value projectCompilerFlagSchemesValue; + + ComboBox schemeBox; + Label newSchemeLabel; + }; + + //============================================================================== ListBoxHeader* header; ToggleButton compileButton, binaryResourceButton, xcodeResourceButton; + CompilerFlagSchemeSelector compilerFlagSchemeSelector; }; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FileGroupInformationComponent) diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 8df7744d23..0c9ab440a8 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -225,13 +225,13 @@ void Project::initialiseProjectValues() reportAppUsageValue.setDefault (true); } - cppStandardValue.referTo (projectRoot, Ids::cppLanguageStandard, getUndoManager(), "14"); + cppStandardValue.referTo (projectRoot, Ids::cppLanguageStandard, getUndoManager(), "14"); - headerSearchPathsValue.referTo (projectRoot, Ids::headerPath, getUndoManager()); - preprocessorDefsValue.referTo (projectRoot, Ids::defines, getUndoManager()); - userNotesValue.referTo (projectRoot, Ids::userNotes, getUndoManager()); + headerSearchPathsValue.referTo (projectRoot, Ids::headerPath, getUndoManager()); + preprocessorDefsValue.referTo (projectRoot, Ids::defines, getUndoManager()); + userNotesValue.referTo (projectRoot, Ids::userNotes, getUndoManager()); - maxBinaryFileSizeValue.referTo (projectRoot, Ids::maxBinaryFileSize, getUndoManager(), 10240 * 1024); + maxBinaryFileSizeValue.referTo (projectRoot, Ids::maxBinaryFileSize, getUndoManager(), 10240 * 1024); // this is here for backwards compatibility with old projects using the incorrect id if (projectRoot.hasProperty ("includeBinaryInAppConfig")) @@ -239,7 +239,9 @@ void Project::initialiseProjectValues() else includeBinaryDataInJuceHeaderValue.referTo (projectRoot, Ids::includeBinaryInJuceHeader, getUndoManager(), true); - binaryDataNamespaceValue.referTo (projectRoot, Ids::binaryDataNamespace, getUndoManager(), "BinaryData"); + binaryDataNamespaceValue.referTo (projectRoot, Ids::binaryDataNamespace, getUndoManager(), "BinaryData"); + + compilerFlagSchemesValue.referTo (projectRoot, Ids::compilerFlagSchemes, getUndoManager(), Array(), ","); } void Project::initialiseAudioPluginValues() @@ -1273,6 +1275,19 @@ bool Project::Item::shouldInhibitWarnings() const { return state [Ids: bool Project::Item::isModuleCode() const { return belongsToModule; } +Value Project::Item::getCompilerFlagSchemeValue() { return state.getPropertyAsValue (Ids::compilerFlagScheme, getUndoManager()); } +String Project::Item::getCompilerFlagSchemeString() const { return state [Ids::compilerFlagScheme]; } + +void Project::Item::setCompilerFlagScheme (const String& scheme) +{ + state.getPropertyAsValue (Ids::compilerFlagScheme, getUndoManager()).setValue (scheme); +} + +void Project::Item::clearCurrentCompilerFlagScheme() +{ + state.removeProperty (Ids::compilerFlagScheme, getUndoManager()); +} + String Project::Item::getFilePath() const { if (isFile()) @@ -1641,6 +1656,55 @@ bool Project::isConfigFlagEnabled (const String& name, bool defaultIsEnabled) co return configValue; } +//============================================================================== +StringArray Project::getCompilerFlagSchemes() const +{ + if (compilerFlagSchemesValue.isUsingDefault()) + return {}; + + StringArray schemes; + auto schemesVar = compilerFlagSchemesValue.get(); + + if (auto* arr = schemesVar.getArray()) + schemes.addArray (arr->begin(), arr->end()); + + return schemes; +} + +void Project::addCompilerFlagScheme (const String& schemeToAdd) +{ + auto schemesVar = compilerFlagSchemesValue.get(); + + if (auto* arr = schemesVar.getArray()) + { + arr->addIfNotAlreadyThere (schemeToAdd); + compilerFlagSchemesValue.setValue ({ *arr }, getUndoManager()); + } +} + +void Project::removeCompilerFlagScheme (const String& schemeToRemove) +{ + auto schemesVar = compilerFlagSchemesValue.get(); + + if (auto* arr = schemesVar.getArray()) + { + for (int i = 0; i < arr->size(); ++i) + { + if (arr->getUnchecked (i).toString() == schemeToRemove) + { + arr->remove (i); + + if (arr->isEmpty()) + compilerFlagSchemesValue.resetToDefault(); + else + compilerFlagSchemesValue.setValue ({ *arr }, getUndoManager()); + + return; + } + } + } +} + //============================================================================== static String getCompanyNameOrDefault (StringRef str) { diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index 92974b1596..887eed09a0 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -129,6 +129,10 @@ public: String getCppStandardString() const { return cppStandardValue.get(); } + StringArray getCompilerFlagSchemes() const; + void addCompilerFlagScheme (const String&); + void removeCompilerFlagScheme (const String&); + //============================================================================== String getPluginNameString() const { return pluginNameValue.get(); } String getPluginDescriptionString() const { return pluginDescriptionValue.get();} @@ -280,6 +284,12 @@ public: bool isModuleCode() const; + Value getCompilerFlagSchemeValue(); + String getCompilerFlagSchemeString() const; + + void setCompilerFlagScheme (const String&); + void clearCurrentCompilerFlagScheme(); + //============================================================================== bool canContain (const Item& child) const; int getNumChildren() const { return state.getNumChildren(); } @@ -417,7 +427,8 @@ private: ValueWithDefault pluginFormatsValue, pluginNameValue, pluginDescriptionValue, pluginManufacturerValue, pluginManufacturerCodeValue, pluginCodeValue, pluginChannelConfigsValue, pluginCharacteristicsValue, pluginAUExportPrefixValue, pluginAAXIdentifierValue, - pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginRTASCategoryValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue; + pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginRTASCategoryValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue, + compilerFlagSchemesValue; //============================================================================== std::unique_ptr compileEngineSettings; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h index a5d2b13f40..59595865cd 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h @@ -501,12 +501,13 @@ private: } Array excludeFromBuild; + Array> extraCompilerFlags; mo << "add_library( ${BINARY_NAME}" << newLine; mo << newLine; mo << " " << (getProject().getProjectType().isStaticLibrary() ? "STATIC" : "SHARED") << newLine; mo << newLine; - addCompileUnits (mo, excludeFromBuild); + addCompileUnits (mo, excludeFromBuild, extraCompilerFlags); mo << ")" << newLine << newLine; if (excludeFromBuild.size() > 0) @@ -517,6 +518,14 @@ private: mo << newLine; } + if (! extraCompilerFlags.isEmpty()) + { + for (auto& extra : extraCompilerFlags) + mo << "set_source_files_properties(\"" << extra.first.toUnixStyle() << "\" PROPERTIES COMPILE_FLAGS " << extra.second << " )" << newLine; + + mo << newLine; + } + auto libraries = getAndroidLibraries(); if (libraries.size() > 0) { @@ -1258,32 +1267,45 @@ private: } //============================================================================== - void addCompileUnits (const Project::Item& projectItem, MemoryOutputStream& mo, Array& excludeFromBuild) const + void addCompileUnits (const Project::Item& projectItem, MemoryOutputStream& mo, + Array& excludeFromBuild, Array>& extraCompilerFlags) const { if (projectItem.isGroup()) { for (int i = 0; i < projectItem.getNumChildren(); ++i) - addCompileUnits (projectItem.getChild(i), mo, excludeFromBuild); + addCompileUnits (projectItem.getChild(i), mo, excludeFromBuild, extraCompilerFlags); } else if (projectItem.shouldBeAddedToTargetProject()) { - RelativePath file (projectItem.getFile(), getTargetFolder().getChildFile ("app"), RelativePath::buildTargetFolder); - auto targetType = getProject().getTargetTypeFromFilePath (projectItem.getFile(), true); + auto f = projectItem.getFile(); + RelativePath file (f, getTargetFolder().getChildFile ("app"), RelativePath::buildTargetFolder); + + auto targetType = getProject().getTargetTypeFromFilePath (f, true); mo << " \"" << file.toUnixStyle() << "\"" << newLine; - if ((! projectItem.shouldBeCompiled()) || (! shouldFileBeCompiledByDefault (file)) + if ((! projectItem.shouldBeCompiled()) || (! shouldFileBeCompiledByDefault (f)) || (getProject().getProjectType().isAudioPlugin() && targetType != ProjectType::Target::SharedCodeTarget && targetType != ProjectType::Target::StandalonePlugIn)) + { excludeFromBuild.add (file); + } + else + { + auto extraFlags = compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString(); + + if (extraFlags.isNotEmpty()) + extraCompilerFlags.add ({ file, extraFlags }); + } } } - void addCompileUnits (MemoryOutputStream& mo, Array& excludeFromBuild) const + void addCompileUnits (MemoryOutputStream& mo, Array& excludeFromBuild, + Array>& extraCompilerFlags) const { for (int i = 0; i < getAllGroups().size(); ++i) - addCompileUnits (getAllGroups().getReference(i), mo, excludeFromBuild); + addCompileUnits (getAllGroups().getReference(i), mo, excludeFromBuild, extraCompilerFlags); } //============================================================================== diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h index b567b291d3..88d6ec4a79 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h @@ -302,7 +302,7 @@ private: } template - void getFileInfoList (Target& target, Exporter& exporter, const Project::Item& projectItem, std::vector>& fileInfoList) const + void getFileInfoList (Target& target, Exporter& exporter, const Project::Item& projectItem, std::vector>& fileInfoList) const { auto targetType = (getProject().getProjectType().isAudioPlugin() ? target.type : Target::Type::SharedCodeTarget); @@ -314,7 +314,9 @@ private: else if (projectItem.shouldBeAddedToTargetProject() && getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType ) { auto path = RelativePath (projectItem.getFile(), exporter.getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle(); - fileInfoList.push_back ({ path, projectItem.shouldBeCompiled() }); + + fileInfoList.push_back (std::make_tuple (path, projectItem.shouldBeCompiled(), + exporter.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString())); } } @@ -364,12 +366,12 @@ private: out << newLine; - std::vector> fileInfoList; + std::vector> fileInfoList; for (auto& group : exporter.getAllGroups()) getFileInfoList (*target, exporter, group, fileInfoList); for (auto& fileInfo : fileInfoList) - out << " " << fileInfo.first.quoted() << newLine; + out << " " << std::get<0> (fileInfo).quoted() << newLine; auto isCMakeBundle = exporter.isXcode() && target->getTargetFileType() == ProjectType::Target::TargetFileType::pluginBundle; auto pkgInfoPath = String ("PkgInfo").quoted(); @@ -414,8 +416,19 @@ private: out << "set_source_files_properties (" << xcodeIcnsFilePath << " PROPERTIES MACOSX_PACKAGE_LOCATION \"Resources\")" << newLine; for (auto& fileInfo : fileInfoList) - if (! fileInfo.second) - out << "set_source_files_properties (" << fileInfo.first.quoted() << " PROPERTIES HEADER_FILE_ONLY TRUE)" << newLine; + { + if (std::get<1> (fileInfo)) + { + auto extraCompilerFlags = std::get<2> (fileInfo); + + if (extraCompilerFlags.isNotEmpty()) + out << "set_source_files_properties(" << std::get<0> (fileInfo).quoted() << " PROPERTIES COMPILE_FLAGS " << extraCompilerFlags << " )" << newLine; + } + else + { + out << "set_source_files_properties (" << std::get<0> (fileInfo).quoted() << " PROPERTIES HEADER_FILE_ONLY TRUE)" << newLine; + } + } out << newLine; } diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h index 17031263f7..46f23b3e68 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h @@ -787,7 +787,20 @@ private: unit->createNewChildElement ("Option")->setAttribute ("target", targetName); } - if (! projectItem.shouldBeCompiled()) + if (projectItem.shouldBeCompiled()) + { + auto extraCompilerFlags = compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString(); + + if (extraCompilerFlags.isNotEmpty()) + { + auto* optionElement = unit->createNewChildElement ("Option"); + + optionElement->setAttribute ("compiler", "gcc"); + optionElement->setAttribute ("use", 1); + optionElement->setAttribute ("buildCommand", "$compiler $options " + extraCompilerFlags + " $includes -c $file -o $object"); + } + } + else { unit->createNewChildElement ("Option")->setAttribute ("compile", 0); unit->createNewChildElement ("Option")->setAttribute ("link", 0); diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h index b57e7ef687..df3ea3ae96 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h @@ -767,8 +767,17 @@ public: if (shouldUseStdCall (path)) e->createNewChildElement ("CallingConvention")->addTextElement ("StdCall"); - if (! projectItem.shouldBeCompiled()) + if (projectItem.shouldBeCompiled()) + { + auto extraCompilerFlags = owner.compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString(); + + if (extraCompilerFlags.isNotEmpty()) + e->createNewChildElement ("AdditionalOptions")->addTextElement (extraCompilerFlags + " %(AdditionalOptions)"); + } + else + { e->createNewChildElement ("ExcludedFromBuild")->addTextElement ("true"); + } } } else if (path.hasFileExtension (headerFileExtensions)) diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h index 4f5a669f90..d477718933 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h @@ -194,63 +194,31 @@ public: return String (getName()).toUpperCase().replaceCharacter (L' ', L'_'); } - void writeObjects (OutputStream& out) const + void writeObjects (OutputStream& out, const Array>& filesToCompile) const { - Array targetFiles; - for (int i = 0; i < owner.getAllGroups().size(); ++i) - findAllFilesToCompile (owner.getAllGroups().getReference(i), targetFiles); - out << "OBJECTS_" + getTargetVarName() + String (" := \\") << newLine; - for (int i = 0; i < targetFiles.size(); ++i) - out << " $(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor (targetFiles.getReference(i))) << " \\" << newLine; + for (auto& f : filesToCompile) + out << " $(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor ({ f.first, owner.getTargetFolder(), RelativePath::buildTargetFolder })) << " \\" << newLine; out << newLine; } - void findAllFilesToCompile (const Project::Item& projectItem, Array& results) const + void addFiles (OutputStream& out, const Array>& filesToCompile) { - if (projectItem.isGroup()) - { - for (int i = 0; i < projectItem.getNumChildren(); ++i) - findAllFilesToCompile (projectItem.getChild(i), results); - } - else - { - if (projectItem.shouldBeCompiled()) - { - auto targetType = (owner.getProject().getProjectType().isAudioPlugin() ? type : SharedCodeTarget); - auto f = projectItem.getFile(); - RelativePath relativePath (f, owner.getTargetFolder(), RelativePath::buildTargetFolder); - - if (owner.shouldFileBeCompiledByDefault (relativePath) - && owner.getProject().getTargetTypeFromFilePath (f, true) == targetType) - results.add (relativePath); - } - } - } - - void addFiles (OutputStream& out) - { - Array targetFiles; - for (int i = 0; i < owner.getAllGroups().size(); ++i) - findAllFilesToCompile (owner.getAllGroups().getReference(i), targetFiles); - auto cppflagsVarName = "JUCE_CPPFLAGS_" + getTargetVarName(); auto cflagsVarName = "JUCE_CFLAGS_" + getTargetVarName(); - for (int i = 0; i < targetFiles.size(); ++i) + for (auto& f : filesToCompile) { - jassert (targetFiles.getReference(i).getRoot() == RelativePath::buildTargetFolder); + RelativePath relativePath (f.first, owner.getTargetFolder(), RelativePath::buildTargetFolder); - out << "$(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor (targetFiles.getReference(i))) - << ": " << escapeSpaces (targetFiles.getReference(i).toUnixStyle()) << newLine - << "\t-$(V_AT)mkdir -p $(JUCE_OBJDIR)" << newLine - << "\t@echo \"Compiling " << targetFiles.getReference(i).getFileName() << "\"" << newLine - << (targetFiles.getReference(i).hasFileExtension ("c;s;S") ? "\t$(V_AT)$(CC) $(JUCE_CFLAGS) " - : "\t$(V_AT)$(CXX) $(JUCE_CXXFLAGS) ") - << "$(" << cppflagsVarName << ") $(" << cflagsVarName << ") -o \"$@\" -c \"$<\"" - << newLine + out << "$(JUCE_OBJDIR)/" << escapeSpaces (owner.getObjectFileFor (relativePath)) << ": " << escapeSpaces (relativePath.toUnixStyle()) << newLine + << "\t-$(V_AT)mkdir -p $(JUCE_OBJDIR)" << newLine + << "\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 << newLine; } } @@ -736,7 +704,7 @@ private: } out << "all : " << dependencies.joinIntoString (" ") << newLine << newLine; - out << subTargetLines.toString() << newLine << newLine; + out << subTargetLines.toString() << newLine << newLine; } else { @@ -761,15 +729,15 @@ private: outputDir = binaryPath.rebased (projectFolder, getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle(); } - out << "ifeq ($(CONFIG)," << escapeSpaces (config.getName()) << ")" << newLine; - out << " JUCE_BINDIR := " << escapeSpaces (buildDirName) << newLine - << " JUCE_LIBDIR := " << escapeSpaces (buildDirName) << newLine - << " JUCE_OBJDIR := " << escapeSpaces (intermediatesDirName) << newLine - << " JUCE_OUTDIR := " << escapeSpaces (outputDir) << newLine + out << "ifeq ($(CONFIG)," << escapeSpaces (config.getName()) << ")" << newLine + << " JUCE_BINDIR := " << escapeSpaces (buildDirName) << newLine + << " JUCE_LIBDIR := " << escapeSpaces (buildDirName) << newLine + << " JUCE_OBJDIR := " << escapeSpaces (intermediatesDirName) << newLine + << " JUCE_OUTDIR := " << escapeSpaces (outputDir) << newLine << newLine - << " ifeq ($(TARGET_ARCH),)" << newLine - << " TARGET_ARCH := " << getArchFlags (config) << newLine - << " endif" << newLine + << " ifeq ($(TARGET_ARCH),)" << newLine + << " TARGET_ARCH := " << getArchFlags (config) << newLine + << " endif" << newLine << newLine; writeCppFlags (out, config); @@ -828,37 +796,84 @@ private: } } + static String getCompilerFlagSchemeVariableName (const String& schemeName) { return "JUCE_COMPILERFLAGSCHEME_" + schemeName; } + + void findAllFilesToCompile (const Project::Item& projectItem, Array>& results) const + { + if (projectItem.isGroup()) + { + for (int i = 0; i < projectItem.getNumChildren(); ++i) + findAllFilesToCompile (projectItem.getChild (i), results); + } + else + { + if (projectItem.shouldBeCompiled()) + { + auto f = projectItem.getFile(); + + if (shouldFileBeCompiledByDefault (f)) + { + auto scheme = projectItem.getCompilerFlagSchemeString(); + auto flags = compilerFlagSchemesMap[scheme].get().toString(); + + if (scheme.isNotEmpty() && flags.isNotEmpty()) + results.add ({ f, scheme }); + else + results.add ({ f, {} }); + } + } + } + } + + void writeCompilerFlagSchemes (OutputStream& out, const Array>& filesToCompile) const + { + StringArray schemesToWrite; + + for (auto& f : filesToCompile) + if (f.second.isNotEmpty()) + schemesToWrite.addIfNotAlreadyThere (f.second); + + if (! schemesToWrite.isEmpty()) + { + for (auto& s : schemesToWrite) + out << getCompilerFlagSchemeVariableName (s) << " := " + << compilerFlagSchemesMap[s].get().toString() << newLine; + + out << newLine; + } + } + void writeMakefile (OutputStream& out) const { - out << "# Automatically generated makefile, created by the Projucer" << newLine + out << "# 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 << newLine; out << "# build with \"V=1\" for verbose builds" << newLine - << "ifeq ($(V), 1)" << newLine - << "V_AT =" << newLine - << "else" << newLine - << "V_AT = @" << newLine - << "endif" << newLine + << "ifeq ($(V), 1)" << newLine + << "V_AT =" << newLine + << "else" << newLine + << "V_AT = @" << newLine + << "endif" << newLine << newLine; out << "# (this disables dependency generation if multiple architectures are set)" << newLine - << "DEPFLAGS := $(if $(word 2, $(TARGET_ARCH)), , -MMD)" << newLine + << "DEPFLAGS := $(if $(word 2, $(TARGET_ARCH)), , -MMD)" << newLine << newLine; - out << "ifndef STRIP" << newLine + out << "ifndef STRIP" << newLine << " STRIP=strip" << newLine - << "endif" << newLine + << "endif" << newLine << newLine; out << "ifndef AR" << newLine - << " AR=ar" << newLine - << "endif" << newLine + << " AR=ar" << newLine + << "endif" << newLine << newLine; - out << "ifndef CONFIG" << newLine + out << "ifndef CONFIG" << newLine << " CONFIG=" << escapeSpaces (getConfiguration(0)->getName()) << newLine - << "endif" << newLine + << "endif" << newLine << newLine; out << "JUCE_ARCH_LABEL := $(shell uname -m)" << newLine @@ -867,21 +882,43 @@ private: for (ConstConfigIterator config (*this); config.next();) writeConfig (out, dynamic_cast (*config)); + Array> filesToCompile; + + for (int i = 0; i < getAllGroups().size(); ++i) + findAllFilesToCompile (getAllGroups().getReference (i), filesToCompile); + + writeCompilerFlagSchemes (out, filesToCompile); + + auto getFilesForTarget = [] (const Array>& files, MakefileTarget* target, const Project& p) -> Array> + { + Array> targetFiles; + + auto targetType = (p.getProjectType().isAudioPlugin() ? target->type : MakefileTarget::SharedCodeTarget); + + for (auto& f : files) + if (p.getTargetTypeFromFilePath (f.first, true) == targetType) + targetFiles.add (f); + + return targetFiles; + }; + for (auto target : targets) - target->writeObjects (out); + target->writeObjects (out, getFilesForTarget (filesToCompile, target, project)); out << getPhonyTargetLine() << newLine << newLine; writeTargetLines (out, getPackages()); for (auto target : targets) - target->addFiles (out);out << "clean:" << newLine + target->addFiles (out, getFilesForTarget (filesToCompile, target, project)); + + out << "clean:" << newLine << "\t@echo Cleaning " << projectName << newLine - << "\t$(V_AT)$(CLEANCMD)" << newLine + << "\t$(V_AT)$(CLEANCMD)" << newLine << newLine; - out << "strip:" << newLine - << "\t@echo Stripping " << projectName << newLine + out << "strip:" << newLine + << "\t@echo Stripping " << projectName << newLine << "\t-$(V_AT)$(STRIP) --strip-unneeded $(JUCE_OUTDIR)/$(TARGET)" << newLine << newLine; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 25c77bb1c6..ccb98c5a99 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -2720,7 +2720,8 @@ private: output << "\t};\n\trootObject = " << createID ("__root") << ";\n}\n"; } - String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, XcodeTarget* xcodeTarget = nullptr) const + String addBuildFile (const String& path, const String& fileRefID, bool addToSourceBuildPhase, bool inhibitWarnings, + XcodeTarget* xcodeTarget = nullptr, String compilerFlags = {}) const { auto fileID = createID (path + "buildref"); @@ -2737,7 +2738,10 @@ private: v->setProperty ("fileRef", fileRefID, nullptr); if (inhibitWarnings) - v->setProperty ("settings", "{ COMPILER_FLAGS = \"-w\"; }", nullptr); + compilerFlags += " -w"; + + if (compilerFlags.isNotEmpty()) + v->setProperty ("settings", "{ COMPILER_FLAGS = \"" + compilerFlags.trim() + "\"; }", nullptr); pbxBuildFiles.add (v); return fileID; @@ -2865,14 +2869,14 @@ private: } String addFile (const RelativePath& path, bool shouldBeCompiled, bool shouldBeAddedToBinaryResources, - bool shouldBeAddedToXcodeResources, bool inhibitWarnings, XcodeTarget* xcodeTarget) const + bool shouldBeAddedToXcodeResources, bool inhibitWarnings, XcodeTarget* xcodeTarget, const String& compilerFlags) const { auto pathAsString = path.toUnixStyle(); auto refID = addFileReference (path.toUnixStyle()); if (shouldBeCompiled) { - addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget); + addBuildFile (pathAsString, refID, true, inhibitWarnings, xcodeTarget, compilerFlags); } else if (! shouldBeAddedToBinaryResources || shouldBeAddedToXcodeResources) { @@ -2991,7 +2995,7 @@ private: overwriteFileIfDifferentOrThrow (entitlementsFile, content); RelativePath plistPath (entitlementsFile, getTargetFolder(), RelativePath::buildTargetFolder); - return addFile (plistPath, false, false, false, false, nullptr); + return addFile (plistPath, false, false, false, false, nullptr, {}); } String addProjectItem (const Project::Item& projectItem) const @@ -3039,7 +3043,8 @@ private: projectItem.shouldBeAddedToBinaryResources(), projectItem.shouldBeAddedToXcodeResources(), projectItem.shouldInhibitWarnings(), - xcodeTarget); + xcodeTarget, + compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get()); } return {}; @@ -3508,7 +3513,7 @@ private: String createFileRefID (const String& path) const { return createID ("__fileref_" + path); } String getIDForGroup (const Project::Item& item) const { return createID (item.getID()); } - bool shouldFileBeCompiledByDefault (const RelativePath& file) const override + bool shouldFileBeCompiledByDefault (const File& file) const override { return file.hasFileExtension (sourceFileExtensions); } diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp index 4234659965..f662f48486 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp @@ -257,6 +257,9 @@ ProjectExporter::ProjectExporter (Project& p, const ValueTree& state) smallIconValue (settings, Ids::smallIcon, getUndoManager()), extraPPDefsValue (settings, Ids::extraDefs, getUndoManager()) { + projectCompilerFlagSchemesValue = project.getProjectValue (Ids::compilerFlagSchemes); + projectCompilerFlagSchemesValue.addListener (this); + updateCompilerFlagValues(); } ProjectExporter::~ProjectExporter() @@ -283,12 +286,20 @@ RelativePath ProjectExporter::rebaseFromProjectFolderToBuildTarget (const Relati return path.rebased (project.getProjectFolder(), getTargetFolder(), RelativePath::buildTargetFolder); } -bool ProjectExporter::shouldFileBeCompiledByDefault (const RelativePath& file) const +bool ProjectExporter::shouldFileBeCompiledByDefault (const File& file) const { return file.hasFileExtension (cOrCppFileExtensions) || file.hasFileExtension (asmFileExtensions); } +void ProjectExporter::updateCompilerFlagValues() +{ + compilerFlagSchemesMap.clear(); + + for (auto& scheme : project.getCompilerFlagSchemes()) + compilerFlagSchemesMap.set (scheme, { settings, scheme, getUndoManager() }); +} + //============================================================================== void ProjectExporter::createPropertyEditors (PropertyListBuilder& props) { @@ -336,6 +347,10 @@ void ProjectExporter::createPropertyEditors (PropertyListBuilder& props) "Extra command-line flags to be passed to the compiler. This string can contain references to preprocessor definitions in the " "form ${NAME_OF_DEFINITION}, which will be replaced with their values."); + for (HashMap::Iterator i (compilerFlagSchemesMap); i.next();) + props.add (new TextPropertyComponent (compilerFlagSchemesMap.getReference (i.getKey()), "Compiler Flags for " + i.getKey().quoted(), 8192, false), + "The exporter-specific compiler flags that will be added to files using this scheme."); + props.add (new TextPropertyComponent (extraLinkerFlagsValue, "Extra Linker Flags", 8192, true), "Extra command-line flags to be passed to the linker. You might want to use this for adding additional libraries. " "This string can contain references to preprocessor definitions in the form ${NAME_OF_VALUE}, which will be replaced with their values."); diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h index 9961f15af9..2fa735a2d9 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h @@ -32,11 +32,11 @@ class ProjectSaver; //============================================================================== -class ProjectExporter +class ProjectExporter : private Value::Listener { public: ProjectExporter (Project&, const ValueTree& settings); - virtual ~ProjectExporter(); + virtual ~ProjectExporter() override; struct ExporterTypeInfo { @@ -78,7 +78,7 @@ public: virtual bool canLaunchProject() = 0; virtual bool launchProject() = 0; virtual void create (const OwnedArray&) const = 0; // may throw a SaveError - virtual bool shouldFileBeCompiledByDefault (const RelativePath& path) const; + virtual bool shouldFileBeCompiledByDefault (const File& path) const; virtual bool canCopeWithDuplicateFiles() = 0; virtual bool supportsUserDefinedConfigurations() const = 0; // false if exporter only supports two configs Debug and Release virtual void updateDeprecatedProjectSettingsInteractively(); @@ -406,6 +406,9 @@ protected: ValueWithDefault targetLocationValue, extraCompilerFlagsValue, extraLinkerFlagsValue, externalLibrariesValue, userNotesValue, gnuExtensionsValue, bigIconValue, smallIconValue, extraPPDefsValue; + Value projectCompilerFlagSchemesValue; + HashMap compilerFlagSchemesMap; + mutable Array itemGroups; void initItemGroups() const; Project::Item* modulesGroup = nullptr; @@ -454,6 +457,10 @@ protected: static Image rescaleImageForIcon (Drawable&, int iconSize); private: + //============================================================================== + void valueChanged (Value&) override { updateCompilerFlagValues(); } + void updateCompilerFlagValues(); + //============================================================================== static String addLibPrefix (const String name) { diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h index d9cb0a3580..f126efbded 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h @@ -345,6 +345,8 @@ namespace Ids DECLARE_ID (continuousRebuildEnabled); DECLARE_ID (warningsEnabled); DECLARE_ID (projectLineFeed); + DECLARE_ID (compilerFlagSchemes); + DECLARE_ID (compilerFlagScheme); const Identifier ID ("id"); const Identifier ID_uppercase ("ID");