@@ -77,13 +77,17 @@ class MSVCScriptBuilder
7777
7878 MSVCScriptBuilder& deleteFile (const StringOrBuilder& path)
7979 {
80+ jassert (path.value .isQuotedString ());
81+
8082 script << " del /s /q " << path.value ;
8183 script << newLine;
8284 return *this ;
8385 }
8486
8587 MSVCScriptBuilder& mkdir (const StringOrBuilder& path)
8688 {
89+ jassert (path.value .isQuotedString ());
90+
8791 script << " mkdir " << path.value ;
8892 script << newLine;
8993 return *this ;
@@ -175,7 +179,8 @@ class MSVCScriptBuilder
175179
176180 MSVCScriptBuilder& append (const StringOrBuilder& string)
177181 {
178- script << string.value << newLine;
182+ if (string.isNotEmpty ())
183+ script << string.value << newLine;
179184 return *this ;
180185 }
181186
@@ -1723,6 +1728,17 @@ class MSVCProjectExporterBase : public ProjectExporter
17231728 return aaxSdk.getChildFile (" Utilities" ).getChildFile (" PlugIn.ico" );
17241729 }
17251730
1731+ static bool shouldPerformCopyStepForPlugin (Target::Type pluginType,
1732+ const MSVCBuildConfiguration& config,
1733+ Architecture arch)
1734+ {
1735+ if (! config.isPluginBinaryCopyStepEnabled ())
1736+ return false ;
1737+
1738+ const auto binaryLocationId = getPluginTypeInfo (pluginType).second ;
1739+ return binaryLocationId.isValid () && config.getBinaryPath (binaryLocationId, arch).isNotEmpty ();
1740+ }
1741+
17261742 String getExtraPostBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const
17271743 {
17281744 using Builder = MSVCScriptBuilder;
@@ -1742,7 +1758,7 @@ class MSVCProjectExporterBase : public ProjectExporter
17421758 + " "
17431759 + (directory + " \\ " + segments[0 ] + " \\ " ).quoted ();
17441760
1745- return config. isPluginBinaryCopyStepEnabled ( ) ? copyStep : " " ;
1761+ return shouldPerformCopyStepForPlugin (type, config, arch ) ? copyStep : " " ;
17461762 };
17471763
17481764 if (type == AAXPlugIn)
@@ -1778,7 +1794,7 @@ class MSVCProjectExporterBase : public ProjectExporter
17781794
17791795 auto pkgScript = String (" copy /Y " ) + scriptPath.toWindowsStyle ().quoted () + " \" $(OutDir)\" " ;
17801796
1781- if (config. isPluginBinaryCopyStepEnabled ( ))
1797+ if (shouldPerformCopyStepForPlugin (type, config, arch ))
17821798 {
17831799 auto copyLocation = config.getBinaryPath (Ids::unityPluginBinaryLocation, arch);
17841800
@@ -1813,7 +1829,9 @@ class MSVCProjectExporterBase : public ProjectExporter
18131829 + " .lv2\"\r\n " ;
18141830
18151831 builder.runAndCheck (writer,
1816- config.isPluginBinaryCopyStepEnabled () ? copyStep : Builder{}.info (" Sucessfully generated LV2 manifest" ).build (),
1832+ shouldPerformCopyStepForPlugin (type, config, arch)
1833+ ? copyStep
1834+ : Builder{}.info (" Successfully generated LV2 manifest" ).build (),
18171835 Builder{}.error (" Failed to generate LV2 manifest." )
18181836 .exit (-1 ));
18191837
@@ -1923,7 +1941,7 @@ class MSVCProjectExporterBase : public ProjectExporter
19231941 .build ();
19241942 }
19251943
1926- if (type == VSTPlugIn && config. isPluginBinaryCopyStepEnabled ( ))
1944+ if (type == VSTPlugIn && shouldPerformCopyStepForPlugin (type, config, arch ))
19271945 {
19281946 const String copyCommand = " copy /Y \" $(OutDir)$(TargetFileName)\" \" "
19291947 + config.getBinaryPath (Ids::vstBinaryLocation, arch)
@@ -1938,25 +1956,90 @@ class MSVCProjectExporterBase : public ProjectExporter
19381956 return {};
19391957 }
19401958
1941- String getExtraPreBuildSteps ( const MSVCBuildConfiguration& config, Architecture arch) const
1959+ static std::pair< String, Identifier> getPluginTypeInfo (Target::Type targetType)
19421960 {
1943- const auto createBundleStructure = [&] (const StringArray& segments)
1961+ if (targetType == AAXPlugIn) return { " AAX" , Ids::aaxBinaryLocation };
1962+ if (targetType == VSTPlugIn) return { " VST (Legacy)" , Ids::vstBinaryLocation };
1963+ if (targetType == VST3PlugIn) return { " VST3" , Ids::vst3BinaryLocation };
1964+ if (targetType == UnityPlugIn) return { " Unity" , Ids::unityPluginBinaryLocation };
1965+ if (targetType == LV2PlugIn) return { " LV2" , Ids::lv2BinaryLocation };
1966+
1967+ return {};
1968+ }
1969+
1970+ static String generatePluginCopyStepPathValidatorScript (Target::Type targetType,
1971+ const MSVCBuildConfiguration& config,
1972+ Architecture arch)
1973+ {
1974+ MSVCScriptBuilder builder;
1975+
1976+ if (config.isPluginBinaryCopyStepEnabled ())
19441977 {
1945- auto directory = getOwner ().getOutDirFile (config, " " );
1946- MSVCScriptBuilder script;
1978+ const auto [projectTypeString, binaryLocationId] = getPluginTypeInfo (targetType);
19471979
1948- std::for_each (segments. begin (), std::prev (segments. end ()), [&] ( const auto & s )
1980+ if (projectTypeString. isNotEmpty () )
19491981 {
1950- directory += (directory. isEmpty () ? " " : " \\ " ) + s ;
1982+ const auto binaryPath = config. getBinaryPath (binaryLocationId, arch) ;
19511983
1952- script.ifelse (" not exist " + (directory + " \\ " ).quoted (),
1953- MSVCScriptBuilder{}.deleteFile (directory.quoted ())
1954- .mkdir (directory.quoted ()).build ());
1955- });
1984+ if (binaryPath.isEmpty ())
1985+ {
1986+ String warningMessage =
1987+ " Plugin Configuration Warning: Plugin copy step is enabled but no target "
1988+ " path is specified in the Projucer." ;
19561989
1957- return script.build ();
1958- };
1990+ warningMessage << " This can be configured via the \" "
1991+ << getArchitectureValueString (arch) << " "
1992+ << projectTypeString << " "
1993+ << " Binary Location\" option in the relevant Exporter configuration panel." ;
1994+
1995+ builder.warning (warningMessage.quoted ());
1996+ }
1997+ else
1998+ {
1999+ constexpr auto errorMessage =
2000+ " Plugin Copy Step Failure: Either the install path does not exist or you "
2001+ " do not have permission to write to the target directory. Ensure you "
2002+ " have the necessary permissions to write to the directory, or choose "
2003+ " a different install location (e.g., a folder in your user directory)." ;
2004+
2005+ MemoryOutputStream script;
2006+
2007+ const auto validProjectName = build_tools::makeValidIdentifier (config.project .getProjectNameString (),
2008+ false ,
2009+ true ,
2010+ false ,
2011+ false );
2012+ const auto validPluginName = build_tools::makeValidIdentifier (projectTypeString,
2013+ false ,
2014+ true ,
2015+ false ,
2016+ false );
2017+ script << " set TOUCH_NAME=\" .touch_\" "
2018+ << validProjectName << " _"
2019+ << validPluginName << " _"
2020+ << " \" %RANDOM%\" " << newLine;
2021+
2022+ String tempPath = binaryPath;
2023+ tempPath << " \\ %TOUCH_NAME%" ;
2024+
2025+ script << " (" << newLine;
2026+ script << " echo \" .\" > " << tempPath.quoted () << newLine;
2027+ script << " ) > nul 2>&1" << newLine;
2028+
2029+ builder.append (script.toString ());
2030+ builder.ifelse (" exist " + tempPath.quoted (),
2031+ MSVCScriptBuilder{}.deleteFile (tempPath.quoted ()),
2032+ MSVCScriptBuilder{}.error (String { errorMessage }.quoted ())
2033+ .exit (1 ));
2034+ }
2035+ }
2036+ }
19592037
2038+ return builder.build ();
2039+ }
2040+
2041+ static String generateToolchainValidatorScript (Architecture arch)
2042+ {
19602043 MSVCScriptBuilder builder;
19612044
19622045 if (arch == Architecture::win64)
@@ -1978,6 +2061,33 @@ class MSVCProjectExporterBase : public ProjectExporter
19782061 }, MSVCScriptBuilder{}.append (x86ToolchainErrorMessage));
19792062 }
19802063
2064+ return builder.build ();
2065+ }
2066+
2067+ String getExtraPreBuildSteps (const MSVCBuildConfiguration& config, Architecture arch) const
2068+ {
2069+ const auto createBundleStructure = [&] (const StringArray& segments)
2070+ {
2071+ auto directory = getOwner ().getOutDirFile (config, " " );
2072+ MSVCScriptBuilder script;
2073+
2074+ std::for_each (segments.begin (), std::prev (segments.end ()), [&] (const auto & s)
2075+ {
2076+ directory += (directory.isEmpty () ? " " : " \\ " ) + s;
2077+
2078+ script.ifelse (" not exist " + (directory + " \\ " ).quoted (),
2079+ MSVCScriptBuilder{}.deleteFile (directory.quoted ())
2080+ .mkdir (directory.quoted ()).build ());
2081+ });
2082+
2083+ return script.build ();
2084+ };
2085+
2086+ MSVCScriptBuilder builder;
2087+
2088+ builder.append (generatePluginCopyStepPathValidatorScript (type, config, arch));
2089+ builder.append (generateToolchainValidatorScript (arch));
2090+
19812091 if (type == LV2PlugIn)
19822092 {
19832093 const auto crossCompilationPairs =
@@ -2010,8 +2120,14 @@ class MSVCProjectExporterBase : public ProjectExporter
20102120 return builder.build ();
20112121 }
20122122
2123+ if (type == UnityPlugIn)
2124+ return builder.build ();
2125+
20132126 if (type == AAXPlugIn)
2014- return createBundleStructure (getAaxBundleStructure (config, arch));
2127+ return builder.build () + " \r\n " + createBundleStructure (getAaxBundleStructure (config, arch));
2128+
2129+ if (type == VSTPlugIn)
2130+ return builder.build ();
20152131
20162132 if (type == VST3PlugIn)
20172133 return builder.build () + " \r\n " + createBundleStructure (getVst3BundleStructure (config, arch));
0 commit comments