diff --git a/extras/Build/juce_build_tools/utils/juce_BuildHelperFunctions.cpp b/extras/Build/juce_build_tools/utils/juce_BuildHelperFunctions.cpp index f44839db3e..0131d8a566 100644 --- a/extras/Build/juce_build_tools/utils/juce_BuildHelperFunctions.cpp +++ b/extras/Build/juce_build_tools/utils/juce_BuildHelperFunctions.cpp @@ -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: diff --git a/extras/Build/juce_build_tools/utils/juce_ProjectType.h b/extras/Build/juce_build_tools/utils/juce_ProjectType.h index 8d71a0743b..cbfd1f235b 100644 --- a/extras/Build/juce_build_tools/utils/juce_ProjectType.h +++ b/extras/Build/juce_build_tools/utils/juce_ProjectType.h @@ -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; } diff --git a/extras/Projucer/Source/Project/jucer_Project.cpp b/extras/Projucer/Source/Project/jucer_Project.cpp index 9195972619..3e39ebf290 100644 --- a/extras/Projucer/Source/Project/jucer_Project.cpp +++ b/extras/Projucer/Source/Project/jucer_Project.cpp @@ -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"); - } } diff --git a/extras/Projucer/Source/Project/jucer_Project.h b/extras/Projucer/Source/Project/jucer_Project.h index 1a67f41a12..01dbad8476 100644 --- a/extras/Projucer/Source/Project/jucer_Project.h +++ b/extras/Projucer/Source/Project/jucer_Project.h @@ -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 @@ -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; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h index 3b05766cec..2f93927992 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h @@ -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: diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h index 8446c05ebd..da58e6ed6d 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h @@ -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; } diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h index 5f9954521d..e7fa047d8f 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h @@ -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; }; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h index 66b48dd2ac..55044d2956 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h @@ -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,37 +1265,43 @@ 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)); - } - else if (type == XcodeTarget::LV2PlugIn) - { - if (auto* helperTarget = owner.getTargetOfType (XcodeTarget::LV2TurtleProgram)) - dependencyIDs.add (helperTarget->addDependencyFor (*this)); - if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) - dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); + return; } - else if (type == XcodeTarget::LV2TurtleProgram) + + if (type == XcodeTarget::LV2Helper || type == XcodeTarget::VST3Helper) { - // No thanks + return; } - else if (type != XcodeTarget::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code + + if (type != XcodeTarget::SharedCodeTarget) // everything else depends on the sharedCodeTarget { if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget)) dependencyIDs.add (sharedCodeTarget->addDependencyFor (*this)); } + + if (type == LV2PlugIn) + { + if (auto* helperTarget = owner.getTargetOfType (LV2Helper)) + dependencyIDs.add (helperTarget->addDependencyFor (*this)); + } + + if (type == VST3PlugIn && owner.project.isVst3ManifestEnabled()) + { + 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"; - for (ConstConfigIterator config (*this); config.next();) + // 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"; + + // 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 (*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"); + auto& xcodeConfig = dynamic_cast (*config); + const auto installPath = target->getInstallPathForConfiguration (xcodeConfig); - script << "if [ \"$CONFIGURATION\" = \"" << config->getName() << "\" ]; then\n" - "mkdir -p \"" << destination << "\"\n" - "/bin/ln -sfh \"$CONFIGURATION_BUILD_DIR\" \"" << destination << "\"\n" - "fi\n"; + 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()); diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h index be0c0796a7..68a11921ae 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h @@ -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 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 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& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; } diff --git a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h index 8880034753..590e645624 100644 --- a/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h +++ b/extras/Projucer/Source/Utility/Helpers/jucer_PresetIDs.h @@ -391,6 +391,7 @@ namespace Ids DECLARE_ID (lv2Uri); DECLARE_ID (lv2UriUi); DECLARE_ID (lv2BinaryLocation); + DECLARE_ID (vst3ManifestEnabled); DECLARE_ID (osxSDK); DECLARE_ID (osxCompatibility);