| @@ -78,7 +78,8 @@ namespace build_tools | |||
| case Type::StaticLibrary: | |||
| case Type::DynamicLibrary: | |||
| case Type::LV2PlugIn: | |||
| case Type::LV2TurtleProgram: | |||
| case Type::LV2Helper: | |||
| case Type::VST3Helper: | |||
| case Type::SharedCodeTarget: | |||
| case Type::AggregateTarget: | |||
| case Type::unspecified: | |||
| @@ -109,7 +110,8 @@ namespace build_tools | |||
| case Type::StaticLibrary: | |||
| case Type::DynamicLibrary: | |||
| case Type::LV2PlugIn: | |||
| case Type::LV2TurtleProgram: | |||
| case Type::LV2Helper: | |||
| case Type::VST3Helper: | |||
| case Type::SharedCodeTarget: | |||
| case Type::AggregateTarget: | |||
| case Type::unspecified: | |||
| @@ -82,7 +82,8 @@ namespace build_tools | |||
| SharedCodeTarget = 20, // internal | |||
| AggregateTarget = 21, | |||
| LV2TurtleProgram = 25, // internal | |||
| LV2Helper = 25, // internal | |||
| VST3Helper = 26, // internal | |||
| unspecified = 30 | |||
| }; | |||
| @@ -118,7 +119,8 @@ namespace build_tools | |||
| case LV2PlugIn: return "LV2 Plugin"; | |||
| case SharedCodeTarget: return "Shared Code"; | |||
| case AggregateTarget: return "All"; | |||
| case LV2TurtleProgram: return "LV2 Manifest Helper"; | |||
| case LV2Helper: return "LV2 Manifest Helper"; | |||
| case VST3Helper: return "VST3 Manifest Helper"; | |||
| case unspecified: break; | |||
| } | |||
| @@ -141,7 +143,8 @@ namespace build_tools | |||
| if (name == "LV2 Plugin") return Type::LV2PlugIn; | |||
| if (name == "Shared Code") return Type::SharedCodeTarget; | |||
| if (name == "All") return Type::AggregateTarget; | |||
| if (name == "LV2 Manifest Helper") return Type::LV2TurtleProgram; | |||
| if (name == "LV2 Manifest Helper") return Type::LV2Helper; | |||
| if (name == "VST3 Manifest Helper") return Type::VST3Helper; | |||
| jassertfalse; | |||
| return Type::ConsoleApp; | |||
| @@ -164,7 +167,8 @@ namespace build_tools | |||
| case UnityPlugIn: return pluginBundle; | |||
| case LV2PlugIn: return pluginBundle; | |||
| case SharedCodeTarget: return staticLibrary; | |||
| case LV2TurtleProgram: return executable; | |||
| case LV2Helper: return executable; | |||
| case VST3Helper: return executable; | |||
| case AggregateTarget: | |||
| case unspecified: | |||
| break; | |||
| @@ -249,7 +253,8 @@ namespace build_tools | |||
| case Target::StandalonePlugIn: | |||
| case Target::UnityPlugIn: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| case Target::VST3Helper: | |||
| case Target::SharedCodeTarget: | |||
| case Target::AggregateTarget: | |||
| return true; | |||
| @@ -295,7 +300,8 @@ namespace build_tools | |||
| case Target::DynamicLibrary: | |||
| case Target::unspecified: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| case Target::VST3Helper: | |||
| break; | |||
| } | |||
| @@ -369,6 +369,8 @@ void Project::initialiseAudioPluginValues() | |||
| pluginVSTNumMidiOutputsValue.referTo (projectRoot, Ids::pluginVSTNumMidiOutputs, getUndoManager(), 16); | |||
| pluginLV2URIValue.referTo (projectRoot, Ids::lv2Uri, getUndoManager(), getDefaultLV2URI()); | |||
| vst3ManifestEnabledValue.referTo (projectRoot, Ids::vst3ManifestEnabled, getUndoManager(), false); | |||
| } | |||
| void Project::updateOldStyleConfigList() | |||
| @@ -1262,6 +1264,8 @@ bool Project::shouldBuildTargetType (build_tools::ProjectType::Target::Type targ | |||
| return shouldBuildVST(); | |||
| case Target::VST3PlugIn: | |||
| return shouldBuildVST3(); | |||
| case Target::VST3Helper: | |||
| return shouldBuildVST3() && isVst3ManifestEnabled(); | |||
| case Target::AAXPlugIn: | |||
| return shouldBuildAAX(); | |||
| case Target::AudioUnitPlugIn: | |||
| @@ -1273,7 +1277,7 @@ bool Project::shouldBuildTargetType (build_tools::ProjectType::Target::Type targ | |||
| case Target::UnityPlugIn: | |||
| return shouldBuildUnityPlugin(); | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| return shouldBuildLV2(); | |||
| case Target::AggregateTarget: | |||
| case Target::SharedCodeTarget: | |||
| @@ -1537,6 +1541,14 @@ void Project::createAudioPluginPropertyEditors (PropertyListBuilder& props) | |||
| "If neither of these are selected, the appropriate one will be automatically added based on the \"Plugin is a synth\" option."); | |||
| } | |||
| props.add (new ChoicePropertyComponent (vst3ManifestEnabledValue, "Plugin VST3 moduleinfo.json Enabled"), | |||
| "Check this box if you want a moduleinfo.json file to be generated as part of the VST3 build. " | |||
| "This may improve startup/scanning time for hosts that understand the contents of this file. " | |||
| "This setting is disabled by default because the moduleinfo.json path can cause problems during code signing on macOS. " | |||
| "Bundles containing a moduleinfo.json may be rejected by code signing verification at any point in the future without notice per https://developer.apple.com/library/archive/technotes/tn2206." | |||
| "If you enable this setting, be aware that the code signature for the moduleinfo.json will be stored in its extended file attributes. " | |||
| "Therefore, you will need to ensure that these attributes are not changed or removed when distributing the VST3."); | |||
| props.add (new MultiChoicePropertyComponent (pluginAAXCategoryValue, "Plugin AAX Category", getAllAAXCategoryStrings(), getAllAAXCategoryVars()), | |||
| "AAX category."); | |||
| @@ -1570,7 +1582,6 @@ void Project::createAudioPluginPropertyEditors (PropertyListBuilder& props) | |||
| props.add (new TextPropertyComponent (pluginARACompatibleArchiveIDsValue, "Plugin ARA Compatible Document Archive IDs", 1024, true), | |||
| "List of compatible ARA Document Archive IDs - one per line"); | |||
| } | |||
| } | |||
| @@ -159,6 +159,7 @@ public: | |||
| static String getJuceSourceHFilename() { return "JuceHeader.h"; } | |||
| static String getJuceLV2DefinesFilename() { return "JuceLV2Defines.h"; } | |||
| static String getLV2FileWriterName() { return "juce_lv2_helper"; } | |||
| static String getVST3FileWriterName() { return "juce_vst3_helper"; } | |||
| //============================================================================== | |||
| template <class FileType> | |||
| @@ -291,6 +292,8 @@ public: | |||
| bool isPluginAAXBypassDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableBypass); } | |||
| bool isPluginAAXMultiMonoDisabled() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginAAXDisableMultiMono); } | |||
| bool isVst3ManifestEnabled() const { return vst3ManifestEnabledValue.get(); } | |||
| void disableStandaloneForARAPlugIn(); | |||
| static StringArray getAllAUMainTypeStrings() noexcept; | |||
| @@ -583,7 +586,7 @@ private: | |||
| pluginCodeValue, pluginChannelConfigsValue, pluginCharacteristicsValue, pluginAUExportPrefixValue, pluginAAXIdentifierValue, | |||
| pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue, | |||
| pluginEnableARA, pluginARAAnalyzableContentValue, pluginARAFactoryIDValue, pluginARAArchiveIDValue, pluginARACompatibleArchiveIDsValue, pluginARATransformFlagsValue, | |||
| pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue, pluginLV2URIValue; | |||
| pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue, pluginLV2URIValue, vst3ManifestEnabledValue; | |||
| //============================================================================== | |||
| std::unique_ptr<EnabledModulesList> enabledModulesList; | |||
| @@ -122,8 +122,9 @@ public: | |||
| case Target::AAXPlugIn: | |||
| case Target::UnityPlugIn: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| case Target::VST3PlugIn: | |||
| case Target::VST3Helper: | |||
| case Target::AudioUnitPlugIn: | |||
| case Target::AudioUnitv3PlugIn: | |||
| case Target::unspecified: | |||
| @@ -228,9 +228,12 @@ public: | |||
| { | |||
| using Target = build_tools::ProjectType::Target::Type; | |||
| if (type == Target::LV2TurtleProgram) | |||
| if (type == Target::LV2Helper) | |||
| return Project::getLV2FileWriterName() + suffix; | |||
| if (type == Target::VST3Helper) | |||
| return Project::getVST3FileWriterName() + suffix; | |||
| const auto forceUnityPrefix = type == Target::UnityPlugIn; | |||
| auto target = File::createLegalFileName (getTargetBinaryNameString (forceUnityPrefix).trim()); | |||
| @@ -655,12 +658,12 @@ public: | |||
| } | |||
| auto externalLibraries = getExternalLibraries (config, getOwner().getExternalLibrariesStringArray()); | |||
| auto additionalDependencies = type != SharedCodeTarget && type != LV2TurtleProgram && ! externalLibraries.isEmpty() | |||
| auto additionalDependencies = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && ! externalLibraries.isEmpty() | |||
| ? externalLibraries.joinIntoString (";") + ";%(AdditionalDependencies)" | |||
| : String(); | |||
| auto librarySearchPaths = config.getLibrarySearchPaths(); | |||
| auto additionalLibraryDirs = type != SharedCodeTarget && type != LV2TurtleProgram && librarySearchPaths.size() > 0 | |||
| auto additionalLibraryDirs = type != SharedCodeTarget && type != LV2Helper && type != VST3Helper && librarySearchPaths.size() > 0 | |||
| ? getOwner().replacePreprocessorTokens (config, librarySearchPaths.joinIntoString (";")) + ";%(AdditionalLibraryDirectories)" | |||
| : String(); | |||
| @@ -672,7 +675,7 @@ public: | |||
| : "%(IgnoreSpecificDefaultLibraries)"); | |||
| link->createNewChildElement ("GenerateDebugInformation")->addTextElement ((isDebug || config.shouldGenerateDebugSymbols()) ? "true" : "false"); | |||
| link->createNewChildElement ("ProgramDatabaseFile")->addTextElement (pdbFilename); | |||
| link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp || type == LV2TurtleProgram ? "Console" : "Windows"); | |||
| link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp || type == LV2Helper || type == VST3Helper ? "Console" : "Windows"); | |||
| if (config.getArchitectureString() == "Win32") | |||
| link->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86"); | |||
| @@ -718,7 +721,7 @@ public: | |||
| bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type))); | |||
| } | |||
| if (type != SharedCodeTarget && type != LV2TurtleProgram) | |||
| if (type != SharedCodeTarget && type != LV2Helper && type != VST3Helper) | |||
| { | |||
| auto* lib = group->createNewChildElement ("Lib"); | |||
| @@ -729,14 +732,27 @@ public: | |||
| lib->createNewChildElement ("AdditionalLibraryDirectories")->addTextElement (additionalLibraryDirs); | |||
| } | |||
| auto manifestFile = getOwner().getManifestPath(); | |||
| if (manifestFile.getRoot() != build_tools::RelativePath::unknown) | |||
| if (auto manifestFile = getOwner().getManifestPath(); manifestFile.getRoot() != build_tools::RelativePath::unknown || type == VST3Helper) | |||
| { | |||
| auto* bsc = group->createNewChildElement ("Manifest"); | |||
| bsc->createNewChildElement ("AdditionalManifestFiles") | |||
| ->addTextElement (manifestFile.rebased (getOwner().getProject().getFile().getParentDirectory(), | |||
| getOwner().getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder).toWindowsStyle()); | |||
| auto* additional = bsc->createNewChildElement ("AdditionalManifestFiles"); | |||
| if (manifestFile.getRoot() != build_tools::RelativePath::unknown) | |||
| { | |||
| additional->addTextElement (manifestFile.rebased (getOwner().getProject().getFile().getParentDirectory(), | |||
| getOwner().getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder).toWindowsStyle()); | |||
| } | |||
| if (type == VST3Helper) | |||
| { | |||
| const auto manifest = getOwner().getModuleFolderRelativeToProject ("juce_audio_processors").getChildFile ("format_types") | |||
| .getChildFile ("VST3_SDK") | |||
| .getChildFile ("helper.manifest"); | |||
| additional->addTextElement (manifest.rebased (getOwner().getProject().getFile().getParentDirectory(), | |||
| getOwner().getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder).toWindowsStyle()); | |||
| } | |||
| } | |||
| if (getTargetFileType() == staticLibrary && config.getArchitectureString() == "Win32") | |||
| @@ -774,12 +790,21 @@ public: | |||
| addFilesToCompile (group, *cppFiles, *headerFiles, *otherFilesGroup); | |||
| } | |||
| if (type == LV2TurtleProgram) | |||
| if (type == LV2Helper) | |||
| { | |||
| const auto location = owner.rebaseFromProjectFolderToBuildTarget (owner.getLV2TurtleDumpProgramSource()) | |||
| const auto location = owner.rebaseFromProjectFolderToBuildTarget (owner.getLV2HelperProgramSource()) | |||
| .toWindowsStyle(); | |||
| cppFiles->createNewChildElement ("ClCompile")->setAttribute ("Include", location); | |||
| } | |||
| else if (type == VST3Helper) | |||
| { | |||
| for (const auto& source : owner.getVST3HelperProgramSources (owner)) | |||
| { | |||
| const auto location = owner.rebaseFromProjectFolderToBuildTarget (source) | |||
| .toWindowsStyle(); | |||
| cppFiles->createNewChildElement ("ClCompile")->setAttribute ("Include", location); | |||
| } | |||
| } | |||
| } | |||
| if (getOwner().iconFile.existsAsFile()) | |||
| @@ -1274,7 +1299,7 @@ public: | |||
| const auto* writerTarget = [&]() -> MSVCTargetBase* | |||
| { | |||
| for (auto* target : owner.targets) | |||
| if (target->type == LV2TurtleProgram) | |||
| if (target->type == LV2Helper) | |||
| return target; | |||
| return nullptr; | |||
| @@ -1298,10 +1323,42 @@ public: | |||
| if (type == VST3PlugIn) | |||
| { | |||
| const auto segments = getVst3BundleStructure (config); | |||
| const auto manifestScript = [&]() -> String | |||
| { | |||
| const auto* writerTarget = [&]() -> MSVCTargetBase* | |||
| { | |||
| for (auto* target : owner.targets) | |||
| if (target->type == VST3Helper) | |||
| return target; | |||
| return nullptr; | |||
| }(); | |||
| if (writerTarget == nullptr) | |||
| return ""; | |||
| const auto writer = writerTarget->getConfigTargetPath (config) | |||
| + "\\" | |||
| + writerTarget->getBinaryNameWithSuffix (config); | |||
| // moduleinfotool doesn't handle Windows-style path separators properly when computing the bundle name | |||
| const auto normalisedBundlePath = getOwner().getOutDirFile (config, segments[0]).replace ("\\", "/"); | |||
| return "\r\n" | |||
| + writer.quoted() | |||
| + " -create -version " | |||
| + getOwner().project.getVersionString().quoted() | |||
| + " -path " | |||
| + normalisedBundlePath.quoted() | |||
| + " -output " | |||
| + (getOwner().getOutDirFile (config, segments[0]) + "\\Contents\\moduleinfo.json").quoted(); | |||
| }(); | |||
| const auto pkgScript = copyBuildOutputIntoBundle (segments); | |||
| const auto copyScript = copyBundleToInstallDirectory (segments, config.getVST3BinaryLocationString()); | |||
| return pkgScript + copyScript; | |||
| return pkgScript + manifestScript + copyScript; | |||
| } | |||
| if (type == VSTPlugIn && config.isPluginBinaryCopyStepEnabled()) | |||
| @@ -1365,7 +1422,7 @@ public: | |||
| { | |||
| auto librarySearchPaths = config.getLibrarySearchPaths(); | |||
| if (type != SharedCodeTarget && type != LV2TurtleProgram) | |||
| if (type != SharedCodeTarget && type != LV2Helper && type != VST3Helper) | |||
| if (auto* shared = getOwner().getSharedCodeTarget()) | |||
| librarySearchPaths.add (shared->getConfigTargetPath (config)); | |||
| @@ -1388,7 +1445,7 @@ public: | |||
| result.addArray (msBuildEscape (getOwner().getModuleLibs())); | |||
| if (type != SharedCodeTarget && type != LV2TurtleProgram) | |||
| if (type != SharedCodeTarget && type != LV2Helper && type != VST3Helper) | |||
| if (auto* shared = getOwner().getSharedCodeTarget()) | |||
| result.add (msBuildEscape (shared->getBinaryNameWithSuffix (config))); | |||
| @@ -1487,10 +1544,11 @@ public: | |||
| case Target::AggregateTarget: | |||
| case Target::VSTPlugIn: | |||
| case Target::VST3PlugIn: | |||
| case Target::VST3Helper: | |||
| case Target::AAXPlugIn: | |||
| case Target::UnityPlugIn: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| case Target::DynamicLibrary: | |||
| return true; | |||
| case Target::AudioUnitPlugIn: | |||
| @@ -1690,7 +1748,8 @@ protected: | |||
| void writeProjectDependencies (OutputStream& out) const | |||
| { | |||
| const auto sharedCodeGuid = getTargetGuid (MSVCTargetBase::SharedCodeTarget); | |||
| const auto turtleGuid = getTargetGuid (MSVCTargetBase::LV2TurtleProgram); | |||
| const auto lv2HelperGuid = getTargetGuid (MSVCTargetBase::LV2Helper); | |||
| const auto vst3HelperGuid = getTargetGuid (MSVCTargetBase::VST3Helper); | |||
| for (int addingOtherTargets = 0; addingOtherTargets < (sharedCodeGuid.isNotEmpty() ? 2 : 1); ++addingOtherTargets) | |||
| { | |||
| @@ -1706,13 +1765,17 @@ protected: | |||
| if (sharedCodeGuid.isNotEmpty() | |||
| && target->type != MSVCTargetBase::SharedCodeTarget | |||
| && target->type != MSVCTargetBase::LV2TurtleProgram) | |||
| && target->type != MSVCTargetBase::LV2Helper | |||
| && target->type != MSVCTargetBase::VST3Helper) | |||
| { | |||
| out << "\tProjectSection(ProjectDependencies) = postProject" << newLine | |||
| << "\t\t" << sharedCodeGuid << " = " << sharedCodeGuid << newLine; | |||
| if (target->type == MSVCTargetBase::LV2PlugIn && turtleGuid.isNotEmpty()) | |||
| out << "\t\t" << turtleGuid << " = " << turtleGuid << newLine; | |||
| if (target->type == MSVCTargetBase::LV2PlugIn && lv2HelperGuid.isNotEmpty()) | |||
| out << "\t\t" << lv2HelperGuid << " = " << lv2HelperGuid << newLine; | |||
| if (target->type == MSVCTargetBase::VST3PlugIn && vst3HelperGuid.isNotEmpty() && project.isVst3ManifestEnabled()) | |||
| out << "\t\t" << vst3HelperGuid << " = " << vst3HelperGuid << newLine; | |||
| out << "\tEndProjectSection" << newLine; | |||
| } | |||
| @@ -221,10 +221,14 @@ public: | |||
| s.add ("JUCE_LV2DIR := " + escapeQuotesAndSpaces (targetName) + ".lv2"); | |||
| targetName = "$(JUCE_LV2DIR)/" + targetName + ".so"; | |||
| } | |||
| else if (type == LV2TurtleProgram) | |||
| else if (type == LV2Helper) | |||
| { | |||
| targetName = Project::getLV2FileWriterName(); | |||
| } | |||
| else if (type == VST3Helper) | |||
| { | |||
| targetName = Project::getVST3FileWriterName(); | |||
| } | |||
| s.add ("JUCE_TARGET_" + getTargetVarName() + String (" := ") + escapeQuotesAndSpaces (targetName)); | |||
| @@ -331,9 +335,12 @@ public: | |||
| String getPhonyName() const | |||
| { | |||
| if (type == LV2TurtleProgram) | |||
| if (type == LV2Helper) | |||
| return "LV2_MANIFEST_HELPER"; | |||
| if (type == VST3Helper) | |||
| return "VST3_MANIFEST_HELPER"; | |||
| return String (getName()).upToFirstOccurrenceOf (" ", false, false); | |||
| } | |||
| @@ -349,6 +356,8 @@ public: | |||
| if (type == LV2PlugIn) | |||
| out << " $(JUCE_OUTDIR)/$(JUCE_TARGET_LV2_MANIFEST_HELPER)"; | |||
| else if (type == VST3PlugIn && owner.project.isVst3ManifestEnabled()) | |||
| out << " $(JUCE_OUTDIR)/$(JUCE_TARGET_VST3_MANIFEST_HELPER)"; | |||
| out << newLine; | |||
| @@ -399,6 +408,15 @@ public: | |||
| if (type == VST3PlugIn) | |||
| { | |||
| if (owner.project.isVst3ManifestEnabled()) | |||
| { | |||
| out << "\t$(V_AT) $(JUCE_OUTDIR)/$(JUCE_TARGET_VST3_MANIFEST_HELPER) " | |||
| "-create " | |||
| "-version " << owner.project.getVersionString().quoted() << " " | |||
| "-path \"$(JUCE_OUTDIR)/$(JUCE_VST3DIR)\" " | |||
| "-output \"$(JUCE_OUTDIR)/$(JUCE_VST3DIR)/Contents/moduleinfo.json\" " << newLine; | |||
| } | |||
| out << "\t-$(V_AT)[ ! \"$(JUCE_VST3DESTDIR)\" ] || (mkdir -p $(JUCE_VST3DESTDIR) && cp -R $(JUCE_COPYCMD_VST3))" << newLine; | |||
| } | |||
| else if (type == VSTPlugIn) | |||
| @@ -486,11 +504,12 @@ public: | |||
| case Target::AggregateTarget: | |||
| case Target::VSTPlugIn: | |||
| case Target::VST3PlugIn: | |||
| case Target::VST3Helper: | |||
| case Target::StandalonePlugIn: | |||
| case Target::DynamicLibrary: | |||
| case Target::UnityPlugIn: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| return true; | |||
| case Target::AAXPlugIn: | |||
| case Target::AudioUnitPlugIn: | |||
| @@ -1172,13 +1191,23 @@ private: | |||
| targetFiles.emplace_back (linuxSubprocessHelperProperties.getLinuxSubprocessHelperBinaryDataSource(), ""); | |||
| } | |||
| if (targetType == MakefileTarget::LV2TurtleProgram) | |||
| if (targetType == MakefileTarget::LV2Helper) | |||
| { | |||
| targetFiles.emplace_back (getLV2TurtleDumpProgramSource().rebased (projectFolder, | |||
| getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder), | |||
| targetFiles.emplace_back (getLV2HelperProgramSource().rebased (projectFolder, | |||
| getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder), | |||
| String{}); | |||
| } | |||
| else if (targetType == MakefileTarget::VST3Helper) | |||
| { | |||
| for (const auto& source : getVST3HelperProgramSources (*this)) | |||
| { | |||
| targetFiles.emplace_back (source.rebased (projectFolder, | |||
| getTargetFolder(), | |||
| build_tools::RelativePath::buildTargetFolder), | |||
| String{}); | |||
| } | |||
| } | |||
| return targetFiles; | |||
| }; | |||
| @@ -298,10 +298,10 @@ public: | |||
| case Target::AudioUnitPlugIn: | |||
| case Target::UnityPlugIn: | |||
| case Target::LV2PlugIn: | |||
| case Target::LV2TurtleProgram: | |||
| case Target::LV2Helper: | |||
| case Target::VST3Helper: | |||
| return ! iOS; | |||
| case Target::unspecified: | |||
| default: | |||
| break; | |||
| } | |||
| @@ -1068,11 +1068,16 @@ public: | |||
| break; | |||
| case ConsoleApp: | |||
| case LV2TurtleProgram: | |||
| case LV2Helper: | |||
| case VST3Helper: | |||
| xcodeFileType = "compiled.mach-o.executable"; | |||
| xcodeBundleExtension = String(); | |||
| xcodeProductType = "com.apple.product-type.tool"; | |||
| xcodeCopyToProductInstallPathAfterBuild = false; | |||
| if (type == VST3Helper) | |||
| xcodeFrameworks.add ("Cocoa"); | |||
| break; | |||
| case StaticLibrary: | |||
| @@ -1216,9 +1221,12 @@ public: | |||
| if (xcodeFileType == "archive.ar") | |||
| return getStaticLibbedFilename (binaryName); | |||
| if (type == LV2TurtleProgram) | |||
| if (type == LV2Helper) | |||
| return Project::getLV2FileWriterName(); | |||
| if (type == VST3Helper) | |||
| return Project::getVST3FileWriterName(); | |||
| return binaryName + xcodeBundleExtension; | |||
| }(); | |||
| @@ -1257,36 +1265,42 @@ public: | |||
| if (! owner.project.isAudioPluginProject()) | |||
| return; | |||
| if (type == XcodeTarget::StandalonePlugIn) // depends on AUv3 and shared code | |||
| { | |||
| if (auto* auv3Target = owner.getTargetOfType (XcodeTarget::AudioUnitv3PlugIn)) | |||
| dependencyIDs.add (auv3Target->addDependencyFor (*this)); | |||
| if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) | |||
| dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); | |||
| } | |||
| else if (type == XcodeTarget::AggregateTarget) // depends on all other targets | |||
| if (type == XcodeTarget::AggregateTarget) // depends on all other targets | |||
| { | |||
| for (auto* target : owner.targets) | |||
| if (target->type != XcodeTarget::AggregateTarget) | |||
| dependencyIDs.add (target->addDependencyFor (*this)); | |||
| return; | |||
| } | |||
| else if (type == XcodeTarget::LV2PlugIn) | |||
| if (type == XcodeTarget::LV2Helper || type == XcodeTarget::VST3Helper) | |||
| { | |||
| if (auto* helperTarget = owner.getTargetOfType (XcodeTarget::LV2TurtleProgram)) | |||
| dependencyIDs.add (helperTarget->addDependencyFor (*this)); | |||
| return; | |||
| } | |||
| if (type != XcodeTarget::SharedCodeTarget) // everything else depends on the sharedCodeTarget | |||
| { | |||
| if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) | |||
| dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); | |||
| } | |||
| else if (type == XcodeTarget::LV2TurtleProgram) | |||
| if (type == LV2PlugIn) | |||
| { | |||
| // No thanks | |||
| if (auto* helperTarget = owner.getTargetOfType (LV2Helper)) | |||
| dependencyIDs.add (helperTarget->addDependencyFor (*this)); | |||
| } | |||
| else if (type != XcodeTarget::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code | |||
| if (type == VST3PlugIn && owner.project.isVst3ManifestEnabled()) | |||
| { | |||
| if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) | |||
| dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); | |||
| if (auto* helperTarget = owner.getTargetOfType (VST3Helper)) | |||
| dependencyIDs.add (helperTarget->addDependencyFor (*this)); | |||
| } | |||
| if (type == XcodeTarget::StandalonePlugIn) | |||
| { | |||
| if (auto* auv3Target = owner.getTargetOfType (XcodeTarget::AudioUnitv3PlugIn)) | |||
| dependencyIDs.add (auv3Target->addDependencyFor (*this)); | |||
| } | |||
| } | |||
| @@ -1479,9 +1493,12 @@ public: | |||
| const auto productName = [&] | |||
| { | |||
| if (type == LV2TurtleProgram) | |||
| if (type == LV2Helper) | |||
| return Project::getLV2FileWriterName().quoted(); | |||
| if (type == VST3Helper) | |||
| return Project::getVST3FileWriterName().quoted(); | |||
| return owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString (type == UnityPlugIn)).quoted(); | |||
| }(); | |||
| @@ -1815,7 +1832,8 @@ public: | |||
| case LV2PlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getLV2PluginBinaryLocationString() : String(); | |||
| case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks"; | |||
| case StaticLibrary: | |||
| case LV2TurtleProgram: | |||
| case LV2Helper: | |||
| case VST3Helper: | |||
| case DynamicLibrary: | |||
| case AudioUnitv3PlugIn: | |||
| case StandalonePlugIn: | |||
| @@ -1831,7 +1849,7 @@ public: | |||
| if (getTargetFileType() == pluginBundle) | |||
| flags.add (owner.isiOS() ? "-bitcode_bundle" : "-bundle"); | |||
| if (type != Target::SharedCodeTarget && type != Target::LV2TurtleProgram) | |||
| if (type != Target::SharedCodeTarget && type != Target::LV2Helper && type != Target::VST3Helper) | |||
| { | |||
| if (owner.project.isAudioPluginProject()) | |||
| { | |||
| @@ -2104,10 +2122,10 @@ private: | |||
| target->addMainBuildProduct(); | |||
| if (target->type == XcodeTarget::LV2TurtleProgram | |||
| if (target->type == XcodeTarget::LV2Helper | |||
| && project.getEnabledModules().isModuleEnabled ("juce_audio_plugin_client")) | |||
| { | |||
| const auto path = rebaseFromProjectFolderToBuildTarget (getLV2TurtleDumpProgramSource()); | |||
| const auto path = rebaseFromProjectFolderToBuildTarget (getLV2HelperProgramSource ()); | |||
| addFile (FileOptions().withRelativePath ({ expandPath (path.toUnixStyle()), path.getRoot() }) | |||
| .withSkipPCHEnabled (true) | |||
| .withCompilationEnabled (true) | |||
| @@ -2116,6 +2134,21 @@ private: | |||
| .withXcodeTarget (target)); | |||
| } | |||
| if (target->type == XcodeTarget::VST3Helper | |||
| && project.getEnabledModules().isModuleEnabled ("juce_audio_processors")) | |||
| { | |||
| for (const auto& source : getVST3HelperProgramSources (*this)) | |||
| { | |||
| const auto path = rebaseFromProjectFolderToBuildTarget (source); | |||
| addFile (FileOptions().withRelativePath ({ expandPath (path.toUnixStyle()), path.getRoot() }) | |||
| .withSkipPCHEnabled (true) | |||
| .withCompilationEnabled (true) | |||
| .withInhibitWarningsEnabled (true) | |||
| .withCompilerFlags ("-std=c++17 -fobjc-arc") | |||
| .withXcodeTarget (target)); | |||
| } | |||
| } | |||
| auto targetName = String (target->getName()); | |||
| auto fileID = createID (targetName + "__targetbuildref"); | |||
| auto fileRefID = createID ("__productFileID" + targetName); | |||
| @@ -2266,7 +2299,8 @@ private: | |||
| if (! projectType.isStaticLibrary() | |||
| && target->type != XcodeTarget::SharedCodeTarget | |||
| && target->type != XcodeTarget::LV2TurtleProgram | |||
| && target->type != XcodeTarget::LV2Helper | |||
| && target->type != XcodeTarget::VST3Helper | |||
| && ! skipAUv3) | |||
| target->addBuildPhase ("PBXResourcesBuildPhase", resourceIDs); | |||
| @@ -2286,42 +2320,68 @@ private: | |||
| if (! projectType.isStaticLibrary() | |||
| && target->type != XcodeTarget::SharedCodeTarget | |||
| && target->type != XcodeTarget::LV2TurtleProgram) | |||
| && target->type != XcodeTarget::LV2Helper) | |||
| { | |||
| target->addBuildPhase ("PBXFrameworksBuildPhase", target->frameworkIDs); | |||
| } | |||
| } | |||
| if (target->type == XcodeTarget::LV2PlugIn) | |||
| // When building LV2 and VST3 plugins on Arm macs, we need to load and run the plugin | |||
| // bundle during a post-build step in order to generate the plugin's supporting files. | |||
| // Arm macs will only load shared libraries if they are signed, but Xcode runs its | |||
| // signing step after any post-build scripts. As a workaround, we check whether the | |||
| // plugin is signed and generate an adhoc certificate if necessary, before running | |||
| // the manifest-generator. | |||
| if (target->type == XcodeTarget::VST3PlugIn || target->type == XcodeTarget::LV2PlugIn) | |||
| { | |||
| // When building LV2 plugins on Arm macs, we need to load and run the plugin bundle | |||
| // during a post-build step in order to generate the plugin's supporting files. Arm | |||
| // macs will only load shared libraries if they are signed, but Xcode runs its | |||
| // signing step after any post-build scripts. As a workaround, we check whether the | |||
| // plugin is signed and generate an adhoc certificate if necessary, before running | |||
| // the manifest-generator. | |||
| auto script = "set -e\n" | |||
| "xcrun codesign --verify \"$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME\" " | |||
| "|| xcrun codesign -s - \"$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME\"\n" | |||
| "\"$CONFIGURATION_BUILD_DIR/../" | |||
| + Project::getLV2FileWriterName() | |||
| + "\" \"$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME\"\n"; | |||
| String script = "set -e\n"; | |||
| // Delete manifest if it's left over from an old build | |||
| if (target->type == XcodeTarget::VST3PlugIn) | |||
| script << "rm -f \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME/Contents/moduleinfo.json\"\n"; | |||
| for (ConstConfigIterator config (*this); config.next();) | |||
| // Sign the bundle so that it can be loaded by the manifest generator tools | |||
| script << "xcrun codesign --verify \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\" " | |||
| "|| xcrun codesign -f -s - \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\"\n"; | |||
| if (target->type == XcodeTarget::LV2PlugIn) | |||
| { | |||
| auto& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config); | |||
| const auto installPath = target->getInstallPathForConfiguration (xcodeConfig); | |||
| // Note: LV2 has a non-standard config build dir | |||
| script << "\"$CONFIGURATION_BUILD_DIR/../" | |||
| + Project::getLV2FileWriterName() | |||
| + "\" \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\"\n"; | |||
| if (installPath.isNotEmpty()) | |||
| for (ConstConfigIterator config (*this); config.next();) | |||
| { | |||
| const auto destination = installPath.replace ("$(HOME)", "$HOME"); | |||
| script << "if [ \"$CONFIGURATION\" = \"" << config->getName() << "\" ]; then\n" | |||
| "mkdir -p \"" << destination << "\"\n" | |||
| "/bin/ln -sfh \"$CONFIGURATION_BUILD_DIR\" \"" << destination << "\"\n" | |||
| "fi\n"; | |||
| auto& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config); | |||
| const auto installPath = target->getInstallPathForConfiguration (xcodeConfig); | |||
| if (installPath.isNotEmpty()) | |||
| { | |||
| const auto destination = installPath.replace ("$(HOME)", "$HOME"); | |||
| script << R"(if [ "$CONFIGURATION" = ")" << config->getName() << "\" ]; then\n" | |||
| "mkdir -p \"" << destination << "\"\n" | |||
| "/bin/ln -sfh \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\" \"" << destination << "\"\n" | |||
| "fi\n"; | |||
| } | |||
| } | |||
| } | |||
| else if (target->type == XcodeTarget::VST3PlugIn && project.isVst3ManifestEnabled()) | |||
| { | |||
| // Generate the manifest | |||
| script << "\"$CONFIGURATION_BUILD_DIR/" << Project::getVST3FileWriterName() << "\" " | |||
| "-create " | |||
| "-version " << project.getVersionString().quoted() << " " | |||
| "-path \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\" " | |||
| "-output \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME/Contents/moduleinfo.json\"\n"; | |||
| // Sign the manifest (a prerequisite of signing the containing bundle) | |||
| script << "xcrun codesign -f -s - \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME/Contents/moduleinfo.json\"\n"; | |||
| // Sign the full bundle | |||
| script << "xcrun codesign -f -s - \"$CONFIGURATION_BUILD_DIR/$FULL_PRODUCT_NAME\"\n"; | |||
| } | |||
| target->addShellScriptBuildPhase ("Generate manifest", script); | |||
| target->addShellScriptBuildPhase ("Update manifest", script); | |||
| } | |||
| target->addShellScriptBuildPhase ("Post-build script", getPostBuildScript()); | |||
| @@ -216,13 +216,52 @@ public: | |||
| void createPropertyEditors (PropertyListBuilder&); | |||
| void addSettingsForProjectType (const build_tools::ProjectType&); | |||
| build_tools::RelativePath getLV2TurtleDumpProgramSource() const | |||
| build_tools::RelativePath getLV2HelperProgramSource() const | |||
| { | |||
| return getModuleFolderRelativeToProject ("juce_audio_plugin_client") | |||
| .getChildFile ("LV2") | |||
| .getChildFile ("juce_LV2TurtleDumpProgram.cpp"); | |||
| } | |||
| std::vector<build_tools::RelativePath> getVST3HelperProgramSources (const ProjectExporter& exporter) const | |||
| { | |||
| const auto base = getModuleFolderRelativeToProject ("juce_audio_processors").getChildFile ("format_types") | |||
| .getChildFile ("VST3_SDK"); | |||
| const auto vst = base.getChildFile ("public.sdk") | |||
| .getChildFile ("source") | |||
| .getChildFile ("vst"); | |||
| const auto hosting = vst.getChildFile ("hosting"); | |||
| std::vector<build_tools::RelativePath> result | |||
| { | |||
| base.getChildFile ("public.sdk") | |||
| .getChildFile ("samples") | |||
| .getChildFile ("vst-utilities") | |||
| .getChildFile ("moduleinfotool") | |||
| .getChildFile ("source") | |||
| .getChildFile ("main.cpp"), | |||
| base.getChildFile ("pluginterfaces") | |||
| .getChildFile ("base") | |||
| .getChildFile ("coreiids.cpp"), | |||
| hosting.getChildFile ("module.cpp"), | |||
| vst.getChildFile ("moduleinfo") | |||
| .getChildFile ("moduleinfocreator.cpp"), | |||
| vst.getChildFile ("moduleinfo") | |||
| .getChildFile ("moduleinfoparser.cpp"), | |||
| vst.getChildFile ("utility") | |||
| .getChildFile ("stringconvert.cpp"), | |||
| }; | |||
| if (exporter.isOSX()) | |||
| result.push_back (hosting.getChildFile ("module_mac.mm")); | |||
| else if (exporter.isLinux()) | |||
| result.push_back (hosting.getChildFile ("module_linux.cpp")); | |||
| else if (exporter.isWindows()) | |||
| result.push_back (hosting.getChildFile ("module_win32.cpp")); | |||
| return result; | |||
| } | |||
| //============================================================================== | |||
| void copyMainGroupFromProject(); | |||
| Array<Project::Item>& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; } | |||
| @@ -391,6 +391,7 @@ namespace Ids | |||
| DECLARE_ID (lv2Uri); | |||
| DECLARE_ID (lv2UriUi); | |||
| DECLARE_ID (lv2BinaryLocation); | |||
| DECLARE_ID (vst3ManifestEnabled); | |||
| DECLARE_ID (osxSDK); | |||
| DECLARE_ID (osxCompatibility); | |||