From 65305b1afe2a3d34dd49a09ce6928b6df922e36d Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 10 Jan 2023 14:10:52 +0000 Subject: [PATCH] Projucer: Build VST3 bundles from the MSVC exporters --- BREAKING-CHANGES.txt | 26 ++++ .../jucer_ProjectExport_CodeBlocks.h | 4 +- .../ProjectSaving/jucer_ProjectExport_MSVC.h | 126 ++++++++++++------ 3 files changed, 115 insertions(+), 41 deletions(-) diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index 973f3e0746..d2f7c04e75 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -1,6 +1,32 @@ JUCE breaking changes ===================== +develop +======= + +Change +------ +Projucer-generated MSVC projects now build VST3s as bundles, rather than as +single DLL files. + +Possible Issues +--------------- +Build workflows that expect the VST3 to be a single DLL may break. + +Workaround +---------- +Any post-build scripts that expect to copy or move the built VST3 should be +updated so that the entire bundle directory is copied/moved. The DLL itself +can still be located and extracted from within the generated bundle if +necessary. + +Rationale +--------- +Distributing VST3s as single files was deprecated in VST3 v3.6.10. JUCE's CMake +scripts already produce VST3s as bundles, so this change increases consistency +between the two build systems. + + Version 7.0.3 ============= diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h index 8d1c04b31f..553bafd6dd 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h @@ -225,9 +225,9 @@ private: if (archFlag.startsWith (prefix)) return archFlag.substring (prefix.length()); - else if (archFlag == "-m64") + if (archFlag == "-m64") return "x86_64"; - else if (archFlag == "-m32") + if (archFlag == "-m32") return "i386"; jassertfalse; diff --git a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h index c7b85ea209..96fb439b7c 100644 --- a/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h +++ b/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h @@ -1105,7 +1105,7 @@ public: .toWindowsStyle()); } - String getConfigTargetPath (const BuildConfiguration& config) const + String getConfigTargetPath (const MSVCBuildConfiguration& config) const { const auto result = getSolutionTargetPath (config) + "\\" + getName(); @@ -1147,7 +1147,6 @@ public: if (fileType == pluginBundle) { - if (type == VST3PlugIn) return ".vst3"; if (type == AAXPlugIn) return ".aaxdll"; return ".dll"; @@ -1209,27 +1208,47 @@ public: String getExtraPostBuildSteps (const MSVCBuildConfiguration& config) const { + const auto copyBuildOutputIntoBundle = [&] (const StringArray& segments) + { + return "copy /Y " + + getOutputFilePath (config).quoted() + + " " + + getOwner().getOutDirFile (config, segments.joinIntoString ("\\")).quoted(); + }; + + const auto copyBundleToInstallDirectory = [&] (const StringArray& segments, const String& directory) + { + const auto copyStep = "\r\nxcopy /E /H /K /R /Y /I " + + getOwner().getOutDirFile (config, segments[0]).quoted() + + " " + + (directory + "\\" + segments[0] + "\\").quoted(); + + return config.isPluginBinaryCopyStepEnabled() ? copyStep : ""; + }; + if (type == AAXPlugIn) { - build_tools::RelativePath aaxSDK (owner.getAAXPathString(), build_tools::RelativePath::projectFolder); - build_tools::RelativePath aaxLibsFolder = aaxSDK.getChildFile ("Libs"); - build_tools::RelativePath bundleScript = aaxSDK.getChildFile ("Utilities").getChildFile ("CreatePackage.bat"); - build_tools::RelativePath iconFilePath = getAAXIconFile(); + const build_tools::RelativePath aaxSDK (owner.getAAXPathString(), build_tools::RelativePath::projectFolder); + const build_tools::RelativePath aaxLibsFolder = aaxSDK.getChildFile ("Libs"); + const build_tools::RelativePath bundleScript = aaxSDK.getChildFile ("Utilities").getChildFile ("CreatePackage.bat"); + const build_tools::RelativePath iconFilePath = getAAXIconFile(); - auto outputFilename = config.getOutputFilename (".aaxplugin", true, type); - auto bundleDir = getOwner().getOutDirFile (config, outputFilename); - auto bundleContents = bundleDir + "\\Contents"; - auto archDir = bundleContents + String ("\\") + config.getArchitectureString(); - auto executablePath = archDir + String ("\\") + outputFilename; + const auto segments = getAaxBundleStructure (config); - auto pkgScript = String ("copy /Y ") + getOutputFilePath (config).quoted() + String (" ") + executablePath.quoted() + String ("\r\ncall ") - + createRebasedPath (bundleScript) + String (" ") + archDir.quoted() + String (" ") + createRebasedPath (iconFilePath); + const auto pkgScript = copyBuildOutputIntoBundle (segments); - if (config.isPluginBinaryCopyStepEnabled()) - return pkgScript + "\r\n" + "xcopy " + bundleDir.quoted() + " " - + String (config.getAAXBinaryLocationString() + "\\" + outputFilename + "\\").quoted() + " /E /H /K /R /Y"; + const auto archDir = StringArray (segments.strings.data(), segments.size() - 1).joinIntoString ("\\"); + const auto rebasedArchDir = getOwner().getOutDirFile (config, archDir); + const auto fixScript = "\r\ncall " + + createRebasedPath (bundleScript) + + " " + + rebasedArchDir.quoted() + + String (" ") + + createRebasedPath (iconFilePath); - return pkgScript; + const auto copyScript = copyBundleToInstallDirectory (segments, config.getAAXBinaryLocationString()); + + return pkgScript + fixScript + copyScript; } if (type == UnityPlugIn) @@ -1266,44 +1285,53 @@ public: + "\\" + writerTarget->getBinaryNameWithSuffix (config); - const auto copyScript = [&]() -> String - { - if (! config.isPluginBinaryCopyStepEnabled()) - return ""; + const auto copyStep = "xcopy /E /H /I /K /R /Y \"$(OutDir)\" \"" + + config.getLV2BinaryLocationString() + + '\\' + + config.getTargetBinaryNameString() + + ".lv2\"\r\n"; - return "xcopy /E /H /I /K /R /Y \"$(OutDir)\" \"" + config.getLV2BinaryLocationString() - + '\\' + config.getTargetBinaryNameString() + ".lv2\"\r\n"; - }(); - - return writer.quoted() + " \"$(OutDir)$(TargetFileName)\"\r\n" + copyScript; + return writer.quoted() + + " \"$(OutDir)$(TargetFileName)\"\r\n" + + (config.isPluginBinaryCopyStepEnabled() ? copyStep : ""); } - if (config.isPluginBinaryCopyStepEnabled()) + if (type == VST3PlugIn) { - auto copyScript = String ("copy /Y \"$(OutDir)$(TargetFileName)\"") + String (" \"$COPYDIR$\\$(TargetFileName)\""); + const auto segments = getVst3BundleStructure (config); + const auto pkgScript = copyBuildOutputIntoBundle (segments); + const auto copyScript = copyBundleToInstallDirectory (segments, config.getVST3BinaryLocationString()); - if (type == VSTPlugIn) return copyScript.replace ("$COPYDIR$", config.getVSTBinaryLocationString()); - if (type == VST3PlugIn) return copyScript.replace ("$COPYDIR$", config.getVST3BinaryLocationString()); + return pkgScript + copyScript; } + if (type == VSTPlugIn && config.isPluginBinaryCopyStepEnabled()) + return "copy /Y \"$(OutDir)$(TargetFileName)\" \"" + config.getVSTBinaryLocationString() + "\\$(TargetFileName)\""; + return {}; } String getExtraPreBuildSteps (const MSVCBuildConfiguration& config) const { - if (type == AAXPlugIn) + const auto createBundleStructure = [&] (const StringArray& segments) { + auto directory = getOwner().getOutDirFile (config, ""); String script; - auto bundleDir = getOwner().getOutDirFile (config, config.getOutputFilename (".aaxplugin", false, type)); - auto bundleContents = bundleDir + "\\Contents"; - auto archDir = bundleContents + String ("\\") + config.getArchitectureString(); - - for (auto& folder : StringArray { bundleDir, bundleContents, archDir }) - script += String ("if not exist \"") + folder + String ("\" mkdir \"") + folder + String ("\"\r\n"); + std::for_each (segments.begin(), std::prev (segments.end()), [&] (const auto& s) + { + directory += (directory.isEmpty() ? "" : "\\") + s; + script += "if not exist \"" + directory + "\" mkdir \"" + directory + "\"\r\n"; + }); return script; - } + }; + + if (type == AAXPlugIn) + return createBundleStructure (getAaxBundleStructure (config)); + + if (type == VST3PlugIn) + return createBundleStructure (getVst3BundleStructure (config)); return {}; } @@ -1343,7 +1371,7 @@ public: return getOwner().getOutDirFile (config, getBinaryNameWithSuffix (config)); } - StringArray getLibrarySearchPaths (const BuildConfiguration& config) const + StringArray getLibrarySearchPaths (const MSVCBuildConfiguration& config) const { auto librarySearchPaths = config.getLibrarySearchPaths(); @@ -1410,6 +1438,26 @@ public: } protected: + StringArray getAaxBundleStructure (const MSVCBuildConfiguration& config) const + { + const auto dllName = config.getOutputFilename (".aaxplugin", false, type); + return { dllName, "Contents", config.getArchitectureString(), dllName }; + } + + StringArray getVst3BundleStructure (const MSVCBuildConfiguration& config) const + { + static const std::map suffixes + { + { "Win32", "x86" }, + { "x64", "x86_64" }, + }; + + const auto iter = suffixes.find (config.getArchitectureString()); + + const auto dllName = config.getOutputFilename (".vst3", false, type); + return { dllName, "Contents", iter != suffixes.cend() ? iter->second + "-win" : "win", dllName }; + } + const MSVCProjectExporterBase& owner; String projectGuid; };