1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

LV2: Add initial client support

This commit is contained in:
reuk 2021-10-20 20:46:02 +01:00
parent 1182024fc4
commit 61f3c1dd98
No known key found for this signature in database
GPG key ID: 9ADCD339CFC98A11
68 changed files with 2624 additions and 452 deletions

View file

@ -155,6 +155,7 @@ void Project::updateTitleDependencies()
bundleIdentifierValue. setDefault (getDefaultBundleIdentifierString());
pluginAUExportPrefixValue.setDefault (build_tools::makeValidIdentifier (projectName, false, true, false) + "AU");
pluginAAXIdentifierValue. setDefault (getDefaultAAXIdentifierString());
pluginLV2URIValue. setDefault (getDefaultLV2URI());
}
String Project::getDocumentTitle()
@ -171,6 +172,11 @@ void Project::updateCompanyNameDependencies()
updateLicenseWarning();
}
void Project::updateWebsiteDependencies()
{
pluginLV2URIValue.setDefault (getDefaultLV2URI());
}
void Project::updateProjectSettings()
{
projectRoot.setProperty (Ids::name, getDocumentTitle(), nullptr);
@ -335,6 +341,8 @@ void Project::initialiseAudioPluginValues()
pluginVSTNumMidiInputsValue.referTo (projectRoot, Ids::pluginVSTNumMidiInputs, getUndoManager(), 16);
pluginVSTNumMidiOutputsValue.referTo (projectRoot, Ids::pluginVSTNumMidiOutputs, getUndoManager(), 16);
pluginLV2URIValue.referTo (projectRoot, Ids::lv2Uri, getUndoManager(), getDefaultLV2URI());
}
void Project::updateOldStyleConfigList()
@ -1064,6 +1072,10 @@ void Project::valueTreePropertyChanged (ValueTree& tree, const Identifier& prope
{
updateCompanyNameDependencies();
}
else if (property == Ids::companyWebsite)
{
updateWebsiteDependencies();
}
else if (property == Ids::defines)
{
parsedPreprocessorDefs = parsePreprocessorDefs (preprocessorDefsValue.get());
@ -1224,33 +1236,38 @@ bool Project::shouldBuildTargetType (build_tools::ProjectType::Target::Type targ
if (! projectType.supportsTargetType (targetType))
return false;
using Target = build_tools::ProjectType::Target;
switch (targetType)
{
case build_tools::ProjectType::Target::VSTPlugIn:
case Target::VSTPlugIn:
return shouldBuildVST();
case build_tools::ProjectType::Target::VST3PlugIn:
case Target::VST3PlugIn:
return shouldBuildVST3();
case build_tools::ProjectType::Target::AAXPlugIn:
case Target::AAXPlugIn:
return shouldBuildAAX();
case build_tools::ProjectType::Target::RTASPlugIn:
case Target::RTASPlugIn:
return shouldBuildRTAS();
case build_tools::ProjectType::Target::AudioUnitPlugIn:
case Target::AudioUnitPlugIn:
return shouldBuildAU();
case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
case Target::AudioUnitv3PlugIn:
return shouldBuildAUv3();
case build_tools::ProjectType::Target::StandalonePlugIn:
case Target::StandalonePlugIn:
return shouldBuildStandalonePlugin();
case build_tools::ProjectType::Target::UnityPlugIn:
case Target::UnityPlugIn:
return shouldBuildUnityPlugin();
case build_tools::ProjectType::Target::AggregateTarget:
case build_tools::ProjectType::Target::SharedCodeTarget:
case Target::LV2PlugIn:
case Target::LV2TurtleProgram:
return shouldBuildLV2();
case Target::AggregateTarget:
case Target::SharedCodeTarget:
return projectType.isAudioPlugin();
case build_tools::ProjectType::Target::unspecified:
case Target::unspecified:
return false;
case build_tools::ProjectType::Target::GUIApp:
case build_tools::ProjectType::Target::ConsoleApp:
case build_tools::ProjectType::Target::StaticLibrary:
case build_tools::ProjectType::Target::DynamicLibrary:
case Target::GUIApp:
case Target::ConsoleApp:
case Target::StaticLibrary:
case Target::DynamicLibrary:
default:
break;
}
@ -1277,14 +1294,28 @@ build_tools::ProjectType::Target::Type Project::getTargetTypeFromFilePath (const
return path.contains (prefix + ".") || path.contains (prefix + "_");
};
if (isPluginClientSource ("AU") || isInPluginClientSubdir ("AU")) return build_tools::ProjectType::Target::AudioUnitPlugIn;
if (isPluginClientSource ("AUv3") || isInPluginClientSubdir ("AU")) return build_tools::ProjectType::Target::AudioUnitv3PlugIn;
if (isPluginClientSource ("AAX") || isInPluginClientSubdir ("AAX")) return build_tools::ProjectType::Target::AAXPlugIn;
if (isPluginClientSource ("RTAS") || isInPluginClientSubdir ("RTAS")) return build_tools::ProjectType::Target::RTASPlugIn;
if (isPluginClientSource ("VST2") || isInPluginClientSubdir ("VST")) return build_tools::ProjectType::Target::VSTPlugIn;
if (isPluginClientSource ("VST3") || isInPluginClientSubdir ("VST3")) return build_tools::ProjectType::Target::VST3PlugIn;
if (isPluginClientSource ("Standalone") || isInPluginClientSubdir ("Standalone")) return build_tools::ProjectType::Target::StandalonePlugIn;
if (isPluginClientSource ("Unity") || isInPluginClientSubdir ("Unity")) return build_tools::ProjectType::Target::UnityPlugIn;
using Target = build_tools::ProjectType::Target::Type;
struct FormatInfo
{
const char* source;
const char* subdir;
Target target;
};
const FormatInfo formatInfo[] { { "AU", "AU", Target::AudioUnitPlugIn },
{ "AUv3", "AU", Target::AudioUnitv3PlugIn },
{ "AAX", "AAX", Target::AAXPlugIn },
{ "RTAS", "RTAS", Target::RTASPlugIn },
{ "VST2", "VST", Target::VSTPlugIn },
{ "VST3", "VST3", Target::VST3PlugIn },
{ "Standalone", "Standalone", Target::StandalonePlugIn },
{ "Unity", "Unity", Target::UnityPlugIn },
{ "LV2", "LV2", Target::LV2PlugIn } };
for (const auto& info : formatInfo)
if (isPluginClientSource (info.source) || isInPluginClientSubdir (info.subdir))
return info.target;
return (returnSharedTargetIfNoValidSuffix ? build_tools::ProjectType::Target::SharedCodeTarget
: build_tools::ProjectType::Target::unspecified);
@ -1411,10 +1442,10 @@ void Project::createPropertyEditors (PropertyListBuilder& props)
void Project::createAudioPluginPropertyEditors (PropertyListBuilder& props)
{
props.add (new MultiChoicePropertyComponent (pluginFormatsValue, "Plugin Formats",
{ "VST3", "AU", "AUv3", "RTAS (deprecated)", "AAX", "Standalone", "Unity", "Enable IAA", "VST (Legacy)" },
{ "VST3", "AU", "AUv3", "RTAS (deprecated)", "AAX", "Standalone", "LV2", "Unity", "Enable IAA", "VST (Legacy)" },
{ Ids::buildVST3.toString(), Ids::buildAU.toString(), Ids::buildAUv3.toString(),
Ids::buildRTAS.toString(), Ids::buildAAX.toString(), Ids::buildStandalone.toString(), Ids::buildUnity.toString(),
Ids::enableIAA.toString(), Ids::buildVST.toString() }),
Ids::buildRTAS.toString(), Ids::buildAAX.toString(), Ids::buildStandalone.toString(),
Ids::buildLV2.toString(), Ids::buildUnity.toString(), Ids::enableIAA.toString(), Ids::buildVST.toString() }),
"Plugin formats to build. If you have selected \"VST (Legacy)\" then you will need to ensure that you have a VST2 SDK "
"in your header search paths. The VST2 SDK can be obtained from the vstsdk3610_11_06_2018_build_37 (or older) VST3 SDK "
"or JUCE version 5.3.2. You also need a VST2 license from Steinberg to distribute VST2 plug-ins.");
@ -1495,6 +1526,11 @@ void Project::createAudioPluginPropertyEditors (PropertyListBuilder& props)
props.add (new MultiChoicePropertyComponent (pluginVSTCategoryValue, "Plugin VST (Legacy) Category", getAllVSTCategoryStrings(), vstCategoryVars, 1),
"VST category.");
}
props.add (new TextPropertyComponent (pluginLV2URIValue, "LV2 URI", 128, false),
"This acts as a unique identifier for this plugin. "
"If you make any incompatible changes to your plugin (remove parameters, reorder parameters, change preset format etc.) "
"you MUST change this value. LV2 hosts will assume that any plugins with the same URI are interchangeable.");
}
//==============================================================================
@ -2609,6 +2645,7 @@ StringPairArray Project::getAudioPluginFlags() const
flags.set ("JucePlugin_Build_AAX", boolToString (shouldBuildAAX()));
flags.set ("JucePlugin_Build_Standalone", boolToString (shouldBuildStandalonePlugin()));
flags.set ("JucePlugin_Build_Unity", boolToString (shouldBuildUnityPlugin()));
flags.set ("JucePlugin_Build_LV2", boolToString (shouldBuildLV2()));
flags.set ("JucePlugin_Enable_IAA", boolToString (shouldEnableIAA()));
flags.set ("JucePlugin_Name", toStringLiteral (getPluginNameString()));
flags.set ("JucePlugin_Desc", toStringLiteral (getPluginDescriptionString()));

View file

@ -153,6 +153,8 @@ public:
static String getAppConfigFilename() { return "AppConfig.h"; }
static String getPluginDefinesFilename() { return "JucePluginDefines.h"; }
static String getJuceSourceHFilename() { return "JuceHeader.h"; }
static String getJuceLV2DefinesFilename() { return "JuceLV2Defines.h"; }
static String getLV2FileWriterName() { return "juce_lv2_helper"; }
//==============================================================================
template <class FileType>
@ -192,6 +194,7 @@ public:
String getDefaultBundleIdentifierString() const;
String getDefaultAAXIdentifierString() const { return getDefaultBundleIdentifierString(); }
String getDefaultPluginManufacturerString() const;
String getDefaultLV2URI() const { return getCompanyWebsiteString() + "/plugins/" + build_tools::makeValidIdentifier (getProjectNameString(), false, true, false); }
String getCompanyNameString() const { return companyNameValue.get(); }
String getCompanyCopyrightString() const { return companyCopyrightValue.get(); }
@ -265,6 +268,7 @@ public:
bool shouldBuildAAX() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildAAX); }
bool shouldBuildStandalonePlugin() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildStandalone); }
bool shouldBuildUnityPlugin() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildUnity); }
bool shouldBuildLV2() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::buildLV2); }
bool shouldEnableIAA() const { return isAudioPluginProject() && checkMultiChoiceVar (pluginFormatsValue, Ids::enableIAA); }
bool isPluginSynth() const { return checkMultiChoiceVar (pluginCharacteristicsValue, Ids::pluginIsSynth); }
@ -314,6 +318,8 @@ public:
return name;
}
String getLV2URI() const { return pluginLV2URIValue.get(); }
//==============================================================================
bool isAUPluginHost();
bool isVSTPluginHost();
@ -550,7 +556,7 @@ private:
ValueTreePropertyWithDefault pluginFormatsValue, pluginNameValue, pluginDescriptionValue, pluginManufacturerValue, pluginManufacturerCodeValue,
pluginCodeValue, pluginChannelConfigsValue, pluginCharacteristicsValue, pluginAUExportPrefixValue, pluginAAXIdentifierValue,
pluginAUMainTypeValue, pluginAUSandboxSafeValue, pluginRTASCategoryValue, pluginVSTCategoryValue, pluginVST3CategoryValue, pluginAAXCategoryValue,
pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue;
pluginVSTNumMidiInputsValue, pluginVSTNumMidiOutputsValue, pluginLV2URIValue;
//==============================================================================
std::unique_ptr<EnabledModulesList> enabledModulesList;
@ -595,6 +601,7 @@ private:
void updateTitleDependencies();
void updateCompanyNameDependencies();
void updateProjectSettings();
void updateWebsiteDependencies();
ValueTree getConfigurations() const;
ValueTree getConfigNode();

View file

@ -100,24 +100,28 @@ public:
bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
{
using Target = build_tools::ProjectType::Target;
switch (type)
{
case build_tools::ProjectType::Target::StandalonePlugIn:
case build_tools::ProjectType::Target::GUIApp:
case build_tools::ProjectType::Target::ConsoleApp:
case build_tools::ProjectType::Target::StaticLibrary:
case build_tools::ProjectType::Target::SharedCodeTarget:
case build_tools::ProjectType::Target::AggregateTarget:
case build_tools::ProjectType::Target::VSTPlugIn:
case build_tools::ProjectType::Target::DynamicLibrary:
case Target::StandalonePlugIn:
case Target::GUIApp:
case Target::ConsoleApp:
case Target::StaticLibrary:
case Target::SharedCodeTarget:
case Target::AggregateTarget:
case Target::VSTPlugIn:
case Target::DynamicLibrary:
return true;
case build_tools::ProjectType::Target::AAXPlugIn:
case build_tools::ProjectType::Target::RTASPlugIn:
case build_tools::ProjectType::Target::UnityPlugIn:
case build_tools::ProjectType::Target::VST3PlugIn:
case build_tools::ProjectType::Target::AudioUnitPlugIn:
case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
case build_tools::ProjectType::Target::unspecified:
case Target::AAXPlugIn:
case Target::RTASPlugIn:
case Target::UnityPlugIn:
case Target::LV2PlugIn:
case Target::LV2TurtleProgram:
case Target::VST3PlugIn:
case Target::AudioUnitPlugIn:
case Target::AudioUnitv3PlugIn:
case Target::unspecified:
default:
break;
}

View file

@ -162,6 +162,7 @@ public:
vst3BinaryLocation (config, Ids::vst3BinaryLocation, getUndoManager()),
rtasBinaryLocation (config, Ids::rtasBinaryLocation, getUndoManager()),
aaxBinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager()),
lv2BinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager()),
unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager(), {})
{
setPluginBinaryCopyLocationDefaults();
@ -181,6 +182,7 @@ public:
String getVST3BinaryLocationString() const { return vst3BinaryLocation.get(); }
String getRTASBinaryLocationString() const { return rtasBinaryLocation.get();}
String getAAXBinaryLocationString() const { return aaxBinaryLocation.get();}
String getLV2BinaryLocationString() const { return lv2BinaryLocation.get();}
String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); }
String getIntermediatesPathString() const { return intermediatesPathValue.get(); }
String getCharacterSetString() const { return characterSetValue.get(); }
@ -204,8 +206,16 @@ public:
return getName() + "|" + (is64Bit() ? "x64" : "Win32");
}
String getOutputFilename (const String& suffix, bool forceSuffix, bool forceUnityPrefix) const
String getOutputFilename (const String& suffix,
bool forceSuffix,
build_tools::ProjectType::Target::Type type) const
{
using Target = build_tools::ProjectType::Target::Type;
if (type == Target::LV2TurtleProgram)
return Project::getLV2FileWriterName() + suffix;
const auto forceUnityPrefix = type == Target::UnityPlugIn;
auto target = File::createLegalFileName (getTargetBinaryNameString (forceUnityPrefix).trim());
if (forceSuffix || ! target.containsChar ('.'))
@ -316,7 +326,7 @@ public:
intermediatesPathValue, characterSetValue, architectureTypeValue, fastMathValue, debugInformationFormatValue,
pluginBinaryCopyStepValue;
ValueTreePropertyWithDefault vstBinaryLocation, vst3BinaryLocation, rtasBinaryLocation, aaxBinaryLocation, unityPluginBinaryLocation;
ValueTreePropertyWithDefault vstBinaryLocation, vst3BinaryLocation, rtasBinaryLocation, aaxBinaryLocation, lv2BinaryLocation, unityPluginBinaryLocation;
Value architectureValueToListenTo;
@ -345,6 +355,11 @@ public:
1024, false),
"The folder in which the compiled AAX binary should be placed.");
if (project.shouldBuildLV2())
props.add (new TextPropertyComponentWithEnablement (lv2BinaryLocation, pluginBinaryCopyStepValue, "LV2 Binary Location",
1024, false),
"The folder in which the compiled LV2 binary should be placed.");
if (project.shouldBuildUnityPlugin())
props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepValue, "Unity Binary Location",
1024, false),
@ -367,6 +382,7 @@ public:
vst3BinaryLocation.setDefault (prefix + String ("\\VST3"));
rtasBinaryLocation.setDefault (prefix + String ("\\Digidesign\\DAE\\Plug-Ins"));
aaxBinaryLocation.setDefault (prefix + String ("\\Avid\\Audio\\Plug-Ins"));
lv2BinaryLocation.setDefault ("%APPDATA%\\LV2");
}
void valueChanged (Value&) override
@ -513,7 +529,7 @@ public:
{
auto* targetName = props->createNewChildElement ("TargetName");
setConditionAttribute (*targetName, config);
targetName->addTextElement (msBuildEscape (config.getOutputFilename ("", false, type == UnityPlugIn)));
targetName->addTextElement (msBuildEscape (config.getOutputFilename ("", false, type)));
}
{
@ -555,7 +571,7 @@ public:
}
bool isUsingEditAndContinue = false;
const auto pdbFilename = getOwner().getIntDirFile (config, config.getOutputFilename (".pdb", true, type == UnityPlugIn));
const auto pdbFilename = getOwner().getIntDirFile (config, config.getOutputFilename (".pdb", true, type));
{
auto* cl = group->createNewChildElement ("ClCompile");
@ -565,7 +581,7 @@ public:
if (isDebug || config.shouldGenerateDebugSymbols())
{
cl->createNewChildElement ("DebugInformationFormat")
->addTextElement (config.getDebugInformationFormatString());
->addTextElement (config.getDebugInformationFormatString());
}
auto includePaths = getOwner().getHeaderSearchPaths (config);
@ -607,24 +623,24 @@ public:
}
auto externalLibraries = getExternalLibraries (config, getOwner().getExternalLibrariesStringArray());
auto additionalDependencies = type != SharedCodeTarget && ! externalLibraries.isEmpty()
auto additionalDependencies = type != SharedCodeTarget && type != LV2TurtleProgram && ! externalLibraries.isEmpty()
? externalLibraries.joinIntoString (";") + ";%(AdditionalDependencies)"
: String();
auto librarySearchPaths = config.getLibrarySearchPaths();
auto additionalLibraryDirs = type != SharedCodeTarget && librarySearchPaths.size() > 0
auto additionalLibraryDirs = type != SharedCodeTarget && type != LV2TurtleProgram && librarySearchPaths.size() > 0
? getOwner().replacePreprocessorTokens (config, librarySearchPaths.joinIntoString (";")) + ";%(AdditionalLibraryDirectories)"
: String();
{
auto* link = group->createNewChildElement ("Link");
link->createNewChildElement ("OutputFile")->addTextElement (getOutputFilePath (config, type == UnityPlugIn));
link->createNewChildElement ("OutputFile")->addTextElement (getOutputFilePath (config));
link->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
link->createNewChildElement ("IgnoreSpecificDefaultLibraries")->addTextElement (isDebug ? "libcmt.lib; msvcrt.lib;;%(IgnoreSpecificDefaultLibraries)"
: "%(IgnoreSpecificDefaultLibraries)");
link->createNewChildElement ("GenerateDebugInformation")->addTextElement ((isDebug || config.shouldGenerateDebugSymbols()) ? "true" : "false");
link->createNewChildElement ("ProgramDatabaseFile")->addTextElement (pdbFilename);
link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp ? "Console" : "Windows");
link->createNewChildElement ("SubSystem")->addTextElement (type == ConsoleApp || type == LV2TurtleProgram ? "Console" : "Windows");
if (! config.is64Bit())
link->createNewChildElement ("TargetMachine")->addTextElement ("MachineX86");
@ -667,10 +683,10 @@ public:
{
auto* bsc = group->createNewChildElement ("Bscmake");
bsc->createNewChildElement ("SuppressStartupBanner")->addTextElement ("true");
bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type == UnityPlugIn)));
bsc->createNewChildElement ("OutputFile")->addTextElement (getOwner().getIntDirFile (config, config.getOutputFilename (".bsc", true, type)));
}
if (type != SharedCodeTarget)
if (type != SharedCodeTarget && type != LV2TurtleProgram)
{
auto* lib = group->createNewChildElement ("Lib");
@ -725,6 +741,12 @@ public:
if (group.getNumChildren() > 0)
addFilesToCompile (group, *cppFiles, *headerFiles, *otherFilesGroup);
}
if (type == LV2TurtleProgram)
{
cppFiles->createNewChildElement ("ClCompile")
->setAttribute ("Include", owner.getLV2TurtleDumpProgramSource().toWindowsStyle());
}
}
if (getOwner().iconFile.existsAsFile())
@ -1055,8 +1077,12 @@ public:
String getConfigTargetPath (const BuildConfiguration& config) const
{
auto solutionTargetFolder = getSolutionTargetPath (config);
return solutionTargetFolder + "\\" + getName();
const auto result = getSolutionTargetPath (config) + "\\" + getName();
if (type == LV2PlugIn)
return result + "\\" + config.getTargetBinaryNameString() + ".lv2";
return result;
}
String getIntermediatesPath (const MSVCBuildConfiguration& config) const
@ -1101,13 +1127,6 @@ public:
return {};
}
XmlElement* createToolElement (XmlElement& parent, const String& toolName) const
{
auto* e = parent.createNewChildElement ("Tool");
e->setAttribute ("Name", toolName);
return e;
}
String getPreprocessorDefs (const BuildConfiguration& config, const String& joinString) const
{
auto defines = getOwner().msvcExtraPreprocessorDefs;
@ -1168,13 +1187,13 @@ public:
build_tools::RelativePath bundleScript = aaxSDK.getChildFile ("Utilities").getChildFile ("CreatePackage.bat");
build_tools::RelativePath iconFilePath = getAAXIconFile();
auto outputFilename = config.getOutputFilename (".aaxplugin", true, false);
auto outputFilename = config.getOutputFilename (".aaxplugin", true, type);
auto bundleDir = getOwner().getOutDirFile (config, outputFilename);
auto bundleContents = bundleDir + "\\Contents";
auto archDir = bundleContents + String ("\\") + (config.is64Bit() ? "x64" : "Win32");
auto executablePath = archDir + String ("\\") + outputFilename;
auto pkgScript = String ("copy /Y ") + getOutputFilePath (config, false).quoted() + String (" ") + executablePath.quoted() + String ("\r\ncall ")
auto pkgScript = String ("copy /Y ") + getOutputFilePath (config).quoted() + String (" ") + executablePath.quoted() + String ("\r\ncall ")
+ createRebasedPath (bundleScript) + String (" ") + archDir.quoted() + String (" ") + createRebasedPath (iconFilePath);
if (config.isPluginBinaryCopyStepEnabled())
@ -1183,11 +1202,12 @@ public:
return pkgScript;
}
else if (type == UnityPlugIn)
if (type == UnityPlugIn)
{
build_tools::RelativePath scriptPath (config.project.getGeneratedCodeFolder().getChildFile (config.project.getUnityScriptName()),
getOwner().getTargetFolder(),
build_tools::RelativePath::projectFolder);
getOwner().getTargetFolder(),
build_tools::RelativePath::projectFolder);
auto pkgScript = String ("copy /Y ") + scriptPath.toWindowsStyle().quoted() + " \"$(OutDir)\"";
@ -1201,7 +1221,35 @@ public:
return pkgScript;
}
else if (config.isPluginBinaryCopyStepEnabled())
if (type == LV2PlugIn)
{
const auto* writerTarget = [&]() -> MSVCTargetBase*
{
for (auto* target : owner.targets)
if (target->type == LV2TurtleProgram)
return target;
return nullptr;
}();
const auto writer = writerTarget->getConfigTargetPath (config)
+ "\\"
+ writerTarget->getBinaryNameWithSuffix (config);
const auto copyScript = [&]() -> String
{
if (! config.isPluginBinaryCopyStepEnabled())
return "";
return "xcopy /E /H /I /K /R /Y \"$(OutDir)\" \"" + config.getLV2BinaryLocationString()
+ '\\' + config.getTargetBinaryNameString() + ".lv2\"\r\n";
}();
return writer.quoted() + " \"$(OutDir)$(TargetFileName)\"\r\n" + copyScript;
}
if (config.isPluginBinaryCopyStepEnabled())
{
auto copyScript = String ("copy /Y \"$(OutDir)$(TargetFileName)\"") + String (" \"$COPYDIR$\\$(TargetFileName)\"");
@ -1219,7 +1267,7 @@ public:
{
String script;
auto bundleDir = getOwner().getOutDirFile (config, config.getOutputFilename (".aaxplugin", false, false));
auto bundleDir = getOwner().getOutDirFile (config, config.getOutputFilename (".aaxplugin", false, type));
auto bundleContents = bundleDir + "\\Contents";
auto archDir = bundleContents + String ("\\") + (config.is64Bit() ? "x64" : "Win32");
@ -1311,21 +1359,21 @@ public:
return searchPaths;
}
String getBinaryNameWithSuffix (const MSVCBuildConfiguration& config, bool forceUnityPrefix) const
String getBinaryNameWithSuffix (const MSVCBuildConfiguration& config) const
{
return config.getOutputFilename (getTargetSuffix(), true, forceUnityPrefix);
return config.getOutputFilename (getTargetSuffix(), true, type);
}
String getOutputFilePath (const MSVCBuildConfiguration& config, bool forceUnityPrefix) const
String getOutputFilePath (const MSVCBuildConfiguration& config) const
{
return getOwner().getOutDirFile (config, getBinaryNameWithSuffix (config, forceUnityPrefix));
return getOwner().getOutDirFile (config, getBinaryNameWithSuffix (config));
}
StringArray getLibrarySearchPaths (const BuildConfiguration& config) const
{
auto librarySearchPaths = config.getLibrarySearchPaths();
if (type != SharedCodeTarget)
if (type != SharedCodeTarget && type != LV2TurtleProgram)
if (auto* shared = getOwner().getSharedCodeTarget())
librarySearchPaths.add (shared->getConfigTargetPath (config));
@ -1348,9 +1396,9 @@ public:
result.addArray (msBuildEscape (getOwner().getModuleLibs()));
if (type != SharedCodeTarget)
if (type != SharedCodeTarget && type != LV2TurtleProgram)
if (auto* shared = getOwner().getSharedCodeTarget())
result.add (msBuildEscape (shared->getBinaryNameWithSuffix (config, false)));
result.add (msBuildEscape (shared->getBinaryNameWithSuffix (config)));
return result;
}
@ -1440,24 +1488,28 @@ public:
bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
{
using Target = build_tools::ProjectType::Target;
switch (type)
{
case build_tools::ProjectType::Target::StandalonePlugIn:
case build_tools::ProjectType::Target::GUIApp:
case build_tools::ProjectType::Target::ConsoleApp:
case build_tools::ProjectType::Target::StaticLibrary:
case build_tools::ProjectType::Target::SharedCodeTarget:
case build_tools::ProjectType::Target::AggregateTarget:
case build_tools::ProjectType::Target::VSTPlugIn:
case build_tools::ProjectType::Target::VST3PlugIn:
case build_tools::ProjectType::Target::AAXPlugIn:
case build_tools::ProjectType::Target::RTASPlugIn:
case build_tools::ProjectType::Target::UnityPlugIn:
case build_tools::ProjectType::Target::DynamicLibrary:
case Target::StandalonePlugIn:
case Target::GUIApp:
case Target::ConsoleApp:
case Target::StaticLibrary:
case Target::SharedCodeTarget:
case Target::AggregateTarget:
case Target::VSTPlugIn:
case Target::VST3PlugIn:
case Target::AAXPlugIn:
case Target::RTASPlugIn:
case Target::UnityPlugIn:
case Target::LV2PlugIn:
case Target::LV2TurtleProgram:
case Target::DynamicLibrary:
return true;
case build_tools::ProjectType::Target::AudioUnitPlugIn:
case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
case build_tools::ProjectType::Target::unspecified:
case Target::AudioUnitPlugIn:
case Target::AudioUnitv3PlugIn:
case Target::unspecified:
default:
break;
}
@ -1639,14 +1691,11 @@ protected:
return getCleanedStringArray (searchPaths);
}
String getSharedCodeGuid() const
String getTargetGuid (MSVCTargetBase::Type type) const
{
String sharedCodeGuid;
for (int i = 0; i < targets.size(); ++i)
if (auto* target = targets[i])
if (target->type == build_tools::ProjectType::Target::SharedCodeTarget)
return target->getProjectGuid();
for (auto* target : targets)
if (target != nullptr && target->type == type)
return target->getProjectGuid();
return {};
}
@ -1654,7 +1703,8 @@ protected:
//==============================================================================
void writeProjectDependencies (OutputStream& out) const
{
auto sharedCodeGuid = getSharedCodeGuid();
const auto sharedCodeGuid = getTargetGuid (MSVCTargetBase::SharedCodeTarget);
const auto turtleGuid = getTargetGuid (MSVCTargetBase::LV2TurtleProgram);
for (int addingOtherTargets = 0; addingOtherTargets < (sharedCodeGuid.isNotEmpty() ? 2 : 1); ++addingOtherTargets)
{
@ -1662,16 +1712,24 @@ protected:
{
if (auto* target = targets[i])
{
if (sharedCodeGuid.isEmpty() || (addingOtherTargets != 0) == (target->type != build_tools::ProjectType::Target::StandalonePlugIn))
if (sharedCodeGuid.isEmpty() || (addingOtherTargets != 0) == (target->type != MSVCTargetBase::StandalonePlugIn))
{
out << "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"" << projectName << " - "
<< target->getName() << "\", \""
<< target->getVCProjFile().getFileName() << "\", \"" << target->getProjectGuid() << '"' << newLine;
if (sharedCodeGuid.isNotEmpty() && target->type != build_tools::ProjectType::Target::SharedCodeTarget)
if (sharedCodeGuid.isNotEmpty()
&& target->type != MSVCTargetBase::SharedCodeTarget
&& target->type != MSVCTargetBase::LV2TurtleProgram)
{
out << "\tProjectSection(ProjectDependencies) = postProject" << newLine
<< "\t\t" << sharedCodeGuid << " = " << sharedCodeGuid << newLine
<< "\tEndProjectSection" << newLine;
<< "\t\t" << sharedCodeGuid << " = " << sharedCodeGuid << newLine;
if (target->type == MSVCTargetBase::LV2PlugIn && turtleGuid.isNotEmpty())
out << "\t\t" << turtleGuid << " = " << turtleGuid << newLine;
out << "\tEndProjectSection" << newLine;
}
out << "EndProject" << newLine;
}

View file

@ -33,6 +33,7 @@ protected:
pluginBinaryCopyStepValue (config, Ids::enablePluginBinaryCopyStep, getUndoManager(), true),
vstBinaryLocation (config, Ids::vstBinaryLocation, getUndoManager(), "$(HOME)/.vst"),
vst3BinaryLocation (config, Ids::vst3BinaryLocation, getUndoManager(), "$(HOME)/.vst3"),
lv2BinaryLocation (config, Ids::lv2BinaryLocation, getUndoManager(), "$(HOME)/.lv2"),
unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager(), "$(HOME)/UnityPlugins")
{
linkTimeOptimisationValue.setDefault (false);
@ -50,7 +51,7 @@ protected:
"Specifies the 32/64-bit architecture to use. If you don't see the required architecture in this list, you can also specify the desired "
"flag on the command-line when invoking make by passing \"TARGET_ARCH=-march=<arch to use>\"");
auto isBuildingAnyPlugins = (project.shouldBuildVST() || project.shouldBuildVST3() || project.shouldBuildUnityPlugin());
auto isBuildingAnyPlugins = (project.shouldBuildVST() || project.shouldBuildVST3() || project.shouldBuildUnityPlugin() || project.shouldBuildLV2());
if (isBuildingAnyPlugins)
{
@ -62,6 +63,11 @@ protected:
1024, false),
"The folder in which the compiled VST3 binary should be placed.");
if (project.shouldBuildLV2())
props.add (new TextPropertyComponentWithEnablement (lv2BinaryLocation, pluginBinaryCopyStepValue, "LV2 Binary Location",
1024, false),
"The folder in which the compiled LV2 binary should be placed.");
if (project.shouldBuildUnityPlugin())
props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepValue, "Unity Binary Location",
1024, false),
@ -96,12 +102,13 @@ protected:
bool isPluginBinaryCopyStepEnabled() const { return pluginBinaryCopyStepValue.get(); }
String getVSTBinaryLocationString() const { return vstBinaryLocation.get(); }
String getVST3BinaryLocationString() const { return vst3BinaryLocation.get(); }
String getLV2BinaryLocationString() const { return lv2BinaryLocation.get(); }
String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); }
private:
//==============================================================================
ValueTreePropertyWithDefault architectureTypeValue, pluginBinaryCopyStepValue,
vstBinaryLocation, vst3BinaryLocation, unityPluginBinaryLocation;
vstBinaryLocation, vst3BinaryLocation, lv2BinaryLocation, unityPluginBinaryLocation;
};
BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
@ -202,10 +209,20 @@ public:
s.add ("JUCE_UNITYDIR := Unity");
targetName = "$(JUCE_UNITYDIR)/" + targetName;
}
else if (type == LV2PlugIn)
{
s.add ("JUCE_LV2DIR := " + targetName + ".lv2");
targetName = "$(JUCE_LV2DIR)/" + targetName + ".so";
}
else if (type == LV2TurtleProgram)
{
targetName = Project::getLV2FileWriterName();
}
s.add ("JUCE_TARGET_" + getTargetVarName() + String (" := ") + escapeQuotesAndSpaces (targetName));
if (config.isPluginBinaryCopyStepEnabled() && (type == VST3PlugIn || type == VSTPlugIn || type == UnityPlugIn))
if (config.isPluginBinaryCopyStepEnabled()
&& (type == VST3PlugIn || type == VSTPlugIn || type == UnityPlugIn || type == LV2PlugIn))
{
String copyCmd ("JUCE_COPYCMD_" + getTargetVarName() + String (" := $(JUCE_OUTDIR)/"));
@ -224,6 +241,12 @@ public:
s.add ("JUCE_UNITYDESTDIR := " + config.getUnityPluginBinaryLocationString());
s.add (copyCmd + "$(JUCE_UNITYDIR)/. $(JUCE_UNITYDESTDIR)");
}
else if (type == LV2PlugIn)
{
s.add ("JUCE_LV2DESTDIR := " + config.getLV2BinaryLocationString());
s.add ("JUCE_LV2_FULL_PATH := $(JUCE_OUTDIR)/$(JUCE_TARGET_LV2_PLUGIN)");
s.add (copyCmd + "$(JUCE_LV2DIR) $(JUCE_LV2DESTDIR)");
}
}
return s;
@ -282,6 +305,9 @@ public:
String getPhonyName() const
{
if (type == LV2TurtleProgram)
return "LV2_MANIFEST_HELPER";
return String (getName()).upToFirstOccurrenceOf (" ", false, false);
}
@ -295,6 +321,9 @@ public:
if (type != SharedCodeTarget && owner.shouldBuildTargetType (SharedCodeTarget))
out << " $(JUCE_OUTDIR)/$(JUCE_TARGET_SHARED_CODE)";
if (type == LV2PlugIn)
out << " $(JUCE_OUTDIR)/$(JUCE_TARGET_LV2_MANIFEST_HELPER)";
out << newLine;
if (! packages.isEmpty())
@ -317,6 +346,8 @@ public:
out << "\t-$(V_AT)mkdir -p $(JUCE_OUTDIR)/$(JUCE_VST3DIR)/$(JUCE_VST3SUBDIR)" << newLine;
else if (type == UnityPlugIn)
out << "\t-$(V_AT)mkdir -p $(JUCE_OUTDIR)/$(JUCE_UNITYDIR)" << newLine;
else if (type == LV2PlugIn)
out << "\t-$(V_AT)mkdir -p $(JUCE_OUTDIR)/$(JUCE_LV2DIR)" << newLine;
if (owner.projectType.isStaticLibrary() || type == SharedCodeTarget)
{
@ -362,6 +393,13 @@ public:
<< "\t-$(V_AT)mkdir -p $(JUCE_UNITYDESTDIR)" << newLine
<< "\t-$(V_AT)cp -R $(JUCE_COPYCMD_UNITY_PLUGIN)" << newLine;
}
else if (type == LV2PlugIn)
{
out << "\t$(V_AT) $(JUCE_OUTDIR)/$(JUCE_TARGET_LV2_MANIFEST_HELPER) "
"$(abspath $(JUCE_LV2_FULL_PATH))" << newLine
<< "\t-$(V_AT)mkdir -p $(JUCE_LV2DESTDIR)" << newLine
<< "\t-$(V_AT)cp -R $(JUCE_COPYCMD_LV2_PLUGIN)" << newLine;
}
out << newLine;
}
@ -417,24 +455,28 @@ public:
bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
{
using Target = build_tools::ProjectType::Target;
switch (type)
{
case build_tools::ProjectType::Target::GUIApp:
case build_tools::ProjectType::Target::ConsoleApp:
case build_tools::ProjectType::Target::StaticLibrary:
case build_tools::ProjectType::Target::SharedCodeTarget:
case build_tools::ProjectType::Target::AggregateTarget:
case build_tools::ProjectType::Target::VSTPlugIn:
case build_tools::ProjectType::Target::VST3PlugIn:
case build_tools::ProjectType::Target::StandalonePlugIn:
case build_tools::ProjectType::Target::DynamicLibrary:
case build_tools::ProjectType::Target::UnityPlugIn:
case Target::GUIApp:
case Target::ConsoleApp:
case Target::StaticLibrary:
case Target::SharedCodeTarget:
case Target::AggregateTarget:
case Target::VSTPlugIn:
case Target::VST3PlugIn:
case Target::StandalonePlugIn:
case Target::DynamicLibrary:
case Target::UnityPlugIn:
case Target::LV2PlugIn:
case Target::LV2TurtleProgram:
return true;
case build_tools::ProjectType::Target::AAXPlugIn:
case build_tools::ProjectType::Target::RTASPlugIn:
case build_tools::ProjectType::Target::AudioUnitPlugIn:
case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
case build_tools::ProjectType::Target::unspecified:
case Target::AAXPlugIn:
case Target::RTASPlugIn:
case Target::AudioUnitPlugIn:
case Target::AudioUnitv3PlugIn:
case Target::unspecified:
default:
break;
}
@ -961,9 +1003,9 @@ private:
writeCompilerFlagSchemes (out, filesToCompile);
auto getFilesForTarget = [] (const Array<std::pair<File, String>>& files,
MakefileTarget* target,
const Project& p) -> Array<std::pair<File, String>>
auto getFilesForTarget = [this] (const Array<std::pair<File, String>>& files,
MakefileTarget* target,
const Project& p) -> Array<std::pair<File, String>>
{
Array<std::pair<File, String>> targetFiles;
@ -973,6 +1015,9 @@ private:
if (p.getTargetTypeFromFilePath (f.first, true) == targetType)
targetFiles.add (f);
if (targetType == MakefileTarget::LV2TurtleProgram)
targetFiles.add ({ project.resolveFilename (getLV2TurtleDumpProgramSource().toUnixStyle()), {} });
return targetFiles;
};

View file

@ -260,25 +260,29 @@ public:
bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
{
using Target = build_tools::ProjectType::Target;
switch (type)
{
case build_tools::ProjectType::Target::AudioUnitv3PlugIn:
case build_tools::ProjectType::Target::StandalonePlugIn:
case build_tools::ProjectType::Target::GUIApp:
case build_tools::ProjectType::Target::StaticLibrary:
case build_tools::ProjectType::Target::DynamicLibrary:
case build_tools::ProjectType::Target::SharedCodeTarget:
case build_tools::ProjectType::Target::AggregateTarget:
case Target::AudioUnitv3PlugIn:
case Target::StandalonePlugIn:
case Target::GUIApp:
case Target::StaticLibrary:
case Target::DynamicLibrary:
case Target::SharedCodeTarget:
case Target::AggregateTarget:
return true;
case build_tools::ProjectType::Target::ConsoleApp:
case build_tools::ProjectType::Target::VSTPlugIn:
case build_tools::ProjectType::Target::VST3PlugIn:
case build_tools::ProjectType::Target::AAXPlugIn:
case build_tools::ProjectType::Target::RTASPlugIn:
case build_tools::ProjectType::Target::AudioUnitPlugIn:
case build_tools::ProjectType::Target::UnityPlugIn:
case Target::ConsoleApp:
case Target::VSTPlugIn:
case Target::VST3PlugIn:
case Target::AAXPlugIn:
case Target::RTASPlugIn:
case Target::AudioUnitPlugIn:
case Target::UnityPlugIn:
case Target::LV2PlugIn:
case Target::LV2TurtleProgram:
return ! iOS;
case build_tools::ProjectType::Target::unspecified:
case Target::unspecified:
default:
break;
}
@ -800,7 +804,8 @@ protected:
auBinaryLocation (config, Ids::auBinaryLocation, getUndoManager(), "$(HOME)/Library/Audio/Plug-Ins/Components/"),
rtasBinaryLocation (config, Ids::rtasBinaryLocation, getUndoManager(), "/Library/Application Support/Digidesign/Plug-Ins/"),
aaxBinaryLocation (config, Ids::aaxBinaryLocation, getUndoManager(), "/Library/Application Support/Avid/Audio/Plug-Ins/"),
unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager())
unityPluginBinaryLocation (config, Ids::unityPluginBinaryLocation, getUndoManager()),
lv2BinaryLocation (config, Ids::lv2BinaryLocation, getUndoManager(), "$(HOME)/Library/Audio/Plug-Ins/LV2/")
{
updateOldPluginBinaryLocations();
updateOldSDKDefaults();
@ -896,6 +901,7 @@ protected:
String getRTASBinaryLocationString() const { return rtasBinaryLocation.get();}
String getAAXBinaryLocationString() const { return aaxBinaryLocation.get();}
String getUnityPluginBinaryLocationString() const { return unityPluginBinaryLocation.get(); }
String getLV2PluginBinaryLocationString() const { return lv2BinaryLocation.get(); }
private:
//==============================================================================
@ -905,7 +911,7 @@ protected:
customXcodeFlags, plistPreprocessorDefinitions, codeSignIdentity,
fastMathEnabled, stripLocalSymbolsEnabled, pluginBinaryCopyStepEnabled,
vstBinaryLocation, vst3BinaryLocation, auBinaryLocation, rtasBinaryLocation,
aaxBinaryLocation, unityPluginBinaryLocation;
aaxBinaryLocation, unityPluginBinaryLocation, lv2BinaryLocation;
//==============================================================================
void valueTreePropertyChanged (ValueTree&, const Identifier& property) override
@ -957,6 +963,11 @@ protected:
1024, false),
"The folder in which the compiled AAX binary should be placed.");
if (project.shouldBuildLV2())
props.add (new TextPropertyComponentWithEnablement (lv2BinaryLocation, pluginBinaryCopyStepEnabled, "LV2 Binary Location",
1024, false),
"The folder in which the compiled LV2 binary should be placed.");
if (project.shouldBuildUnityPlugin())
props.add (new TextPropertyComponentWithEnablement (unityPluginBinaryLocation, pluginBinaryCopyStepEnabled, "Unity Binary Location",
1024, false),
@ -1041,6 +1052,7 @@ public:
break;
case ConsoleApp:
case LV2TurtleProgram:
xcodeFileType = "compiled.mach-o.executable";
xcodeBundleExtension = String();
xcodeProductType = "com.apple.product-type.tool";
@ -1122,6 +1134,13 @@ public:
xcodeCopyToProductInstallPathAfterBuild = true;
break;
case LV2PlugIn:
xcodeFileType = "compiled.mach-o.executable";
xcodeProductType = "com.apple.product-type.tool";
xcodeBundleExtension = ".so";
xcodeCopyToProductInstallPathAfterBuild = true;
break;
case SharedCodeTarget:
xcodeFileType = "archive.ar";
xcodeBundleExtension = ".a";
@ -1173,43 +1192,6 @@ public:
String mainBuildProductID;
File infoPlistFile;
struct SourceFileInfo
{
build_tools::RelativePath path;
bool shouldBeCompiled = false;
};
Array<SourceFileInfo> getSourceFilesInfo (const Project::Item& projectItem) const
{
Array<SourceFileInfo> result;
auto targetType = (owner.getProject().isAudioPluginProject() ? type : SharedCodeTarget);
if (projectItem.isGroup())
{
for (int i = 0; i < projectItem.getNumChildren(); ++i)
result.addArray (getSourceFilesInfo (projectItem.getChild (i)));
}
else if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (owner)
&& owner.getProject().getTargetTypeFromFilePath (projectItem.getFile(), true) == targetType)
{
SourceFileInfo info;
info.path = build_tools::RelativePath (projectItem.getFile(),
owner.getTargetFolder(),
build_tools::RelativePath::buildTargetFolder);
jassert (info.path.getRoot() == build_tools::RelativePath::buildTargetFolder);
if (targetType == SharedCodeTarget || projectItem.shouldBeCompiled())
info.shouldBeCompiled = projectItem.shouldBeCompiled();
result.add (info);
}
return result;
}
//==============================================================================
void addMainBuildProduct() const
{
@ -1218,12 +1200,18 @@ public:
if (ProjectExporter::BuildConfiguration::Ptr config = owner.getConfiguration (0))
{
auto productName = owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString (type == UnityPlugIn));
const auto productName = [&]() -> String
{
const auto binaryName = owner.replacePreprocessorTokens (*config, config->getTargetBinaryNameString (type == UnityPlugIn));
if (xcodeFileType == "archive.ar")
productName = getStaticLibbedFilename (productName);
else
productName += xcodeBundleExtension;
if (xcodeFileType == "archive.ar")
return getStaticLibbedFilename (binaryName);
if (type == LV2TurtleProgram)
return Project::getLV2FileWriterName();
return binaryName + xcodeBundleExtension;
}();
addBuildProduct (xcodeFileType, productName);
}
@ -1274,6 +1262,18 @@ public:
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));
}
else if (type == XcodeTarget::LV2TurtleProgram)
{
// No thanks
}
else if (type != XcodeTarget::SharedCodeTarget) // shared code doesn't depend on anything; all other targets depend only on the shared code
{
if (auto* sharedCodeTarget = owner.getTargetOfType (XcodeTarget::SharedCodeTarget))
@ -1429,6 +1429,27 @@ public:
return mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type));
}
String getConfigurationBuildDir (const XcodeBuildConfiguration& config) const
{
const String configurationBuildDir ("$(PROJECT_DIR)/build/$(CONFIGURATION)");
if (config.getTargetBinaryRelativePathString().isEmpty())
return configurationBuildDir;
// a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild
// (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!)
jassert (! xcodeCopyToProductInstallPathAfterBuild);
build_tools::RelativePath binaryPath (config.getTargetBinaryRelativePathString(),
build_tools::RelativePath::projectFolder);
return expandPath (binaryPath.rebased (owner.projectFolder,
owner.getTargetFolder(),
build_tools::RelativePath::buildTargetFolder).toUnixStyle());
}
String getLV2BundleName() const { return owner.project.getPluginNameString() + ".lv2"; }
//==============================================================================
StringPairArray getTargetSettings (const XcodeBuildConfiguration& config) const
{
@ -1444,7 +1465,15 @@ public:
return s;
}
s.set ("PRODUCT_NAME", owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString (type == UnityPlugIn)).quoted());
const auto productName = [&]
{
if (type == LV2TurtleProgram)
return Project::getLV2FileWriterName().quoted();
return owner.replacePreprocessorTokens (config, config.getTargetBinaryNameString (type == UnityPlugIn)).quoted();
}();
s.set ("PRODUCT_NAME", productName);
s.set ("PRODUCT_BUNDLE_IDENTIFIER", getBundleIdentifier());
auto arch = (! owner.isiOS() && type == Target::AudioUnitv3PlugIn) ? macOSArch_64Bit
@ -1598,7 +1627,7 @@ public:
{
s.set ("INSTALL_PATH", installPath.quoted());
if (type == Target::SharedCodeTarget)
if (type == Target::SharedCodeTarget || type == Target::LV2PlugIn)
s.set ("SKIP_INSTALL", "YES");
if (! owner.embeddedFrameworkIDs.isEmpty())
@ -1621,24 +1650,11 @@ public:
if (xcodeOtherRezFlags.isNotEmpty())
s.set ("OTHER_REZFLAGS", "\"" + xcodeOtherRezFlags + "\"");
String configurationBuildDir ("$(PROJECT_DIR)/build/$(CONFIGURATION)");
const auto configurationBuildDir = getConfigurationBuildDir (config);
const auto adjustedConfigBuildDir = type == LV2PlugIn ? configurationBuildDir + "/" + getLV2BundleName()
: configurationBuildDir;
if (config.getTargetBinaryRelativePathString().isNotEmpty())
{
// a target's position can either be defined via installPath + xcodeCopyToProductInstallPathAfterBuild
// (= for audio plug-ins) or using a custom binary path (for everything else), but not both (= conflict!)
jassert (! xcodeCopyToProductInstallPathAfterBuild);
build_tools::RelativePath binaryPath (config.getTargetBinaryRelativePathString(),
build_tools::RelativePath::projectFolder);
configurationBuildDir = expandPath (binaryPath.rebased (owner.projectFolder,
owner.getTargetFolder(),
build_tools::RelativePath::buildTargetFolder)
.toUnixStyle());
}
s.set ("CONFIGURATION_BUILD_DIR", addQuotesIfRequired (configurationBuildDir));
s.set ("CONFIGURATION_BUILD_DIR", addQuotesIfRequired (adjustedConfigBuildDir));
if (owner.isHardenedRuntimeEnabled())
s.set ("ENABLE_HARDENED_RUNTIME", "YES");
@ -1703,6 +1719,10 @@ public:
s.set ("OTHER_LDFLAGS", linkerFlags.joinIntoString (" ").quoted());
librarySearchPaths.addArray (config.getLibrarySearchPaths());
if (type == LV2PlugIn)
librarySearchPaths.add (configurationBuildDir);
librarySearchPaths = getCleanedStringArray (librarySearchPaths);
if (librarySearchPaths.size() > 0)
@ -1777,8 +1797,10 @@ public:
case RTASPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getRTASBinaryLocationString() : String();
case AAXPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getAAXBinaryLocationString() : String();
case UnityPlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getUnityPluginBinaryLocationString() : String();
case LV2PlugIn: return config.isPluginBinaryCopyStepEnabled() ? config.getLV2PluginBinaryLocationString() : String();
case SharedCodeTarget: return owner.isiOS() ? "@executable_path/Frameworks" : "@executable_path/../Frameworks";
case StaticLibrary:
case LV2TurtleProgram:
case DynamicLibrary:
case AudioUnitv3PlugIn:
case StandalonePlugIn:
@ -1794,7 +1816,7 @@ public:
if (getTargetFileType() == pluginBundle)
flags.add (owner.isiOS() ? "-bitcode_bundle" : "-bundle");
if (type != Target::SharedCodeTarget)
if (type != Target::SharedCodeTarget && type != Target::LV2TurtleProgram)
{
Array<build_tools::RelativePath> extraLibs;
@ -2141,6 +2163,18 @@ private:
target->addMainBuildProduct();
if (target->type == XcodeTarget::LV2TurtleProgram
&& project.getEnabledModules().isModuleEnabled ("juce_audio_plugin_client"))
{
const auto path = getLV2TurtleDumpProgramSource();
addFile (FileOptions().withRelativePath ({ expandPath (path.toUnixStyle()), path.getRoot() })
.withSkipPCHEnabled (true)
.withCompilationEnabled (true)
.withInhibitWarningsEnabled (true)
.withCompilerFlags ("-std=c++11")
.withXcodeTarget (target));
}
auto targetName = String (target->getName());
auto fileID = createID (targetName + "__targetbuildref");
auto fileRefID = createID ("__productFileID" + targetName);
@ -2289,7 +2323,10 @@ private:
{
auto skipAUv3 = (target->type == XcodeTarget::AudioUnitv3PlugIn && ! shouldDuplicateAppExResourcesFolder());
if (! projectType.isStaticLibrary() && target->type != XcodeTarget::SharedCodeTarget && ! skipAUv3)
if (! projectType.isStaticLibrary()
&& target->type != XcodeTarget::SharedCodeTarget
&& target->type != XcodeTarget::LV2TurtleProgram
&& ! skipAUv3)
target->addBuildPhase ("PBXResourcesBuildPhase", resourceIDs);
auto rezFiles = rezFileIDs;
@ -2306,10 +2343,35 @@ private:
target->addBuildPhase ("PBXSourcesBuildPhase", sourceFiles);
if (! projectType.isStaticLibrary() && target->type != XcodeTarget::SharedCodeTarget)
if (! projectType.isStaticLibrary()
&& target->type != XcodeTarget::SharedCodeTarget
&& target->type != XcodeTarget::LV2TurtleProgram)
target->addBuildPhase ("PBXFrameworksBuildPhase", target->frameworkIDs);
}
if (target->type == XcodeTarget::LV2PlugIn)
{
auto script = "set -e\n\"$CONFIGURATION_BUILD_DIR/../"
+ Project::getLV2FileWriterName()
+ "\" \"$CONFIGURATION_BUILD_DIR/$PRODUCT_NAME\"\n";
for (ConstConfigIterator config (*this); config.next();)
{
auto& xcodeConfig = dynamic_cast<const XcodeBuildConfiguration&> (*config);
const auto installPath = target->getInstallPathForConfiguration (xcodeConfig);
if (installPath.isNotEmpty())
{
script << "if [ \"$CONFIGURATION\" = \"" << config->getName()
<< "\" ]; then\n /bin/ln -sfh \"$CONFIGURATION_BUILD_DIR\" \""
<< installPath.replace ("$(HOME)", "$HOME") << '/' << target->getLV2BundleName()
<< "\"\nfi\n";
}
}
target->addShellScriptBuildPhase ("Generate manifest", script);
}
target->addShellScriptBuildPhase ("Post-build script", getPostBuildScript());
if (project.isAudioPluginProject() && project.shouldBuildAUv3()

View file

@ -364,7 +364,7 @@ void ProjectExporter::addExtraIncludePathsIfPluginOrHost()
const auto lv2BasePath = getModuleFolderRelativeToProject ("juce_audio_processors").getChildFile ("format_types")
.getChildFile ("LV2_SDK");
if (project.isLV2PluginHost())
if ((shouldBuildTargetType (Target::LV2PlugIn) && project.shouldBuildLV2()) || project.isLV2PluginHost())
{
const std::vector<const char*> paths[] { { "" },
{ "lv2" },
@ -455,16 +455,16 @@ StringPairArray ProjectExporter::getAllPreprocessorDefs() const
void ProjectExporter::addTargetSpecificPreprocessorDefs (StringPairArray& defs, const build_tools::ProjectType::Target::Type targetType) const
{
std::pair<String, build_tools::ProjectType::Target::Type> targetFlags[] = {
{"JucePlugin_Build_VST", build_tools::ProjectType::Target::VSTPlugIn},
{"JucePlugin_Build_VST3", build_tools::ProjectType::Target::VST3PlugIn},
{"JucePlugin_Build_AU", build_tools::ProjectType::Target::AudioUnitPlugIn},
{"JucePlugin_Build_AUv3", build_tools::ProjectType::Target::AudioUnitv3PlugIn},
{"JucePlugin_Build_RTAS", build_tools::ProjectType::Target::RTASPlugIn},
{"JucePlugin_Build_AAX", build_tools::ProjectType::Target::AAXPlugIn},
{"JucePlugin_Build_Standalone", build_tools::ProjectType::Target::StandalonePlugIn},
{"JucePlugin_Build_Unity", build_tools::ProjectType::Target::UnityPlugIn}
};
using Target = build_tools::ProjectType::Target::Type;
const std::pair<const char*, Target> targetFlags[] { { "JucePlugin_Build_VST", Target::VSTPlugIn },
{ "JucePlugin_Build_VST3", Target::VST3PlugIn },
{ "JucePlugin_Build_AU", Target::AudioUnitPlugIn },
{ "JucePlugin_Build_AUv3", Target::AudioUnitv3PlugIn },
{ "JucePlugin_Build_RTAS", Target::RTASPlugIn },
{ "JucePlugin_Build_AAX", Target::AAXPlugIn },
{ "JucePlugin_Build_Standalone", Target::StandalonePlugIn },
{ "JucePlugin_Build_Unity", Target::UnityPlugIn },
{ "JucePlugin_Build_LV2", Target::LV2PlugIn } };
if (targetType == build_tools::ProjectType::Target::SharedCodeTarget)
{

View file

@ -181,6 +181,13 @@ public:
void createPropertyEditors (PropertyListBuilder&);
void addSettingsForProjectType (const build_tools::ProjectType&);
build_tools::RelativePath getLV2TurtleDumpProgramSource() const
{
return getModuleFolderRelativeToProject ("juce_audio_plugin_client")
.getChildFile ("LV2")
.getChildFile ("juce_LV2TurtleDumpProgram.cpp");
}
//==============================================================================
void copyMainGroupFromProject();
Array<Project::Item>& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; }
@ -460,7 +467,6 @@ private:
: name + suffix;
}
void createDependencyPathProperties (PropertyListBuilder&);
void createIconProperties (PropertyListBuilder&);
void addExtraIncludePathsIfPluginOrHost();
void addCommonAudioPluginSettings();

View file

@ -74,6 +74,7 @@ Result ProjectSaver::saveResourcesOnly()
void ProjectSaver::saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent)
{
writeLV2DefinesFile();
writePluginDefines();
writeAppConfigFile (modules, appConfigUserContent);
writeBinaryDataFiles();
@ -495,6 +496,17 @@ void ProjectSaver::writeAppConfigFile (const OwnedArray<LibraryModule>& modules,
});
}
void ProjectSaver::writeLV2DefinesFile()
{
if (! project.shouldBuildLV2())
return;
writeOrRemoveGeneratedFile (Project::getJuceLV2DefinesFilename(), [&] (MemoryOutputStream& mem)
{
writeLV2Defines (mem);
});
}
void ProjectSaver::writeAppHeader (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules)
{
writeAutoGenWarningComment (out);
@ -638,6 +650,21 @@ void ProjectSaver::writeBinaryDataFiles()
}
}
void ProjectSaver::writeLV2Defines (MemoryOutputStream& mem)
{
String templateFile { BinaryData::JuceLV2Defines_h_in };
const auto isValidUri = [&] (const String& text) { return URL (text).isWellFormed(); };
if (! isValidUri (project.getLV2URI()))
{
addError ("LV2 URI is malformed.");
return;
}
mem << templateFile.replace ("${JUCE_LV2URI}", project.getLV2URI());
}
void ProjectSaver::writeReadmeFile()
{
MemoryOutputStream out;

View file

@ -96,6 +96,7 @@ private:
void writePluginDefines (MemoryOutputStream& outStream) const;
void writePluginDefines();
void writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent);
void writeLV2Defines (MemoryOutputStream&);
void writeProjectFile();
void writeAppConfig (MemoryOutputStream& outStream, const OwnedArray<LibraryModule>& modules, const String& userContent);
@ -107,6 +108,7 @@ private:
void writePluginCharacteristicsFile();
void writeUnityScriptFile();
void writeProjects (const OwnedArray<LibraryModule>&, ProjectExporter*);
void writeLV2DefinesFile();
void runPostExportScript();
void saveExporter (ProjectExporter& exporter, const OwnedArray<LibraryModule>& modules);

View file

@ -332,6 +332,7 @@ namespace Ids
DECLARE_ID (buildAAX);
DECLARE_ID (buildStandalone);
DECLARE_ID (buildUnity);
DECLARE_ID (buildLV2);
DECLARE_ID (enableIAA);
DECLARE_ID (pluginName);
DECLARE_ID (pluginDesc);
@ -376,6 +377,9 @@ namespace Ids
DECLARE_ID (guiEditorEnabled);
DECLARE_ID (jucerFormatVersion);
DECLARE_ID (buildNumber);
DECLARE_ID (lv2Uri);
DECLARE_ID (lv2UriUi);
DECLARE_ID (lv2BinaryLocation);
DECLARE_ID (osxSDK);
DECLARE_ID (osxCompatibility);