git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
1877
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h
vendored
Normal file
1877
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Android.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1216
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h
vendored
Normal file
1216
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CLion.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
829
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h
vendored
Normal file
829
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_CodeBlocks.h
vendored
Normal file
@ -0,0 +1,829 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_ProjectExport_MSVC.h"
|
||||
|
||||
//==============================================================================
|
||||
class CodeBlocksProjectExporter : public ProjectExporter
|
||||
{
|
||||
public:
|
||||
enum CodeBlocksOS
|
||||
{
|
||||
windowsTarget,
|
||||
linuxTarget
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String getDisplayNameWindows() { return "Code::Blocks (Windows)"; }
|
||||
static String getDisplayNameLinux() { return "Code::Blocks (Linux)"; }
|
||||
|
||||
static String getValueTreeTypeNameWindows() { return "CODEBLOCKS_WINDOWS"; }
|
||||
static String getValueTreeTypeNameLinux() { return "CODEBLOCKS_LINUX"; }
|
||||
|
||||
static String getTargetFolderNameWindows() { return "CodeBlocksWindows"; }
|
||||
static String getTargetFolderNameLinux() { return "CodeBlocksLinux"; }
|
||||
|
||||
//==============================================================================
|
||||
static CodeBlocksProjectExporter* createForSettings (Project& projectToUse, const ValueTree& settingsToUse)
|
||||
{
|
||||
// this will also import legacy jucer files where CodeBlocks only worked for Windows,
|
||||
// had valueTreetTypeName "CODEBLOCKS", and there was no OS distinction
|
||||
if (settingsToUse.hasType (getValueTreeTypeNameWindows()) || settingsToUse.hasType ("CODEBLOCKS"))
|
||||
return new CodeBlocksProjectExporter (projectToUse, settingsToUse, windowsTarget);
|
||||
|
||||
if (settingsToUse.hasType (getValueTreeTypeNameLinux()))
|
||||
return new CodeBlocksProjectExporter (projectToUse, settingsToUse, linuxTarget);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
CodeBlocksProjectExporter (Project& p, const ValueTree& t, CodeBlocksOS codeBlocksOs)
|
||||
: ProjectExporter (p, t), os (codeBlocksOs)
|
||||
{
|
||||
if (isWindows())
|
||||
{
|
||||
name = getDisplayNameWindows();
|
||||
targetLocationValue.setDefault (getDefaultBuildsRootFolder() + getTargetFolderNameWindows());
|
||||
targetPlatformValue.referTo (settings, Ids::codeBlocksWindowsTarget, getUndoManager());
|
||||
}
|
||||
else
|
||||
{
|
||||
name = getDisplayNameLinux();
|
||||
targetLocationValue.setDefault (getDefaultBuildsRootFolder() + getTargetFolderNameLinux());
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool canLaunchProject() override { return false; }
|
||||
bool launchProject() override { return false; }
|
||||
bool usesMMFiles() const override { return false; }
|
||||
bool canCopeWithDuplicateFiles() override { return false; }
|
||||
bool supportsUserDefinedConfigurations() const override { return true; }
|
||||
|
||||
bool isXcode() const override { return false; }
|
||||
bool isVisualStudio() const override { return false; }
|
||||
bool isCodeBlocks() const override { return true; }
|
||||
bool isMakefile() const override { return false; }
|
||||
bool isAndroidStudio() const override { return false; }
|
||||
bool isCLion() const override { return false; }
|
||||
|
||||
bool isAndroid() const override { return false; }
|
||||
bool isWindows() const override { return os == windowsTarget; }
|
||||
bool isLinux() const override { return os == linuxTarget; }
|
||||
bool isOSX() const override { return false; }
|
||||
bool isiOS() const override { return false; }
|
||||
|
||||
Identifier getExporterIdentifier() const override
|
||||
{
|
||||
return isLinux() ? getValueTreeTypeNameLinux() : getValueTreeTypeNameWindows();
|
||||
}
|
||||
|
||||
String getNewLineString() const override { return isWindows() ? "\r\n" : "\n"; }
|
||||
|
||||
bool supportsTargetType (build_tools::ProjectType::Target::Type type) const override
|
||||
{
|
||||
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:
|
||||
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:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void createExporterProperties (PropertyListBuilder& props) override
|
||||
{
|
||||
if (isWindows())
|
||||
{
|
||||
props.add (new ChoicePropertyComponent (targetPlatformValue, "Target platform",
|
||||
{ "Windows NT 4.0", "Windows 2000", "Windows XP", "Windows Server 2003", "Windows Vista", "Windows Server 2008",
|
||||
"Windows 7", "Windows 8", "Windows 8.1", "Windows 10" },
|
||||
{ "0x0400", "0x0500", "0x0501", "0x0502", "0x0600", "0x0600",
|
||||
"0x0601", "0x0602", "0x0603", "0x0A00" }),
|
||||
"This sets the preprocessor macro WINVER to an appropriate value for the corresponding platform.");
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void create (const OwnedArray<LibraryModule>&) const override
|
||||
{
|
||||
auto cbpFile = getTargetFolder().getChildFile (project.getProjectFilenameRootString())
|
||||
.withFileExtension (".cbp");
|
||||
|
||||
XmlElement xml ("CodeBlocks_project_file");
|
||||
addVersion (xml);
|
||||
createProject (*xml.createNewChildElement ("Project"));
|
||||
writeXmlOrThrow (xml, cbpFile, "UTF-8", 10, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType&) override
|
||||
{
|
||||
// add shared code target first as order matters for Codeblocks
|
||||
if (shouldBuildTargetType (build_tools::ProjectType::Target::SharedCodeTarget))
|
||||
targets.add (new CodeBlocksTarget (*this, build_tools::ProjectType::Target::SharedCodeTarget));
|
||||
|
||||
//resource::ProjectType::Target::SharedCodeTarget
|
||||
callForAllSupportedTargets ([this] (build_tools::ProjectType::Target::Type targetType)
|
||||
{
|
||||
if (targetType == build_tools::ProjectType::Target::SharedCodeTarget)
|
||||
return;
|
||||
|
||||
targets.insert (targetType == build_tools::ProjectType::Target::AggregateTarget ? 0 : -1,
|
||||
new CodeBlocksTarget (*this, targetType));
|
||||
});
|
||||
|
||||
// If you hit this assert, you tried to generate a project for an exporter
|
||||
// that does not support any of your targets!
|
||||
jassert (targets.size() > 0);
|
||||
}
|
||||
|
||||
void initialiseDependencyPathValues() override
|
||||
{
|
||||
auto targetOS = isWindows() ? TargetOS::windows : TargetOS::linux;
|
||||
|
||||
vstLegacyPathValueWrapper.init ({ settings, Ids::vstLegacyFolder, nullptr },
|
||||
getAppSettings().getStoredPath (Ids::vstLegacyPath, targetOS), targetOS);
|
||||
}
|
||||
|
||||
private:
|
||||
ValueWithDefault targetPlatformValue;
|
||||
|
||||
String getTargetPlatformString() const { return targetPlatformValue.get(); }
|
||||
|
||||
//==============================================================================
|
||||
class CodeBlocksBuildConfiguration : public BuildConfiguration
|
||||
{
|
||||
public:
|
||||
CodeBlocksBuildConfiguration (Project& p, const ValueTree& settings, const ProjectExporter& e)
|
||||
: BuildConfiguration (p, settings, e),
|
||||
architectureTypeValue (config, exporter.isWindows() ? Ids::windowsCodeBlocksArchitecture
|
||||
: Ids::linuxCodeBlocksArchitecture, getUndoManager(), "-m64")
|
||||
{
|
||||
linkTimeOptimisationValue.setDefault (false);
|
||||
optimisationLevelValue.setDefault (isDebug() ? gccO0 : gccO3);
|
||||
}
|
||||
|
||||
void createConfigProperties (PropertyListBuilder& props) override
|
||||
{
|
||||
addRecommendedLinuxCompilerWarningsProperty (props);
|
||||
addGCCOptimisationProperty (props);
|
||||
|
||||
props.add (new ChoicePropertyComponent (architectureTypeValue, "Architecture",
|
||||
{ "32-bit (-m32)", "64-bit (-m64)", "ARM v6", "ARM v7" },
|
||||
{ "-m32", "-m64", "-march=armv6", "-march=armv7" }),
|
||||
"Specifies the 32/64-bit architecture to use.");
|
||||
}
|
||||
|
||||
String getModuleLibraryArchName() const override
|
||||
{
|
||||
auto archFlag = getArchitectureTypeString();
|
||||
String prefix ("-march=");
|
||||
|
||||
if (archFlag.startsWith (prefix))
|
||||
return archFlag.substring (prefix.length());
|
||||
else if (archFlag == "-m64")
|
||||
return "x86_64";
|
||||
else if (archFlag == "-m32")
|
||||
return "i386";
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
String getArchitectureTypeString() const { return architectureTypeValue.get(); }
|
||||
|
||||
//==============================================================================
|
||||
ValueWithDefault architectureTypeValue;
|
||||
};
|
||||
|
||||
BuildConfiguration::Ptr createBuildConfig (const ValueTree& tree) const override
|
||||
{
|
||||
return *new CodeBlocksBuildConfiguration (project, tree, *this);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class CodeBlocksTarget : public build_tools::ProjectType::Target
|
||||
{
|
||||
public:
|
||||
CodeBlocksTarget (const CodeBlocksProjectExporter& e,
|
||||
build_tools::ProjectType::Target::Type typeToUse)
|
||||
: Target (typeToUse),
|
||||
exporter (e)
|
||||
{}
|
||||
|
||||
String getTargetNameForConfiguration (const BuildConfiguration& config) const
|
||||
{
|
||||
if (type == build_tools::ProjectType::Target::AggregateTarget)
|
||||
return config.getName();
|
||||
|
||||
return getName() + String (" | ") + config.getName();
|
||||
}
|
||||
|
||||
String getTargetSuffix() const
|
||||
{
|
||||
auto fileType = getTargetFileType();
|
||||
|
||||
if (exporter.isWindows())
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case executable: return ".exe";
|
||||
case staticLibrary: return ".lib";
|
||||
case sharedLibraryOrDLL:
|
||||
case pluginBundle: return ".dll";
|
||||
case macOSAppex:
|
||||
case unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case executable: return {};
|
||||
case staticLibrary: return ".a";
|
||||
case pluginBundle:
|
||||
case sharedLibraryOrDLL: return ".so";
|
||||
case macOSAppex:
|
||||
case unknown:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isDynamicLibrary() const
|
||||
{
|
||||
return (type == DynamicLibrary || type == VSTPlugIn);
|
||||
}
|
||||
|
||||
const CodeBlocksProjectExporter& exporter;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
void addVersion (XmlElement& xml) const
|
||||
{
|
||||
auto* fileVersion = xml.createNewChildElement ("FileVersion");
|
||||
fileVersion->setAttribute ("major", 1);
|
||||
fileVersion->setAttribute ("minor", 6);
|
||||
}
|
||||
|
||||
void addOptions (XmlElement& xml) const
|
||||
{
|
||||
xml.createNewChildElement ("Option")->setAttribute ("title", project.getProjectNameString());
|
||||
xml.createNewChildElement ("Option")->setAttribute ("pch_mode", 2);
|
||||
xml.createNewChildElement ("Option")->setAttribute ("compiler", "gcc");
|
||||
}
|
||||
|
||||
StringArray getDefines (const BuildConfiguration& config, CodeBlocksTarget& target) const
|
||||
{
|
||||
StringPairArray defines;
|
||||
|
||||
if (isWindows())
|
||||
{
|
||||
defines.set ("__MINGW__", "1");
|
||||
defines.set ("__MINGW_EXTENSION", {});
|
||||
|
||||
auto targetPlatform = getTargetPlatformString();
|
||||
|
||||
if (targetPlatform.isNotEmpty())
|
||||
defines.set ("WINVER", targetPlatform);
|
||||
}
|
||||
else
|
||||
{
|
||||
defines.set ("LINUX", "1");
|
||||
}
|
||||
|
||||
if (config.isDebug())
|
||||
{
|
||||
defines.set ("DEBUG", "1");
|
||||
defines.set ("_DEBUG", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
defines.set ("NDEBUG", "1");
|
||||
}
|
||||
|
||||
defines = mergePreprocessorDefs (defines, getAllPreprocessorDefs (config, target.type));
|
||||
|
||||
StringArray defs;
|
||||
auto keys = defines.getAllKeys();
|
||||
auto values = defines.getAllValues();
|
||||
|
||||
const auto escapedQuote = isWindows() ? "\\\"" : "\\\\\"";
|
||||
|
||||
for (int i = 0; i < defines.size(); ++i)
|
||||
{
|
||||
auto result = keys[i];
|
||||
|
||||
if (values[i].isNotEmpty())
|
||||
result += "=\"" + values[i].replace ("\"", escapedQuote) + "\"";
|
||||
|
||||
defs.add (result);
|
||||
}
|
||||
|
||||
return getCleanedStringArray (defs);
|
||||
}
|
||||
|
||||
StringArray getCompilerFlags (const BuildConfiguration& config, CodeBlocksTarget& target) const
|
||||
{
|
||||
StringArray flags;
|
||||
|
||||
if (auto* codeBlocksConfig = dynamic_cast<const CodeBlocksBuildConfiguration*> (&config))
|
||||
flags.add (codeBlocksConfig->getArchitectureTypeString());
|
||||
|
||||
auto recommendedFlags = config.getRecommendedCompilerWarningFlags();
|
||||
|
||||
for (auto& recommendedFlagsType : { recommendedFlags.common, recommendedFlags.cpp })
|
||||
for (auto& recommended : recommendedFlagsType)
|
||||
flags.add (recommended);
|
||||
|
||||
flags.add ("-O" + config.getGCCOptimisationFlag());
|
||||
|
||||
if (config.isLinkTimeOptimisationEnabled())
|
||||
flags.add ("-flto");
|
||||
|
||||
{
|
||||
auto cppStandard = config.project.getCppStandardString();
|
||||
|
||||
if (cppStandard == "latest")
|
||||
cppStandard = project.getLatestNumberedCppStandardString();
|
||||
|
||||
flags.add ("-std=" + String (shouldUseGNUExtensions() ? "gnu++" : "c++") + cppStandard);
|
||||
}
|
||||
|
||||
flags.add ("-mstackrealign");
|
||||
|
||||
if (config.isDebug())
|
||||
flags.add ("-g");
|
||||
|
||||
flags.addTokens (replacePreprocessorTokens (config, getExtraCompilerFlagsString()).trim(),
|
||||
" \n", "\"'");
|
||||
|
||||
if (config.exporter.isLinux())
|
||||
{
|
||||
if (target.isDynamicLibrary() || getProject().isAudioPluginProject())
|
||||
flags.add ("-fPIC");
|
||||
|
||||
auto packages = config.exporter.getLinuxPackages (PackageDependencyType::compile);
|
||||
|
||||
if (! packages.isEmpty())
|
||||
{
|
||||
auto pkgconfigFlags = String ("`pkg-config --cflags");
|
||||
|
||||
for (auto& p : packages)
|
||||
pkgconfigFlags << " " << p;
|
||||
|
||||
pkgconfigFlags << "`";
|
||||
flags.add (pkgconfigFlags);
|
||||
}
|
||||
|
||||
if (linuxLibs.contains ("pthread"))
|
||||
flags.add ("-pthread");
|
||||
}
|
||||
|
||||
return getCleanedStringArray (flags);
|
||||
}
|
||||
|
||||
StringArray getLinkerFlags (const BuildConfiguration& config, CodeBlocksTarget& target) const
|
||||
{
|
||||
auto flags = makefileExtraLinkerFlags;
|
||||
|
||||
if (auto* codeBlocksConfig = dynamic_cast<const CodeBlocksBuildConfiguration*> (&config))
|
||||
flags.add (codeBlocksConfig->getArchitectureTypeString());
|
||||
|
||||
if (! config.isDebug())
|
||||
flags.add ("-s");
|
||||
|
||||
if (config.isLinkTimeOptimisationEnabled())
|
||||
flags.add ("-flto");
|
||||
|
||||
flags.addTokens (replacePreprocessorTokens (config, getExtraLinkerFlagsString()).trim(), " \n", "\"'");
|
||||
|
||||
if (config.exporter.isLinux())
|
||||
{
|
||||
if (target.isDynamicLibrary())
|
||||
flags.add ("-shared");
|
||||
|
||||
auto packages = config.exporter.getLinuxPackages (PackageDependencyType::link);
|
||||
|
||||
if (! packages.isEmpty())
|
||||
{
|
||||
String pkgconfigLibs ("`pkg-config --libs");
|
||||
|
||||
for (auto& p : packages)
|
||||
pkgconfigLibs << " " << p;
|
||||
|
||||
pkgconfigLibs << "`";
|
||||
flags.add (pkgconfigLibs);
|
||||
}
|
||||
}
|
||||
|
||||
return getCleanedStringArray (flags);
|
||||
}
|
||||
|
||||
StringArray getLinkerSearchPaths (const BuildConfiguration& config, CodeBlocksTarget& target) const
|
||||
{
|
||||
auto librarySearchPaths = config.getLibrarySearchPaths();
|
||||
|
||||
if (getProject().isAudioPluginProject() && target.type != build_tools::ProjectType::Target::SharedCodeTarget)
|
||||
librarySearchPaths.add (build_tools::RelativePath (getSharedCodePath (config), build_tools::RelativePath::buildTargetFolder).getParentDirectory().toUnixStyle().quoted());
|
||||
|
||||
return librarySearchPaths;
|
||||
}
|
||||
|
||||
StringArray getIncludePaths (const BuildConfiguration& config) const
|
||||
{
|
||||
StringArray paths;
|
||||
|
||||
paths.add (".");
|
||||
paths.addArray (extraSearchPaths);
|
||||
paths.addArray (config.getHeaderSearchPaths());
|
||||
|
||||
if (! isWindows())
|
||||
{
|
||||
paths.add ("/usr/include/freetype2");
|
||||
|
||||
// Replace ~ character with $(HOME) environment variable
|
||||
for (auto& path : paths)
|
||||
path = path.replace ("~", "$(HOME)");
|
||||
}
|
||||
|
||||
return getCleanedStringArray (paths);
|
||||
}
|
||||
|
||||
static int getTypeIndex (const build_tools::ProjectType::Target::Type& type)
|
||||
{
|
||||
if (type == build_tools::ProjectType::Target::GUIApp || type == build_tools::ProjectType::Target::StandalonePlugIn) return 0;
|
||||
if (type == build_tools::ProjectType::Target::ConsoleApp) return 1;
|
||||
if (type == build_tools::ProjectType::Target::StaticLibrary || type == build_tools::ProjectType::Target::SharedCodeTarget) return 2;
|
||||
if (type == build_tools::ProjectType::Target::DynamicLibrary || type == build_tools::ProjectType::Target::VSTPlugIn) return 3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
String getOutputPathForTarget (CodeBlocksTarget& target, const BuildConfiguration& config) const
|
||||
{
|
||||
String outputPath;
|
||||
if (config.getTargetBinaryRelativePathString().isNotEmpty())
|
||||
{
|
||||
build_tools::RelativePath binaryPath (config.getTargetBinaryRelativePathString(), build_tools::RelativePath::projectFolder);
|
||||
binaryPath = binaryPath.rebased (projectFolder, getTargetFolder(), build_tools::RelativePath::buildTargetFolder);
|
||||
outputPath = config.getTargetBinaryRelativePathString();
|
||||
}
|
||||
else
|
||||
{
|
||||
outputPath ="bin/" + File::createLegalFileName (config.getName().trim());
|
||||
}
|
||||
|
||||
return outputPath + "/" + replacePreprocessorTokens (config, config.getTargetBinaryNameString() + target.getTargetSuffix());
|
||||
}
|
||||
|
||||
String getSharedCodePath (const BuildConfiguration& config) const
|
||||
{
|
||||
auto outputPath = getOutputPathForTarget (getTargetWithType (build_tools::ProjectType::Target::SharedCodeTarget), config);
|
||||
build_tools::RelativePath path (outputPath, build_tools::RelativePath::buildTargetFolder);
|
||||
auto filename = path.getFileName();
|
||||
|
||||
if (isLinux())
|
||||
filename = "lib" + filename;
|
||||
|
||||
return path.getParentDirectory().getChildFile (filename).toUnixStyle();
|
||||
}
|
||||
|
||||
void createBuildTarget (XmlElement& xml, CodeBlocksTarget& target, const BuildConfiguration& config) const
|
||||
{
|
||||
xml.setAttribute ("title", target.getTargetNameForConfiguration (config));
|
||||
|
||||
{
|
||||
auto* output = xml.createNewChildElement ("Option");
|
||||
|
||||
output->setAttribute ("output", getOutputPathForTarget (target, config));
|
||||
|
||||
if (isLinux())
|
||||
{
|
||||
bool keepPrefix = (target.type == build_tools::ProjectType::Target::VSTPlugIn);
|
||||
|
||||
output->setAttribute ("prefix_auto", keepPrefix ? 0 : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
output->setAttribute ("prefix_auto", 0);
|
||||
}
|
||||
|
||||
output->setAttribute ("extension_auto", 0);
|
||||
}
|
||||
|
||||
xml.createNewChildElement ("Option")
|
||||
->setAttribute ("object_output", "obj/" + File::createLegalFileName (config.getName().trim()));
|
||||
|
||||
xml.createNewChildElement ("Option")->setAttribute ("type", getTypeIndex (target.type));
|
||||
xml.createNewChildElement ("Option")->setAttribute ("compiler", "gcc");
|
||||
|
||||
if (getProject().isAudioPluginProject() && target.type != build_tools::ProjectType::Target::SharedCodeTarget)
|
||||
xml.createNewChildElement ("Option")->setAttribute ("external_deps", getSharedCodePath (config));
|
||||
|
||||
{
|
||||
auto* compiler = xml.createNewChildElement ("Compiler");
|
||||
|
||||
{
|
||||
StringArray flags;
|
||||
|
||||
for (auto& def : getDefines (config, target))
|
||||
{
|
||||
if (! def.containsChar ('='))
|
||||
def << '=';
|
||||
|
||||
flags.add ("-D" + def);
|
||||
}
|
||||
|
||||
flags.addArray (getCompilerFlags (config, target));
|
||||
|
||||
for (auto flag : flags)
|
||||
setAddOption (*compiler, "option", flag);
|
||||
}
|
||||
|
||||
{
|
||||
auto includePaths = getIncludePaths (config);
|
||||
|
||||
for (auto path : includePaths)
|
||||
setAddOption (*compiler, "directory", path);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto* linker = xml.createNewChildElement ("Linker");
|
||||
|
||||
if (getProject().isAudioPluginProject() && target.type != build_tools::ProjectType::Target::SharedCodeTarget)
|
||||
setAddOption (*linker, "option", getSharedCodePath (config).quoted());
|
||||
|
||||
for (auto& flag : getLinkerFlags (config, target))
|
||||
setAddOption (*linker, "option", flag);
|
||||
|
||||
const StringArray& libs = isWindows() ? mingwLibs : linuxLibs;
|
||||
|
||||
for (auto lib : libs)
|
||||
setAddOption (*linker, "library", lib);
|
||||
|
||||
for (auto& path : getLinkerSearchPaths (config, target))
|
||||
setAddOption (*linker, "directory",
|
||||
build_tools::replacePreprocessorDefs (getAllPreprocessorDefs(), path));
|
||||
}
|
||||
}
|
||||
|
||||
void addBuild (XmlElement& xml) const
|
||||
{
|
||||
auto* build = xml.createNewChildElement ("Build");
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
for (auto target : targets)
|
||||
if (target->type != build_tools::ProjectType::Target::AggregateTarget)
|
||||
createBuildTarget (*build->createNewChildElement ("Target"), *target, *config);
|
||||
}
|
||||
|
||||
void addVirtualTargets (XmlElement& xml) const
|
||||
{
|
||||
auto* virtualTargets = xml.createNewChildElement ("VirtualTargets");
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
{
|
||||
StringArray allTargets;
|
||||
|
||||
for (auto target : targets)
|
||||
if (target->type != build_tools::ProjectType::Target::AggregateTarget)
|
||||
allTargets.add (target->getTargetNameForConfiguration (*config));
|
||||
|
||||
for (auto target : targets)
|
||||
{
|
||||
if (target->type == build_tools::ProjectType::Target::AggregateTarget)
|
||||
{
|
||||
auto* configTarget = virtualTargets->createNewChildElement ("Add");
|
||||
|
||||
configTarget->setAttribute ("alias", config->getName());
|
||||
configTarget->setAttribute ("targets", allTargets.joinIntoString (";"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringArray getProjectCompilerOptions() const
|
||||
{
|
||||
return { "-Wall", "-Wno-strict-aliasing", "-Wno-strict-overflow" };
|
||||
}
|
||||
|
||||
void addProjectCompilerOptions (XmlElement& xml) const
|
||||
{
|
||||
auto* compiler = xml.createNewChildElement ("Compiler");
|
||||
|
||||
for (auto& option : getProjectCompilerOptions())
|
||||
setAddOption (*compiler, "option", option);
|
||||
}
|
||||
|
||||
StringArray getProjectLinkerLibs() const
|
||||
{
|
||||
StringArray result;
|
||||
|
||||
if (isWindows())
|
||||
result.addArray ({ "gdi32", "user32", "kernel32", "comctl32" });
|
||||
|
||||
result.addTokens (getExternalLibrariesString(), ";\n", "\"'");
|
||||
|
||||
result = getCleanedStringArray (result);
|
||||
|
||||
for (auto& option : result)
|
||||
option = build_tools::replacePreprocessorDefs (getAllPreprocessorDefs(), option);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void addProjectLinkerOptions (XmlElement& xml) const
|
||||
{
|
||||
auto* linker = xml.createNewChildElement ("Linker");
|
||||
|
||||
for (auto& lib : getProjectLinkerLibs())
|
||||
setAddOption (*linker, "library", lib);
|
||||
}
|
||||
|
||||
CodeBlocksTarget& getTargetWithType (build_tools::ProjectType::Target::Type type) const
|
||||
{
|
||||
CodeBlocksTarget* nonAggregrateTarget = nullptr;
|
||||
|
||||
for (auto* target : targets)
|
||||
{
|
||||
if (target->type == type)
|
||||
return *target;
|
||||
|
||||
if (target->type != build_tools::ProjectType::Target::AggregateTarget)
|
||||
nonAggregrateTarget = target;
|
||||
}
|
||||
|
||||
// this project has no valid targets
|
||||
jassert (nonAggregrateTarget != nullptr);
|
||||
|
||||
return *nonAggregrateTarget;
|
||||
}
|
||||
|
||||
// Returns SharedCode target for multi-target projects, otherwise it returns
|
||||
// the single target
|
||||
CodeBlocksTarget& getMainTarget() const
|
||||
{
|
||||
if (getProject().isAudioPluginProject())
|
||||
return getTargetWithType (build_tools::ProjectType::Target::SharedCodeTarget);
|
||||
|
||||
for (auto* target : targets)
|
||||
if (target->type != build_tools::ProjectType::Target::AggregateTarget)
|
||||
return *target;
|
||||
|
||||
jassertfalse;
|
||||
|
||||
return *targets[0];
|
||||
}
|
||||
|
||||
CodeBlocksTarget& getTargetForProjectItem (const Project::Item& projectItem) const
|
||||
{
|
||||
if (getProject().isAudioPluginProject())
|
||||
{
|
||||
if (! projectItem.shouldBeCompiled())
|
||||
return getTargetWithType (build_tools::ProjectType::Target::SharedCodeTarget);
|
||||
|
||||
return getTargetWithType (getProject().getTargetTypeFromFilePath (projectItem.getFile(), true));
|
||||
}
|
||||
|
||||
return getMainTarget();
|
||||
}
|
||||
|
||||
void addCompileUnits (const Project::Item& projectItem, XmlElement& xml) const
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
addCompileUnits (projectItem.getChild(i), xml);
|
||||
}
|
||||
else if (projectItem.shouldBeAddedToTargetProject() && projectItem.shouldBeAddedToTargetExporter (*this))
|
||||
{
|
||||
build_tools::RelativePath file (projectItem.getFile(), getTargetFolder(), build_tools::RelativePath::buildTargetFolder);
|
||||
|
||||
auto* unit = xml.createNewChildElement ("Unit");
|
||||
unit->setAttribute ("filename", file.toUnixStyle());
|
||||
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
{
|
||||
auto targetName = getTargetForProjectItem (projectItem).getTargetNameForConfiguration (*config);
|
||||
unit->createNewChildElement ("Option")->setAttribute ("target", targetName);
|
||||
}
|
||||
|
||||
if (projectItem.shouldBeCompiled())
|
||||
{
|
||||
auto extraCompilerFlags = compilerFlagSchemesMap[projectItem.getCompilerFlagSchemeString()].get().toString();
|
||||
|
||||
if (extraCompilerFlags.isNotEmpty())
|
||||
{
|
||||
auto* optionElement = unit->createNewChildElement ("Option");
|
||||
|
||||
optionElement->setAttribute ("compiler", "gcc");
|
||||
optionElement->setAttribute ("use", 1);
|
||||
optionElement->setAttribute ("buildCommand", "$compiler $options " + extraCompilerFlags + " $includes -c $file -o $object");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unit->createNewChildElement ("Option")->setAttribute ("compile", 0);
|
||||
unit->createNewChildElement ("Option")->setAttribute ("link", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasResourceFile() const
|
||||
{
|
||||
return ! projectType.isStaticLibrary();
|
||||
}
|
||||
|
||||
void addCompileUnits (XmlElement& xml) const
|
||||
{
|
||||
for (int i = 0; i < getAllGroups().size(); ++i)
|
||||
addCompileUnits (getAllGroups().getReference(i), xml);
|
||||
|
||||
if (hasResourceFile())
|
||||
{
|
||||
const auto iconFile = getTargetFolder().getChildFile ("icon.ico");
|
||||
|
||||
if (! build_tools::asArray (getIcons()).isEmpty())
|
||||
build_tools::writeWinIcon (getIcons(), iconFile);
|
||||
|
||||
auto rcFile = getTargetFolder().getChildFile ("resources.rc");
|
||||
MSVCProjectExporterBase::createRCFile (project, iconFile, rcFile);
|
||||
|
||||
auto* unit = xml.createNewChildElement ("Unit");
|
||||
unit->setAttribute ("filename", rcFile.getFileName());
|
||||
unit->createNewChildElement ("Option")->setAttribute ("compilerVar", "WINDRES");
|
||||
}
|
||||
}
|
||||
|
||||
void createProject (XmlElement& xml) const
|
||||
{
|
||||
addOptions (xml);
|
||||
addBuild (xml);
|
||||
addVirtualTargets (xml);
|
||||
addProjectCompilerOptions (xml);
|
||||
addProjectLinkerOptions (xml);
|
||||
addCompileUnits (xml);
|
||||
}
|
||||
|
||||
void setAddOption (XmlElement& xml, const String& nm, const String& value) const
|
||||
{
|
||||
xml.createNewChildElement ("Add")->setAttribute (nm, value);
|
||||
}
|
||||
|
||||
CodeBlocksOS os;
|
||||
|
||||
OwnedArray<CodeBlocksTarget> targets;
|
||||
|
||||
friend class CLionProjectExporter;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (CodeBlocksProjectExporter)
|
||||
};
|
1937
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h
vendored
Normal file
1937
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_MSVC.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1039
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h
vendored
Normal file
1039
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Make.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3568
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
vendored
Normal file
3568
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExport_Xcode.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1077
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp
vendored
Normal file
1077
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
479
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h
vendored
Normal file
479
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectExporter.h
vendored
Normal file
@ -0,0 +1,479 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Project/jucer_Project.h"
|
||||
#include "../Utility/UI/PropertyComponents/jucer_PropertyComponentsWithEnablement.h"
|
||||
#include "../Utility/Helpers/jucer_ValueWithDefaultWrapper.h"
|
||||
#include "../Project/Modules/jucer_Modules.h"
|
||||
|
||||
class ProjectSaver;
|
||||
|
||||
//==============================================================================
|
||||
class ProjectExporter : private Value::Listener
|
||||
{
|
||||
public:
|
||||
ProjectExporter (Project&, const ValueTree& settings);
|
||||
virtual ~ProjectExporter() override = default;
|
||||
|
||||
//==============================================================================
|
||||
struct ExporterTypeInfo
|
||||
{
|
||||
Identifier identifier;
|
||||
String displayName;
|
||||
String targetFolder;
|
||||
|
||||
Image icon;
|
||||
};
|
||||
|
||||
static std::vector<ExporterTypeInfo> getExporterTypeInfos();
|
||||
static ExporterTypeInfo getTypeInfoForExporter (const Identifier& exporterIdentifier);
|
||||
static ExporterTypeInfo getCurrentPlatformExporterTypeInfo();
|
||||
|
||||
static std::unique_ptr<ProjectExporter> createNewExporter (Project&, const Identifier& exporterIdentifier);
|
||||
static std::unique_ptr<ProjectExporter> createExporterFromSettings (Project&, const ValueTree& settings);
|
||||
|
||||
static bool canProjectBeLaunched (Project*);
|
||||
|
||||
virtual Identifier getExporterIdentifier() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
// capabilities of exporter
|
||||
virtual bool usesMMFiles() const = 0;
|
||||
virtual void createExporterProperties (PropertyListBuilder&) = 0;
|
||||
virtual bool canLaunchProject() = 0;
|
||||
virtual bool launchProject() = 0;
|
||||
virtual void create (const OwnedArray<LibraryModule>&) const = 0; // may throw a SaveError
|
||||
virtual bool shouldFileBeCompiledByDefault (const File& path) const;
|
||||
virtual bool canCopeWithDuplicateFiles() = 0;
|
||||
virtual bool supportsUserDefinedConfigurations() const = 0; // false if exporter only supports two configs Debug and Release
|
||||
virtual void updateDeprecatedSettings() {}
|
||||
virtual void updateDeprecatedSettingsInteractively() {}
|
||||
virtual void initialiseDependencyPathValues() {}
|
||||
|
||||
// IDE targeted by exporter
|
||||
virtual bool isXcode() const = 0;
|
||||
virtual bool isVisualStudio() const = 0;
|
||||
virtual bool isCodeBlocks() const = 0;
|
||||
virtual bool isMakefile() const = 0;
|
||||
virtual bool isAndroidStudio() const = 0;
|
||||
virtual bool isCLion() const = 0;
|
||||
|
||||
// operating system targeted by exporter
|
||||
virtual bool isAndroid() const = 0;
|
||||
virtual bool isWindows() const = 0;
|
||||
virtual bool isLinux() const = 0;
|
||||
virtual bool isOSX() const = 0;
|
||||
virtual bool isiOS() const = 0;
|
||||
|
||||
virtual String getNewLineString() const = 0;
|
||||
virtual String getDescription() { return {}; }
|
||||
|
||||
virtual bool supportsPrecompiledHeaders() const { return false; }
|
||||
|
||||
//==============================================================================
|
||||
// cross-platform audio plug-ins supported by exporter
|
||||
virtual bool supportsTargetType (build_tools::ProjectType::Target::Type type) const = 0;
|
||||
|
||||
inline bool shouldBuildTargetType (build_tools::ProjectType::Target::Type type) const
|
||||
{
|
||||
return project.shouldBuildTargetType (type) && supportsTargetType (type);
|
||||
}
|
||||
|
||||
inline void callForAllSupportedTargets (std::function<void (build_tools::ProjectType::Target::Type)> callback)
|
||||
{
|
||||
for (int i = 0; i < build_tools::ProjectType::Target::unspecified; ++i)
|
||||
if (shouldBuildTargetType (static_cast<build_tools::ProjectType::Target::Type> (i)))
|
||||
callback (static_cast<build_tools::ProjectType::Target::Type> (i));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool mayCompileOnCurrentOS() const
|
||||
{
|
||||
#if JUCE_MAC
|
||||
return isOSX() || isAndroid() || isiOS();
|
||||
#elif JUCE_WINDOWS
|
||||
return isWindows() || isAndroid();
|
||||
#elif JUCE_LINUX
|
||||
return isLinux() || isAndroid();
|
||||
#elif JUCE_BSD
|
||||
return isLinux();
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String getUniqueName() const;
|
||||
File getTargetFolder() const;
|
||||
|
||||
Project& getProject() noexcept { return project; }
|
||||
const Project& getProject() const noexcept { return project; }
|
||||
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (settings); }
|
||||
|
||||
Value getSetting (const Identifier& nm) { return settings.getPropertyAsValue (nm, project.getUndoManagerFor (settings)); }
|
||||
String getSettingString (const Identifier& nm) const { return settings [nm]; }
|
||||
|
||||
Value getTargetLocationValue() { return targetLocationValue.getPropertyAsValue(); }
|
||||
String getTargetLocationString() const { return targetLocationValue.get(); }
|
||||
|
||||
String getExtraCompilerFlagsString() const { return extraCompilerFlagsValue.get().toString().replaceCharacters ("\r\n", " "); }
|
||||
String getExtraLinkerFlagsString() const { return extraLinkerFlagsValue.get().toString().replaceCharacters ("\r\n", " "); }
|
||||
|
||||
StringArray getExternalLibrariesStringArray() const { return getSearchPathsFromString (externalLibrariesValue.get().toString()); }
|
||||
String getExternalLibrariesString() const { return getExternalLibrariesStringArray().joinIntoString (";"); }
|
||||
|
||||
bool shouldUseGNUExtensions() const { return gnuExtensionsValue.get(); }
|
||||
|
||||
String getVSTLegacyPathString() const { return vstLegacyPathValueWrapper.getCurrentValue(); }
|
||||
String getAAXPathString() const { return aaxPathValueWrapper.getCurrentValue(); }
|
||||
String getRTASPathString() const { return rtasPathValueWrapper.getCurrentValue(); }
|
||||
|
||||
// NB: this is the path to the parent "modules" folder that contains the named module, not the
|
||||
// module folder itself.
|
||||
ValueWithDefault getPathForModuleValue (const String& moduleID);
|
||||
String getPathForModuleString (const String& moduleID) const;
|
||||
void removePathForModule (const String& moduleID);
|
||||
|
||||
TargetOS::OS getTargetOSForExporter() const;
|
||||
|
||||
build_tools::RelativePath getLegacyModulePath (const String& moduleID) const;
|
||||
String getLegacyModulePath() const;
|
||||
|
||||
// Returns a path to the actual module folder itself
|
||||
build_tools::RelativePath getModuleFolderRelativeToProject (const String& moduleID) const;
|
||||
void updateOldModulePaths();
|
||||
|
||||
build_tools::RelativePath rebaseFromProjectFolderToBuildTarget (const build_tools::RelativePath& path) const;
|
||||
void addToExtraSearchPaths (const build_tools::RelativePath& pathFromProjectFolder, int index = -1);
|
||||
void addToModuleLibPaths (const build_tools::RelativePath& pathFromProjectFolder);
|
||||
|
||||
void addProjectPathToBuildPathList (StringArray&, const build_tools::RelativePath&, int index = -1) const;
|
||||
|
||||
std::unique_ptr<Drawable> getBigIcon() const;
|
||||
std::unique_ptr<Drawable> getSmallIcon() const;
|
||||
build_tools::Icons getIcons() const { return { getSmallIcon(), getBigIcon() }; }
|
||||
|
||||
String getExporterIdentifierMacro() const
|
||||
{
|
||||
return "JUCER_" + settings.getType().toString() + "_"
|
||||
+ String::toHexString (getTargetLocationString().hashCode()).toUpperCase();
|
||||
}
|
||||
|
||||
// An exception that can be thrown by the create() method.
|
||||
void createPropertyEditors (PropertyListBuilder&);
|
||||
void addSettingsForProjectType (const build_tools::ProjectType&);
|
||||
|
||||
//==============================================================================
|
||||
void copyMainGroupFromProject();
|
||||
Array<Project::Item>& getAllGroups() noexcept { jassert (itemGroups.size() > 0); return itemGroups; }
|
||||
const Array<Project::Item>& getAllGroups() const noexcept { jassert (itemGroups.size() > 0); return itemGroups; }
|
||||
Project::Item& getModulesGroup();
|
||||
|
||||
//==============================================================================
|
||||
StringArray linuxLibs, linuxPackages, makefileExtraLinkerFlags;
|
||||
|
||||
enum class PackageDependencyType
|
||||
{
|
||||
compile,
|
||||
link
|
||||
};
|
||||
|
||||
StringArray getLinuxPackages (PackageDependencyType type) const;
|
||||
|
||||
//==============================================================================
|
||||
StringPairArray msvcExtraPreprocessorDefs;
|
||||
String msvcDelayLoadedDLLs;
|
||||
StringArray mingwLibs, windowsLibs;
|
||||
|
||||
//==============================================================================
|
||||
StringArray androidLibs;
|
||||
|
||||
//==============================================================================
|
||||
StringArray extraSearchPaths;
|
||||
StringArray moduleLibSearchPaths;
|
||||
|
||||
//==============================================================================
|
||||
class BuildConfiguration : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
BuildConfiguration (Project& project, const ValueTree& configNode, const ProjectExporter&);
|
||||
~BuildConfiguration();
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<BuildConfiguration>;
|
||||
|
||||
//==============================================================================
|
||||
virtual void createConfigProperties (PropertyListBuilder&) = 0;
|
||||
virtual String getModuleLibraryArchName() const = 0;
|
||||
|
||||
//==============================================================================
|
||||
String getName() const { return configNameValue.get(); }
|
||||
bool isDebug() const { return isDebugValue.get(); }
|
||||
|
||||
String getTargetBinaryRelativePathString() const { return targetBinaryPathValue.get(); }
|
||||
String getTargetBinaryNameString (bool isUnityPlugin = false) const
|
||||
{
|
||||
return (isUnityPlugin ? Project::addUnityPluginPrefixIfNecessary (targetNameValue.get().toString())
|
||||
: targetNameValue.get().toString());
|
||||
}
|
||||
|
||||
int getOptimisationLevelInt() const { return optimisationLevelValue.get(); }
|
||||
String getGCCOptimisationFlag() const;
|
||||
bool isLinkTimeOptimisationEnabled() const { return linkTimeOptimisationValue.get(); }
|
||||
|
||||
String getBuildConfigPreprocessorDefsString() const { return ppDefinesValue.get(); }
|
||||
StringPairArray getAllPreprocessorDefs() const; // includes inherited definitions
|
||||
StringPairArray getUniquePreprocessorDefs() const; // returns pre-processor definitions that are not already in the project pre-processor defs
|
||||
|
||||
String getHeaderSearchPathString() const { return headerSearchPathValue.get(); }
|
||||
StringArray getHeaderSearchPaths() const;
|
||||
|
||||
String getLibrarySearchPathString() const { return librarySearchPathValue.get(); }
|
||||
StringArray getLibrarySearchPaths() const;
|
||||
|
||||
String getPrecompiledHeaderFilename() const { return "JucePrecompiledHeader_" + getName(); }
|
||||
static String getSkipPrecompiledHeaderDefine() { return "JUCE_SKIP_PRECOMPILED_HEADER"; }
|
||||
|
||||
bool shouldUsePrecompiledHeaderFile() const { return usePrecompiledHeaderFileValue.get(); }
|
||||
String getPrecompiledHeaderFileContent() const;
|
||||
|
||||
//==============================================================================
|
||||
Value getValue (const Identifier& nm) { return config.getPropertyAsValue (nm, getUndoManager()); }
|
||||
UndoManager* getUndoManager() const { return project.getUndoManagerFor (config); }
|
||||
|
||||
//==============================================================================
|
||||
void createPropertyEditors (PropertyListBuilder&);
|
||||
void addRecommendedLinuxCompilerWarningsProperty (PropertyListBuilder&);
|
||||
void addRecommendedLLVMCompilerWarningsProperty (PropertyListBuilder&);
|
||||
|
||||
struct CompilerNames
|
||||
{
|
||||
static constexpr const char* gcc = "GCC";
|
||||
static constexpr const char* llvm = "LLVM";
|
||||
};
|
||||
|
||||
struct CompilerWarningFlags
|
||||
{
|
||||
static CompilerWarningFlags getRecommendedForGCCAndLLVM()
|
||||
{
|
||||
CompilerWarningFlags result;
|
||||
result.common = { "-Wall", "-Wstrict-aliasing", "-Wuninitialized", "-Wunused-parameter",
|
||||
"-Wswitch-enum", "-Wsign-conversion", "-Wsign-compare",
|
||||
"-Wunreachable-code", "-Wcast-align", "-Wno-ignored-qualifiers" };
|
||||
result.cpp = { "-Woverloaded-virtual", "-Wreorder", "-Wzero-as-null-pointer-constant" };
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StringArray common;
|
||||
StringArray cpp;
|
||||
};
|
||||
|
||||
CompilerWarningFlags getRecommendedCompilerWarningFlags() const;
|
||||
|
||||
void addGCCOptimisationProperty (PropertyListBuilder&);
|
||||
void removeFromExporter();
|
||||
|
||||
//==============================================================================
|
||||
ValueTree config;
|
||||
Project& project;
|
||||
const ProjectExporter& exporter;
|
||||
|
||||
protected:
|
||||
ValueWithDefault isDebugValue, configNameValue, targetNameValue, targetBinaryPathValue, recommendedWarningsValue, optimisationLevelValue,
|
||||
linkTimeOptimisationValue, ppDefinesValue, headerSearchPathValue, librarySearchPathValue, userNotesValue,
|
||||
usePrecompiledHeaderFileValue, precompiledHeaderFileValue;
|
||||
|
||||
private:
|
||||
std::map<String, CompilerWarningFlags> recommendedCompilerWarningFlags;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BuildConfiguration)
|
||||
};
|
||||
|
||||
void addNewConfigurationFromExisting (const BuildConfiguration& configToCopy);
|
||||
void addNewConfiguration (bool isDebugConfig);
|
||||
bool hasConfigurationNamed (const String& name) const;
|
||||
String getUniqueConfigName (String name) const;
|
||||
|
||||
String getExternalLibraryFlags (const BuildConfiguration& config) const;
|
||||
|
||||
//==============================================================================
|
||||
struct ConfigIterator
|
||||
{
|
||||
ConfigIterator (ProjectExporter& exporter);
|
||||
|
||||
bool next();
|
||||
|
||||
BuildConfiguration& operator*() const { return *config; }
|
||||
BuildConfiguration* operator->() const { return config.get(); }
|
||||
|
||||
BuildConfiguration::Ptr config;
|
||||
int index;
|
||||
|
||||
private:
|
||||
ProjectExporter& exporter;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConfigIterator)
|
||||
};
|
||||
|
||||
struct ConstConfigIterator
|
||||
{
|
||||
ConstConfigIterator (const ProjectExporter& exporter);
|
||||
|
||||
bool next();
|
||||
|
||||
const BuildConfiguration& operator*() const { return *config; }
|
||||
const BuildConfiguration* operator->() const { return config.get(); }
|
||||
|
||||
BuildConfiguration::Ptr config;
|
||||
int index;
|
||||
|
||||
private:
|
||||
const ProjectExporter& exporter;
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ConstConfigIterator)
|
||||
};
|
||||
|
||||
int getNumConfigurations() const;
|
||||
BuildConfiguration::Ptr getConfiguration (int index) const;
|
||||
|
||||
ValueTree getConfigurations() const;
|
||||
virtual void createDefaultConfigs();
|
||||
void createDefaultModulePaths();
|
||||
|
||||
//==============================================================================
|
||||
Value getExporterPreprocessorDefsValue() { return extraPPDefsValue.getPropertyAsValue(); }
|
||||
String getExporterPreprocessorDefsString() const { return extraPPDefsValue.get(); }
|
||||
|
||||
// includes exporter, project + config defs
|
||||
StringPairArray getAllPreprocessorDefs (const BuildConfiguration& config, const build_tools::ProjectType::Target::Type targetType) const;
|
||||
// includes exporter + project defs
|
||||
StringPairArray getAllPreprocessorDefs() const;
|
||||
|
||||
void addTargetSpecificPreprocessorDefs (StringPairArray& defs, const build_tools::ProjectType::Target::Type targetType) const;
|
||||
|
||||
String replacePreprocessorTokens (const BuildConfiguration&, const String& sourceString) const;
|
||||
|
||||
ValueTree settings;
|
||||
|
||||
enum GCCOptimisationLevel
|
||||
{
|
||||
gccO0 = 1,
|
||||
gccO1 = 4,
|
||||
gccO2 = 5,
|
||||
gccO3 = 3,
|
||||
gccOs = 2,
|
||||
gccOfast = 6
|
||||
};
|
||||
|
||||
bool isPCHEnabledForAnyConfigurations() const
|
||||
{
|
||||
if (supportsPrecompiledHeaders())
|
||||
for (ConstConfigIterator config (*this); config.next();)
|
||||
if (config->shouldUsePrecompiledHeaderFile())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
//==============================================================================
|
||||
String name;
|
||||
Project& project;
|
||||
const build_tools::ProjectType& projectType;
|
||||
const String projectName;
|
||||
const File projectFolder;
|
||||
|
||||
//==============================================================================
|
||||
ValueWithDefaultWrapper vstLegacyPathValueWrapper, rtasPathValueWrapper, aaxPathValueWrapper;
|
||||
|
||||
ValueWithDefault targetLocationValue, extraCompilerFlagsValue, extraLinkerFlagsValue, externalLibrariesValue,
|
||||
userNotesValue, gnuExtensionsValue, bigIconValue, smallIconValue, extraPPDefsValue;
|
||||
|
||||
Value projectCompilerFlagSchemesValue;
|
||||
HashMap<String, ValueWithDefault> compilerFlagSchemesMap;
|
||||
|
||||
mutable Array<Project::Item> itemGroups;
|
||||
Project::Item* modulesGroup = nullptr;
|
||||
|
||||
virtual BuildConfiguration::Ptr createBuildConfig (const ValueTree&) const = 0;
|
||||
|
||||
void addDefaultPreprocessorDefs (StringPairArray&) const;
|
||||
|
||||
static String getDefaultBuildsRootFolder() { return "Builds/"; }
|
||||
|
||||
static String getStaticLibbedFilename (String name) { return addSuffix (addLibPrefix (name), ".a"); }
|
||||
static String getDynamicLibbedFilename (String name) { return addSuffix (addLibPrefix (name), ".so"); }
|
||||
|
||||
virtual void addPlatformSpecificSettingsForProjectType (const build_tools::ProjectType&) = 0;
|
||||
|
||||
//==============================================================================
|
||||
static void createDirectoryOrThrow (const File& dirToCreate)
|
||||
{
|
||||
if (! dirToCreate.createDirectory())
|
||||
throw build_tools::SaveError ("Can't create folder: " + dirToCreate.getFullPathName());
|
||||
}
|
||||
|
||||
static void writeXmlOrThrow (const XmlElement& xml, const File& file, const String& encoding,
|
||||
int maxCharsPerLine, bool useUnixNewLines = false)
|
||||
{
|
||||
XmlElement::TextFormat format;
|
||||
format.customEncoding = encoding;
|
||||
format.lineWrapLength = maxCharsPerLine;
|
||||
format.newLineChars = useUnixNewLines ? "\n" : "\r\n";
|
||||
|
||||
MemoryOutputStream mo (8192);
|
||||
xml.writeTo (mo, format);
|
||||
build_tools::overwriteFileIfDifferentOrThrow (file, mo);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
void valueChanged (Value&) override { updateCompilerFlagValues(); }
|
||||
void updateCompilerFlagValues();
|
||||
|
||||
//==============================================================================
|
||||
static String addLibPrefix (const String name)
|
||||
{
|
||||
return name.startsWith ("lib") ? name
|
||||
: "lib" + name;
|
||||
}
|
||||
|
||||
static String addSuffix (const String name, const String suffix)
|
||||
{
|
||||
return name.endsWithIgnoreCase (suffix) ? name
|
||||
: name + suffix;
|
||||
}
|
||||
|
||||
void createDependencyPathProperties (PropertyListBuilder&);
|
||||
void createIconProperties (PropertyListBuilder&);
|
||||
void addVSTPathsIfPluginOrHost();
|
||||
void addCommonAudioPluginSettings();
|
||||
void addLegacyVSTFolderToPathIfSpecified();
|
||||
build_tools::RelativePath getInternalVST3SDKPath();
|
||||
void addAAXFoldersToPath();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectExporter)
|
||||
};
|
864
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp
vendored
Normal file
864
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.cpp
vendored
Normal file
@ -0,0 +1,864 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "jucer_ProjectSaver.h"
|
||||
#include "jucer_ProjectExport_CLion.h"
|
||||
#include "../Application/jucer_Application.h"
|
||||
|
||||
static constexpr const char* generatedGroupID = "__jucelibfiles";
|
||||
static constexpr const char* generatedGroupUID = "__generatedcode__";
|
||||
|
||||
constexpr int jucerFormatVersion = 1;
|
||||
|
||||
//==============================================================================
|
||||
ProjectSaver::ProjectSaver (Project& p)
|
||||
: project (p),
|
||||
generatedCodeFolder (project.getGeneratedCodeFolder()),
|
||||
generatedFilesGroup (Project::Item::createGroup (project, getJuceCodeGroupName(), generatedGroupUID, true)),
|
||||
projectLineFeed (project.getProjectLineFeed())
|
||||
{
|
||||
generatedFilesGroup.setID (generatedGroupID);
|
||||
}
|
||||
|
||||
void ProjectSaver::save (Async async, ProjectExporter* exporterToSave, std::function<void (Result)> onCompletion)
|
||||
{
|
||||
if (async == Async::yes)
|
||||
saveProjectAsync (exporterToSave, std::move (onCompletion));
|
||||
else
|
||||
onCompletion (saveProject (exporterToSave));
|
||||
}
|
||||
|
||||
void ProjectSaver::saveProjectAsync (ProjectExporter* exporterToSave, std::function<void (Result)> onCompletion)
|
||||
{
|
||||
jassert (saveThread == nullptr);
|
||||
|
||||
saveThread = std::make_unique<SaveThreadWithProgressWindow> (*this, exporterToSave,
|
||||
[ref = WeakReference<ProjectSaver> { this }, onCompletion] (Result result)
|
||||
{
|
||||
if (ref == nullptr)
|
||||
return;
|
||||
|
||||
// Clean up old save thread in case onCompletion wants to start a new save thread
|
||||
ref->saveThread->waitForThreadToExit (-1);
|
||||
ref->saveThread = nullptr;
|
||||
|
||||
if (onCompletion != nullptr)
|
||||
onCompletion (result);
|
||||
});
|
||||
saveThread->launchThread();
|
||||
}
|
||||
|
||||
Result ProjectSaver::saveResourcesOnly()
|
||||
{
|
||||
writeBinaryDataFiles();
|
||||
|
||||
if (! errors.isEmpty())
|
||||
return Result::fail (errors[0]);
|
||||
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
void ProjectSaver::saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent)
|
||||
{
|
||||
writePluginDefines();
|
||||
writeAppConfigFile (modules, appConfigUserContent);
|
||||
writeBinaryDataFiles();
|
||||
writeAppHeader (modules);
|
||||
writeModuleCppWrappers (modules);
|
||||
}
|
||||
|
||||
Project::Item ProjectSaver::addFileToGeneratedGroup (const File& file)
|
||||
{
|
||||
auto item = generatedFilesGroup.findItemForFile (file);
|
||||
|
||||
if (item.isValid())
|
||||
return item;
|
||||
|
||||
generatedFilesGroup.addFileAtIndex (file, -1, true);
|
||||
return generatedFilesGroup.findItemForFile (file);
|
||||
}
|
||||
|
||||
bool ProjectSaver::copyFolder (const File& source, const File& dest)
|
||||
{
|
||||
if (source.isDirectory() && dest.createDirectory())
|
||||
{
|
||||
for (auto& f : source.findChildFiles (File::findFiles, false))
|
||||
{
|
||||
auto target = dest.getChildFile (f.getFileName());
|
||||
filesCreated.add (target);
|
||||
|
||||
if (! f.copyFileTo (target))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& f : source.findChildFiles (File::findDirectories, false))
|
||||
{
|
||||
auto name = f.getFileName();
|
||||
|
||||
if (name == ".git" || name == ".svn" || name == ".cvs")
|
||||
continue;
|
||||
|
||||
if (! copyFolder (f, dest.getChildFile (f.getFileName())))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Project::Item ProjectSaver::saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData)
|
||||
{
|
||||
if (! generatedCodeFolder.createDirectory())
|
||||
{
|
||||
addError ("Couldn't create folder: " + generatedCodeFolder.getFullPathName());
|
||||
return Project::Item (project, {}, false);
|
||||
}
|
||||
|
||||
auto file = generatedCodeFolder.getChildFile (filePath);
|
||||
|
||||
if (replaceFileIfDifferent (file, newData))
|
||||
return addFileToGeneratedGroup (file);
|
||||
|
||||
return { project, {}, true };
|
||||
}
|
||||
|
||||
bool ProjectSaver::replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData)
|
||||
{
|
||||
filesCreated.add (f);
|
||||
|
||||
if (! build_tools::overwriteFileWithNewDataIfDifferent (f, newData))
|
||||
{
|
||||
addError ("Can't write to file: " + f.getFullPathName());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ProjectSaver::deleteUnwantedFilesIn (const File& parent)
|
||||
{
|
||||
// Recursively clears out any files in a folder that we didn't create, but avoids
|
||||
// any folders containing hidden files that might be used by version-control systems.
|
||||
auto shouldFileBeKept = [] (const String& filename)
|
||||
{
|
||||
static StringArray filesToKeep (".svn", ".cvs", "CMakeLists.txt");
|
||||
return filesToKeep.contains (filename);
|
||||
};
|
||||
|
||||
bool folderIsNowEmpty = true;
|
||||
Array<File> filesToDelete;
|
||||
|
||||
for (const auto& i : RangedDirectoryIterator (parent, false, "*", File::findFilesAndDirectories))
|
||||
{
|
||||
auto f = i.getFile();
|
||||
|
||||
if (filesCreated.contains (f) || shouldFileBeKept (f.getFileName()))
|
||||
{
|
||||
folderIsNowEmpty = false;
|
||||
}
|
||||
else if (i.isDirectory())
|
||||
{
|
||||
if (deleteUnwantedFilesIn (f))
|
||||
filesToDelete.add (f);
|
||||
else
|
||||
folderIsNowEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
filesToDelete.add (f);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = filesToDelete.size(); --j >= 0;)
|
||||
filesToDelete.getReference (j).deleteRecursively();
|
||||
|
||||
return folderIsNowEmpty;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ProjectSaver::addError (const String& message)
|
||||
{
|
||||
const ScopedLock sl (errorLock);
|
||||
errors.add (message);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
File ProjectSaver::getAppConfigFile() const
|
||||
{
|
||||
return generatedCodeFolder.getChildFile (Project::getAppConfigFilename());
|
||||
}
|
||||
|
||||
File ProjectSaver::getPluginDefinesFile() const
|
||||
{
|
||||
return generatedCodeFolder.getChildFile (Project::getPluginDefinesFilename());
|
||||
}
|
||||
|
||||
String ProjectSaver::loadUserContentFromAppConfig() const
|
||||
{
|
||||
StringArray userContent;
|
||||
bool foundCodeSection = false;
|
||||
auto lines = StringArray::fromLines (getAppConfigFile().loadFileAsString());
|
||||
|
||||
for (int i = 0; i < lines.size(); ++i)
|
||||
{
|
||||
if (lines[i].contains ("[BEGIN_USER_CODE_SECTION]"))
|
||||
{
|
||||
for (int j = i + 1; j < lines.size() && ! lines[j].contains ("[END_USER_CODE_SECTION]"); ++j)
|
||||
userContent.add (lines[j]);
|
||||
|
||||
foundCodeSection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (! foundCodeSection)
|
||||
{
|
||||
userContent.add ({});
|
||||
userContent.add ("// (You can add your own code in this section, and the Projucer will not overwrite it)");
|
||||
userContent.add ({});
|
||||
}
|
||||
|
||||
return userContent.joinIntoString (projectLineFeed) + projectLineFeed;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
OwnedArray<LibraryModule> ProjectSaver::getModules()
|
||||
{
|
||||
OwnedArray<LibraryModule> modules;
|
||||
project.getEnabledModules().createRequiredModules (modules);
|
||||
|
||||
auto isCommandLine = ProjucerApplication::getApp().isRunningCommandLine;
|
||||
|
||||
for (auto* module : modules)
|
||||
{
|
||||
if (! module->isValid())
|
||||
{
|
||||
addError (String ("At least one of your JUCE module paths is invalid!\n")
|
||||
+ (isCommandLine ? "Please ensure each module path points to the correct JUCE modules folder."
|
||||
: "Please go to the Modules settings page and ensure each path points to the correct JUCE modules folder."));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (project.getEnabledModules().getExtraDependenciesNeeded (module->getID()).size() > 0)
|
||||
{
|
||||
addError (String ("At least one of your modules has missing dependencies!\n")
|
||||
+ (isCommandLine ? "Please add the required dependencies, or run the command again with the \"--fix-missing-dependencies\" option."
|
||||
: "Please go to the settings page of the highlighted modules and add the required dependencies."));
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return modules;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Result ProjectSaver::saveProject (ProjectExporter* specifiedExporterToSave)
|
||||
{
|
||||
if (project.getNumExporters() == 0)
|
||||
{
|
||||
return Result::fail ("No exporters found!\n"
|
||||
"Please add an exporter before saving.");
|
||||
}
|
||||
|
||||
auto oldProjectFile = project.getFile();
|
||||
auto modules = getModules();
|
||||
|
||||
if (errors.isEmpty())
|
||||
{
|
||||
if (project.isAudioPluginProject())
|
||||
{
|
||||
const auto isInvalidCode = [] (String code)
|
||||
{
|
||||
return code.length() != 4 || code.toStdString().size() != 4;
|
||||
};
|
||||
|
||||
if (isInvalidCode (project.getPluginManufacturerCodeString()))
|
||||
return Result::fail ("The plugin manufacturer code must contain exactly four characters.");
|
||||
|
||||
if (isInvalidCode (project.getPluginCodeString()))
|
||||
return Result::fail ("The plugin code must contain exactly four characters.");
|
||||
}
|
||||
|
||||
if (project.isAudioPluginProject())
|
||||
{
|
||||
if (project.shouldBuildUnityPlugin())
|
||||
writeUnityScriptFile();
|
||||
}
|
||||
|
||||
saveBasicProjectItems (modules, loadUserContentFromAppConfig());
|
||||
writeProjects (modules, specifiedExporterToSave);
|
||||
writeProjectFile();
|
||||
|
||||
runPostExportScript();
|
||||
|
||||
if (generatedCodeFolder.exists())
|
||||
{
|
||||
writeReadmeFile();
|
||||
deleteUnwantedFilesIn (generatedCodeFolder);
|
||||
}
|
||||
|
||||
if (errors.isEmpty())
|
||||
return Result::ok();
|
||||
}
|
||||
|
||||
project.setFile (oldProjectFile);
|
||||
return Result::fail (errors[0]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void ProjectSaver::writePluginDefines (MemoryOutputStream& out) const
|
||||
{
|
||||
const auto pluginDefines = getAudioPluginDefines();
|
||||
|
||||
if (pluginDefines.isEmpty())
|
||||
return;
|
||||
|
||||
writeAutoGenWarningComment (out);
|
||||
|
||||
out << "*/" << newLine << newLine
|
||||
<< "#pragma once" << newLine << newLine
|
||||
<< pluginDefines << newLine;
|
||||
}
|
||||
|
||||
void ProjectSaver::writeProjectFile()
|
||||
{
|
||||
auto root = project.getProjectRoot();
|
||||
|
||||
root.removeProperty ("jucerVersion", nullptr);
|
||||
|
||||
if ((int) root.getProperty (Ids::jucerFormatVersion, -1) != jucerFormatVersion)
|
||||
root.setProperty (Ids::jucerFormatVersion, jucerFormatVersion, nullptr);
|
||||
|
||||
project.updateCachedFileState();
|
||||
|
||||
auto newSerialisedXml = project.serialiseProjectXml (root.createXml());
|
||||
jassert (newSerialisedXml.isNotEmpty());
|
||||
|
||||
if (newSerialisedXml != project.getCachedFileStateContent())
|
||||
{
|
||||
project.getFile().replaceWithText (newSerialisedXml);
|
||||
project.updateCachedFileState();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::writeAppConfig (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules, const String& userContent)
|
||||
{
|
||||
if (! project.shouldUseAppConfig())
|
||||
return;
|
||||
|
||||
writeAutoGenWarningComment (out);
|
||||
|
||||
out << " There's a section below where you can add your own custom code safely, and the" << newLine
|
||||
<< " Projucer will preserve the contents of that block, but the best way to change" << newLine
|
||||
<< " any of these definitions is by using the Projucer's project settings." << newLine
|
||||
<< newLine
|
||||
<< " Any commented-out settings will assume their default values." << newLine
|
||||
<< newLine
|
||||
<< "*/" << newLine
|
||||
<< newLine;
|
||||
|
||||
out << "#pragma once" << newLine
|
||||
<< newLine
|
||||
<< "//==============================================================================" << newLine
|
||||
<< "// [BEGIN_USER_CODE_SECTION]" << newLine
|
||||
<< userContent
|
||||
<< "// [END_USER_CODE_SECTION]" << newLine;
|
||||
|
||||
if (getPluginDefinesFile().existsAsFile() && getAudioPluginDefines().isNotEmpty())
|
||||
out << newLine << CodeHelpers::createIncludeStatement (Project::getPluginDefinesFilename()) << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "/*" << newLine
|
||||
<< " ==============================================================================" << newLine
|
||||
<< newLine
|
||||
<< " In accordance with the terms of the JUCE 6 End-Use License Agreement, the" << newLine
|
||||
<< " JUCE Code in SECTION A cannot be removed, changed or otherwise rendered" << newLine
|
||||
<< " ineffective unless you have a JUCE Indie or Pro license, or are using JUCE" << newLine
|
||||
<< " under the GPL v3 license." << newLine
|
||||
<< newLine
|
||||
<< " End User License Agreement: www.juce.com/juce-6-licence" << newLine
|
||||
<< newLine
|
||||
<< " ==============================================================================" << newLine
|
||||
<< "*/" << newLine
|
||||
<< newLine
|
||||
<< "// BEGIN SECTION A" << newLine
|
||||
<< newLine
|
||||
<< "#ifndef JUCE_DISPLAY_SPLASH_SCREEN" << newLine
|
||||
<< " #define JUCE_DISPLAY_SPLASH_SCREEN " << (project.shouldDisplaySplashScreen() ? "1" : "0") << newLine
|
||||
<< "#endif" << newLine << newLine
|
||||
<< "// END SECTION A" << newLine
|
||||
<< newLine
|
||||
<< "#define JUCE_USE_DARK_SPLASH_SCREEN " << (project.getSplashScreenColourString() == "Dark" ? "1" : "0") << newLine
|
||||
<< newLine
|
||||
<< "#define JUCE_PROJUCER_VERSION 0x" << String::toHexString (ProjectInfo::versionNumber) << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "//==============================================================================" << newLine;
|
||||
|
||||
auto longestModuleName = [&modules]()
|
||||
{
|
||||
int longest = 0;
|
||||
|
||||
for (auto* module : modules)
|
||||
longest = jmax (longest, module->getID().length());
|
||||
|
||||
return longest;
|
||||
}();
|
||||
|
||||
for (auto* module : modules)
|
||||
{
|
||||
out << "#define JUCE_MODULE_AVAILABLE_" << module->getID()
|
||||
<< String::repeatedString (" ", longestModuleName + 5 - module->getID().length()) << " 1" << newLine;
|
||||
}
|
||||
|
||||
out << newLine << "#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1" << newLine;
|
||||
|
||||
for (auto* module : modules)
|
||||
{
|
||||
OwnedArray<Project::ConfigFlag> flags;
|
||||
module->getConfigFlags (project, flags);
|
||||
|
||||
if (flags.size() > 0)
|
||||
{
|
||||
out << newLine
|
||||
<< "//==============================================================================" << newLine
|
||||
<< "// " << module->getID() << " flags:" << newLine;
|
||||
|
||||
for (auto* flag : flags)
|
||||
{
|
||||
out << newLine
|
||||
<< "#ifndef " << flag->symbol
|
||||
<< newLine
|
||||
<< (flag->value.isUsingDefault() ? " //#define " : " #define ") << flag->symbol << " " << (flag->value.get() ? "1" : "0")
|
||||
<< newLine
|
||||
<< "#endif"
|
||||
<< newLine;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& type = project.getProjectType();
|
||||
auto isStandaloneApplication = (! type.isAudioPlugin() && ! type.isDynamicLibrary());
|
||||
|
||||
out << newLine
|
||||
<< "//==============================================================================" << newLine
|
||||
<< "#ifndef JUCE_STANDALONE_APPLICATION" << newLine
|
||||
<< " #if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)" << newLine
|
||||
<< " #define JUCE_STANDALONE_APPLICATION JucePlugin_Build_Standalone" << newLine
|
||||
<< " #else" << newLine
|
||||
<< " #define JUCE_STANDALONE_APPLICATION " << (isStandaloneApplication ? "1" : "0") << newLine
|
||||
<< " #endif" << newLine
|
||||
<< "#endif" << newLine;
|
||||
}
|
||||
|
||||
template <typename WriterCallback>
|
||||
void ProjectSaver::writeOrRemoveGeneratedFile (const String& name, WriterCallback&& writerCallback)
|
||||
{
|
||||
MemoryOutputStream mem;
|
||||
mem.setNewLineString (projectLineFeed);
|
||||
|
||||
writerCallback (mem);
|
||||
|
||||
if (mem.getDataSize() != 0)
|
||||
{
|
||||
saveGeneratedFile (name, mem);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto destFile = generatedCodeFolder.getChildFile (name);
|
||||
|
||||
if (destFile.existsAsFile())
|
||||
{
|
||||
if (! destFile.deleteFile())
|
||||
addError ("Couldn't remove unnecessary file: " + destFile.getFullPathName());
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::writePluginDefines()
|
||||
{
|
||||
writeOrRemoveGeneratedFile (Project::getPluginDefinesFilename(), [&] (MemoryOutputStream& mem)
|
||||
{
|
||||
writePluginDefines (mem);
|
||||
});
|
||||
}
|
||||
|
||||
void ProjectSaver::writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent)
|
||||
{
|
||||
writeOrRemoveGeneratedFile (Project::getAppConfigFilename(), [&] (MemoryOutputStream& mem)
|
||||
{
|
||||
writeAppConfig (mem, modules, userContent);
|
||||
});
|
||||
}
|
||||
|
||||
void ProjectSaver::writeAppHeader (MemoryOutputStream& out, const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
writeAutoGenWarningComment (out);
|
||||
|
||||
out << " This is the header file that your files should include in order to get all the" << newLine
|
||||
<< " JUCE library headers. You should avoid including the JUCE headers directly in" << newLine
|
||||
<< " your own source files, because that wouldn't pick up the correct configuration" << newLine
|
||||
<< " options for your app." << newLine
|
||||
<< newLine
|
||||
<< "*/" << newLine << newLine;
|
||||
|
||||
out << "#pragma once" << newLine << newLine;
|
||||
|
||||
if (getAppConfigFile().exists() && project.shouldUseAppConfig())
|
||||
out << CodeHelpers::createIncludeStatement (Project::getAppConfigFilename()) << newLine;
|
||||
|
||||
if (modules.size() > 0)
|
||||
{
|
||||
out << newLine;
|
||||
|
||||
for (auto* module : modules)
|
||||
module->writeIncludes (*this, out);
|
||||
|
||||
out << newLine;
|
||||
}
|
||||
|
||||
if (hasBinaryData && project.shouldIncludeBinaryInJuceHeader())
|
||||
out << CodeHelpers::createIncludeStatement (project.getBinaryDataHeaderFile(), getAppConfigFile()) << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "#if defined (JUCE_PROJUCER_VERSION) && JUCE_PROJUCER_VERSION < JUCE_VERSION" << newLine
|
||||
<< " /** If you've hit this error then the version of the Projucer that was used to generate this project is" << newLine
|
||||
<< " older than the version of the JUCE modules being included. To fix this error, re-save your project" << newLine
|
||||
<< " using the latest version of the Projucer or, if you aren't using the Projucer to manage your project," << newLine
|
||||
<< " remove the JUCE_PROJUCER_VERSION define." << newLine
|
||||
<< " */" << newLine
|
||||
<< " #error \"This project was last saved using an outdated version of the Projucer! Re-save this project with the latest version to fix this error.\"" << newLine
|
||||
<< "#endif" << newLine
|
||||
<< newLine;
|
||||
|
||||
if (project.shouldAddUsingNamespaceToJuceHeader())
|
||||
out << "#if ! DONT_SET_USING_JUCE_NAMESPACE" << newLine
|
||||
<< " // If your code uses a lot of JUCE classes, then this will obviously save you" << newLine
|
||||
<< " // a lot of typing, but can be disabled by setting DONT_SET_USING_JUCE_NAMESPACE." << newLine
|
||||
<< " using namespace juce;" << newLine
|
||||
<< "#endif" << newLine;
|
||||
|
||||
out << newLine
|
||||
<< "#if ! JUCE_DONT_DECLARE_PROJECTINFO" << newLine
|
||||
<< "namespace ProjectInfo" << newLine
|
||||
<< "{" << newLine
|
||||
<< " const char* const projectName = " << CppTokeniserFunctions::addEscapeChars (project.getProjectNameString()).quoted() << ";" << newLine
|
||||
<< " const char* const companyName = " << CppTokeniserFunctions::addEscapeChars (project.getCompanyNameString()).quoted() << ";" << newLine
|
||||
<< " const char* const versionString = " << CppTokeniserFunctions::addEscapeChars (project.getVersionString()).quoted() << ";" << newLine
|
||||
<< " const int versionNumber = " << project.getVersionAsHex() << ";" << newLine
|
||||
<< "}" << newLine
|
||||
<< "#endif" << newLine;
|
||||
}
|
||||
|
||||
void ProjectSaver::writeAppHeader (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
MemoryOutputStream mem;
|
||||
mem.setNewLineString (projectLineFeed);
|
||||
|
||||
writeAppHeader (mem, modules);
|
||||
saveGeneratedFile (Project::getJuceSourceHFilename(), mem);
|
||||
}
|
||||
|
||||
void ProjectSaver::writeModuleCppWrappers (const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
for (auto* module : modules)
|
||||
{
|
||||
for (auto& cu : module->getAllCompileUnits())
|
||||
{
|
||||
MemoryOutputStream mem;
|
||||
mem.setNewLineString (projectLineFeed);
|
||||
|
||||
writeAutoGenWarningComment (mem);
|
||||
|
||||
mem << "*/" << newLine << newLine;
|
||||
|
||||
if (project.shouldUseAppConfig())
|
||||
mem << "#include " << Project::getAppConfigFilename().quoted() << newLine;
|
||||
|
||||
mem << "#include <";
|
||||
|
||||
if (cu.file.getFileExtension() != ".r") // .r files are included without the path
|
||||
mem << module->getID() << "/";
|
||||
|
||||
mem << cu.file.getFileName() << ">" << newLine;
|
||||
|
||||
replaceFileIfDifferent (generatedCodeFolder.getChildFile (cu.getFilenameForProxyFile()), mem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::writeBinaryDataFiles()
|
||||
{
|
||||
auto binaryDataH = project.getBinaryDataHeaderFile();
|
||||
|
||||
JucerResourceFile resourceFile (project);
|
||||
|
||||
if (resourceFile.getNumFiles() > 0)
|
||||
{
|
||||
auto dataNamespace = project.getBinaryDataNamespaceString().trim();
|
||||
|
||||
if (dataNamespace.isEmpty())
|
||||
dataNamespace = "BinaryData";
|
||||
|
||||
resourceFile.setClassName (dataNamespace);
|
||||
|
||||
auto maxSize = project.getMaxBinaryFileSize();
|
||||
|
||||
if (maxSize <= 0)
|
||||
maxSize = 10 * 1024 * 1024;
|
||||
|
||||
Array<File> binaryDataFiles;
|
||||
auto r = resourceFile.write (maxSize);
|
||||
|
||||
if (r.result.wasOk())
|
||||
{
|
||||
hasBinaryData = true;
|
||||
|
||||
for (auto& f : r.filesCreated)
|
||||
{
|
||||
filesCreated.add (f);
|
||||
generatedFilesGroup.addFileRetainingSortOrder (f, ! f.hasFileExtension (".h"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addError (r.result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 20; --i >= 0;)
|
||||
project.getBinaryDataCppFile (i).deleteFile();
|
||||
|
||||
binaryDataH.deleteFile();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::writeReadmeFile()
|
||||
{
|
||||
MemoryOutputStream out;
|
||||
out.setNewLineString (projectLineFeed);
|
||||
|
||||
out << newLine
|
||||
<< " Important Note!!" << newLine
|
||||
<< " ================" << newLine
|
||||
<< newLine
|
||||
<< "The purpose of this folder is to contain files that are auto-generated by the Projucer," << newLine
|
||||
<< "and ALL files in this folder will be mercilessly DELETED and completely re-written whenever" << newLine
|
||||
<< "the Projucer saves your project." << newLine
|
||||
<< newLine
|
||||
<< "Therefore, it's a bad idea to make any manual changes to the files in here, or to" << newLine
|
||||
<< "put any of your own files in here if you don't want to lose them. (Of course you may choose" << newLine
|
||||
<< "to add the folder's contents to your version-control system so that you can re-merge your own" << newLine
|
||||
<< "modifications after the Projucer has saved its changes)." << newLine;
|
||||
|
||||
replaceFileIfDifferent (generatedCodeFolder.getChildFile ("ReadMe.txt"), out);
|
||||
}
|
||||
|
||||
String ProjectSaver::getAudioPluginDefines() const
|
||||
{
|
||||
const auto flags = project.getAudioPluginFlags();
|
||||
|
||||
if (flags.size() == 0)
|
||||
return {};
|
||||
|
||||
MemoryOutputStream mem;
|
||||
mem.setNewLineString (projectLineFeed);
|
||||
|
||||
mem << "//==============================================================================" << newLine
|
||||
<< "// Audio plugin settings.." << newLine
|
||||
<< newLine;
|
||||
|
||||
for (int i = 0; i < flags.size(); ++i)
|
||||
{
|
||||
mem << "#ifndef " << flags.getAllKeys()[i] << newLine
|
||||
<< " #define " << flags.getAllKeys()[i].paddedRight (' ', 32) << " "
|
||||
<< flags.getAllValues()[i] << newLine
|
||||
<< "#endif" << newLine;
|
||||
}
|
||||
|
||||
return mem.toString().trim();
|
||||
}
|
||||
|
||||
void ProjectSaver::writeUnityScriptFile()
|
||||
{
|
||||
auto unityScriptContents = replaceLineFeeds (BinaryData::UnityPluginGUIScript_cs_in,
|
||||
projectLineFeed);
|
||||
|
||||
auto projectName = Project::addUnityPluginPrefixIfNecessary (project.getProjectNameString());
|
||||
|
||||
unityScriptContents = unityScriptContents.replace ("${plugin_class_name}", projectName.replace (" ", "_"))
|
||||
.replace ("${plugin_name}", projectName)
|
||||
.replace ("${plugin_vendor}", project.getPluginManufacturerString())
|
||||
.replace ("${plugin_description}", project.getPluginDescriptionString());
|
||||
|
||||
auto f = generatedCodeFolder.getChildFile (project.getUnityScriptName());
|
||||
|
||||
MemoryOutputStream out;
|
||||
out << unityScriptContents;
|
||||
|
||||
replaceFileIfDifferent (f, out);
|
||||
}
|
||||
|
||||
void ProjectSaver::writeProjects (const OwnedArray<LibraryModule>& modules, ProjectExporter* specifiedExporterToSave)
|
||||
{
|
||||
ThreadPool threadPool;
|
||||
|
||||
// keep a copy of the basic generated files group, as each exporter may modify it.
|
||||
auto originalGeneratedGroup = generatedFilesGroup.state.createCopy();
|
||||
|
||||
CLionProjectExporter* clionExporter = nullptr;
|
||||
std::vector<std::unique_ptr<ProjectExporter>> exporters;
|
||||
|
||||
try
|
||||
{
|
||||
for (Project::ExporterIterator exp (project); exp.next();)
|
||||
{
|
||||
if (specifiedExporterToSave != nullptr && exp->getUniqueName() != specifiedExporterToSave->getUniqueName())
|
||||
continue;
|
||||
|
||||
exporters.push_back (std::move (exp.exporter));
|
||||
}
|
||||
|
||||
for (auto& exporter : exporters)
|
||||
{
|
||||
exporter->initialiseDependencyPathValues();
|
||||
|
||||
if (exporter->getTargetFolder().createDirectory())
|
||||
{
|
||||
if (exporter->isCLion())
|
||||
{
|
||||
clionExporter = dynamic_cast<CLionProjectExporter*> (exporter.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
exporter->copyMainGroupFromProject();
|
||||
exporter->settings = exporter->settings.createCopy();
|
||||
|
||||
exporter->addToExtraSearchPaths (build_tools::RelativePath ("JuceLibraryCode", build_tools::RelativePath::projectFolder));
|
||||
|
||||
generatedFilesGroup.state = originalGeneratedGroup.createCopy();
|
||||
exporter->addSettingsForProjectType (project.getProjectType());
|
||||
|
||||
for (auto* module : modules)
|
||||
module->addSettingsForModuleToExporter (*exporter, *this);
|
||||
|
||||
generatedFilesGroup.sortAlphabetically (true, true);
|
||||
exporter->getAllGroups().add (generatedFilesGroup);
|
||||
}
|
||||
|
||||
if (ProjucerApplication::getApp().isRunningCommandLine)
|
||||
saveExporter (*exporter, modules);
|
||||
else
|
||||
threadPool.addJob ([this, &exporter, &modules] { saveExporter (*exporter, modules); });
|
||||
}
|
||||
else
|
||||
{
|
||||
addError ("Can't create folder: " + exporter->getTargetFolder().getFullPathName());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (build_tools::SaveError& saveError)
|
||||
{
|
||||
addError (saveError.message);
|
||||
}
|
||||
|
||||
while (threadPool.getNumJobs() > 0)
|
||||
Thread::sleep (10);
|
||||
|
||||
if (clionExporter != nullptr)
|
||||
{
|
||||
for (auto& exporter : exporters)
|
||||
clionExporter->writeCMakeListsExporterSection (exporter.get());
|
||||
|
||||
std::cout << "Finished saving: " << clionExporter->getUniqueName() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::runPostExportScript()
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
auto cmdString = project.getPostExportShellCommandWinString();
|
||||
#else
|
||||
auto cmdString = project.getPostExportShellCommandPosixString();
|
||||
#endif
|
||||
|
||||
auto shellCommand = cmdString.replace ("%%1%%", project.getProjectFolder().getFullPathName());
|
||||
|
||||
if (shellCommand.isNotEmpty())
|
||||
{
|
||||
#if JUCE_WINDOWS
|
||||
StringArray argList ("cmd.exe", "/c");
|
||||
#else
|
||||
StringArray argList ("/bin/sh", "-c");
|
||||
#endif
|
||||
|
||||
argList.add (shellCommand);
|
||||
ChildProcess shellProcess;
|
||||
|
||||
if (! shellProcess.start (argList))
|
||||
{
|
||||
addError ("Failed to run shell command: " + argList.joinIntoString (" "));
|
||||
return;
|
||||
}
|
||||
|
||||
if (! shellProcess.waitForProcessToFinish (10000))
|
||||
{
|
||||
addError ("Timeout running shell command: " + argList.joinIntoString (" "));
|
||||
return;
|
||||
}
|
||||
|
||||
auto exitCode = shellProcess.getExitCode();
|
||||
|
||||
if (exitCode != 0)
|
||||
addError ("Shell command: " + argList.joinIntoString (" ") + " failed with exit code: " + String (exitCode));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectSaver::saveExporter (ProjectExporter& exporter, const OwnedArray<LibraryModule>& modules)
|
||||
{
|
||||
try
|
||||
{
|
||||
exporter.create (modules);
|
||||
|
||||
if (! exporter.isCLion())
|
||||
{
|
||||
auto outputString = "Finished saving: " + exporter.getUniqueName();
|
||||
|
||||
if (MessageManager::getInstance()->isThisTheMessageThread())
|
||||
std::cout << outputString << std::endl;
|
||||
else
|
||||
MessageManager::callAsync ([outputString] { std::cout << outputString << std::endl; });
|
||||
}
|
||||
}
|
||||
catch (build_tools::SaveError& error)
|
||||
{
|
||||
addError (error.message);
|
||||
}
|
||||
}
|
138
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.h
vendored
Normal file
138
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ProjectSaver.h
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "jucer_ResourceFile.h"
|
||||
#include "../Project/Modules/jucer_Modules.h"
|
||||
#include "jucer_ProjectExporter.h"
|
||||
|
||||
//==============================================================================
|
||||
class ProjectSaver
|
||||
{
|
||||
public:
|
||||
ProjectSaver (Project& projectToSave);
|
||||
|
||||
void save (Async async, ProjectExporter* exporterToSave, std::function<void (Result)> onCompletion);
|
||||
Result saveResourcesOnly();
|
||||
void saveBasicProjectItems (const OwnedArray<LibraryModule>& modules, const String& appConfigUserContent);
|
||||
|
||||
Project& getProject() { return project; }
|
||||
|
||||
Project::Item addFileToGeneratedGroup (const File& file);
|
||||
bool copyFolder (const File& source, const File& dest);
|
||||
|
||||
static String getJuceCodeGroupName() { return "JUCE Library Code"; }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct SaveThreadWithProgressWindow : public ThreadWithProgressWindow
|
||||
{
|
||||
public:
|
||||
SaveThreadWithProgressWindow (ProjectSaver& ps,
|
||||
ProjectExporter* exporterToSave,
|
||||
std::function<void (Result)> onCompletionIn)
|
||||
: ThreadWithProgressWindow ("Saving...", true, false),
|
||||
saver (ps),
|
||||
specifiedExporterToSave (exporterToSave),
|
||||
onCompletion (std::move (onCompletionIn))
|
||||
{
|
||||
jassert (onCompletion != nullptr);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
setProgress (-1);
|
||||
const auto result = saver.saveProject (specifiedExporterToSave);
|
||||
const auto callback = onCompletion;
|
||||
|
||||
MessageManager::callAsync ([callback, result] { callback (result); });
|
||||
}
|
||||
|
||||
private:
|
||||
ProjectSaver& saver;
|
||||
ProjectExporter* specifiedExporterToSave;
|
||||
std::function<void (Result)> onCompletion;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (SaveThreadWithProgressWindow)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Project::Item saveGeneratedFile (const String& filePath, const MemoryOutputStream& newData);
|
||||
bool replaceFileIfDifferent (const File& f, const MemoryOutputStream& newData);
|
||||
bool deleteUnwantedFilesIn (const File& parent);
|
||||
|
||||
void addError (const String& message);
|
||||
|
||||
File getAppConfigFile() const;
|
||||
File getPluginDefinesFile() const;
|
||||
|
||||
String loadUserContentFromAppConfig() const;
|
||||
String getAudioPluginDefines() const;
|
||||
OwnedArray<LibraryModule> getModules();
|
||||
|
||||
Result saveProject (ProjectExporter* specifiedExporterToSave);
|
||||
void saveProjectAsync (ProjectExporter* exporterToSave, std::function<void (Result)> onCompletion);
|
||||
|
||||
template <typename WriterCallback>
|
||||
void writeOrRemoveGeneratedFile (const String& name, WriterCallback&& writerCallback);
|
||||
|
||||
void writePluginDefines (MemoryOutputStream& outStream) const;
|
||||
void writePluginDefines();
|
||||
void writeAppConfigFile (const OwnedArray<LibraryModule>& modules, const String& userContent);
|
||||
|
||||
void writeProjectFile();
|
||||
void writeAppConfig (MemoryOutputStream& outStream, const OwnedArray<LibraryModule>& modules, const String& userContent);
|
||||
void writeAppHeader (MemoryOutputStream& outStream, const OwnedArray<LibraryModule>& modules);
|
||||
void writeAppHeader (const OwnedArray<LibraryModule>& modules);
|
||||
void writeModuleCppWrappers (const OwnedArray<LibraryModule>& modules);
|
||||
void writeBinaryDataFiles();
|
||||
void writeReadmeFile();
|
||||
void writePluginCharacteristicsFile();
|
||||
void writeUnityScriptFile();
|
||||
void writeProjects (const OwnedArray<LibraryModule>&, ProjectExporter*);
|
||||
void runPostExportScript();
|
||||
void saveExporter (ProjectExporter& exporter, const OwnedArray<LibraryModule>& modules);
|
||||
|
||||
//==============================================================================
|
||||
Project& project;
|
||||
|
||||
File generatedCodeFolder;
|
||||
Project::Item generatedFilesGroup;
|
||||
SortedSet<File> filesCreated;
|
||||
String projectLineFeed;
|
||||
|
||||
CriticalSection errorLock;
|
||||
StringArray errors;
|
||||
|
||||
std::unique_ptr<SaveThreadWithProgressWindow> saveThread;
|
||||
|
||||
bool hasBinaryData = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectSaver)
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (ProjectSaver)
|
||||
};
|
52
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ResourceFile.cpp
vendored
Normal file
52
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ResourceFile.cpp
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#include "../Application/jucer_Headers.h"
|
||||
#include "jucer_ResourceFile.h"
|
||||
|
||||
//==============================================================================
|
||||
JucerResourceFile::JucerResourceFile (Project& p) : project (p)
|
||||
{
|
||||
addResourcesFromProjectItem (project.getMainGroup());
|
||||
}
|
||||
|
||||
JucerResourceFile::~JucerResourceFile()
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void JucerResourceFile::addResourcesFromProjectItem (const Project::Item& projectItem)
|
||||
{
|
||||
if (projectItem.isGroup())
|
||||
{
|
||||
for (int i = 0; i < projectItem.getNumChildren(); ++i)
|
||||
addResourcesFromProjectItem (projectItem.getChild(i));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (projectItem.shouldBeAddedToBinaryResources())
|
||||
addFile (projectItem.getFile());
|
||||
}
|
||||
}
|
67
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ResourceFile.h
vendored
Normal file
67
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_ResourceFile.h
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../Project/jucer_Project.h"
|
||||
|
||||
//==============================================================================
|
||||
class JucerResourceFile
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
explicit JucerResourceFile (Project& project);
|
||||
~JucerResourceFile();
|
||||
|
||||
//==============================================================================
|
||||
void setClassName (const String& className) { resourceFile.setClassName (className); }
|
||||
String getClassName() const { return resourceFile.getClassName(); }
|
||||
|
||||
void addFile (const File& file) { resourceFile.addFile (file); }
|
||||
String getDataVariableFor (const File& file) const { return resourceFile.getDataVariableFor (file); }
|
||||
String getSizeVariableFor (const File& file) const { return resourceFile.getSizeVariableFor (file); }
|
||||
|
||||
int getNumFiles() const { return resourceFile.getNumFiles(); }
|
||||
const File& getFile (int index) const { return resourceFile.getFile (index); }
|
||||
|
||||
int64 getTotalDataSize() const { return resourceFile.getTotalDataSize(); }
|
||||
|
||||
build_tools::ResourceFile::WriteResult write (int maxFileSize)
|
||||
{
|
||||
return resourceFile.write (maxFileSize,
|
||||
project.getProjectLineFeed(),
|
||||
project.getBinaryDataHeaderFile(),
|
||||
[this] (int index) { return project.getBinaryDataCppFile (index); });
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
private:
|
||||
void addResourcesFromProjectItem (const Project::Item& node);
|
||||
|
||||
Project& project;
|
||||
build_tools::ResourceFile resourceFile;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JucerResourceFile)
|
||||
};
|
253
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_XcodeProjectParser.h
vendored
Normal file
253
deps/juce/extras/Projucer/Source/ProjectSaving/jucer_XcodeProjectParser.h
vendored
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
|
||||
//==============================================================================
|
||||
class XcodeProjectParser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static std::unique_ptr<HashMap<std::string, std::string>> parseObjects (const File& projectFile)
|
||||
{
|
||||
auto pbxprojs = projectFile.findChildFiles (File::TypesOfFileToFind::findFiles, false, "*.pbxproj");
|
||||
|
||||
if (pbxprojs.isEmpty())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto content = pbxprojs[0].loadFileAsString().toStdString();
|
||||
|
||||
std::regex comments ("/\\*.*?\\*/");
|
||||
std::string empty ("");
|
||||
content = (std::regex_replace (content, comments, empty));
|
||||
|
||||
std::regex whitespace ("\\s+");
|
||||
std::string space (" ");
|
||||
content = (std::regex_replace (content, whitespace, space));
|
||||
|
||||
auto objects = std::make_unique<HashMap<std::string, std::string>>();
|
||||
std::smatch objectsStartMatch;
|
||||
|
||||
if (! std::regex_search (content, objectsStartMatch, std::regex ("[ ;{]+objects *= *\\{")))
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto strPtr = content.begin() + objectsStartMatch.position() + objectsStartMatch.length();
|
||||
|
||||
while (strPtr++ != content.end())
|
||||
{
|
||||
if (*strPtr == ' ' || *strPtr == ';')
|
||||
continue;
|
||||
|
||||
if (*strPtr == '}')
|
||||
break;
|
||||
|
||||
auto groupReference = parseObjectID (content, strPtr);
|
||||
|
||||
if (groupReference.empty())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (*strPtr == ' ' || *strPtr == '=')
|
||||
{
|
||||
if (++strPtr == content.end())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto bracedContent = parseBracedContent (content, strPtr);
|
||||
|
||||
if (bracedContent.empty())
|
||||
return nullptr;
|
||||
|
||||
objects->set (groupReference, bracedContent);
|
||||
}
|
||||
|
||||
jassert (strPtr <= content.end());
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> findObjectMatching (const HashMap<std::string, std::string>& objects,
|
||||
const std::regex& rgx)
|
||||
{
|
||||
HashMap<std::string, std::string>::Iterator it (objects);
|
||||
std::smatch match;
|
||||
|
||||
while (it.next())
|
||||
{
|
||||
auto key = it.getValue();
|
||||
|
||||
if (std::regex_search (key, match, rgx))
|
||||
return { it.getKey(), it.getValue() };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct BuildProduct
|
||||
{
|
||||
String name;
|
||||
String path;
|
||||
};
|
||||
|
||||
static std::vector<BuildProduct> parseBuildProducts (const File& projectFile)
|
||||
{
|
||||
auto objects = parseObjects (projectFile);
|
||||
|
||||
if (objects == nullptr)
|
||||
return {};
|
||||
|
||||
auto mainObject = findObjectMatching (*objects, std::regex ("[ ;{]+isa *= *PBXProject[ ;}]+"));
|
||||
jassert (! mainObject.first.empty());
|
||||
|
||||
auto targetRefs = parseObjectItemList (mainObject.second, "targets");
|
||||
jassert (! targetRefs.isEmpty());
|
||||
|
||||
std::vector<BuildProduct> results;
|
||||
|
||||
for (auto& t : targetRefs)
|
||||
{
|
||||
auto targetRef = t.toStdString();
|
||||
|
||||
if (! objects->contains (targetRef))
|
||||
{
|
||||
jassertfalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto name = parseObjectItemValue (objects->getReference (targetRef), "name");
|
||||
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
auto productRef = parseObjectItemValue (objects->getReference (targetRef), "productReference");
|
||||
|
||||
if (productRef.empty())
|
||||
continue;
|
||||
|
||||
if (! objects->contains (productRef))
|
||||
{
|
||||
jassertfalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = parseObjectItemValue (objects->getReference (productRef), "path");
|
||||
|
||||
if (path.empty())
|
||||
continue;
|
||||
|
||||
results.push_back ({ String (name).unquoted(), String (path).unquoted() });
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static std::string parseObjectID (std::string& content, std::string::iterator& ptr)
|
||||
{
|
||||
auto start = ptr;
|
||||
|
||||
while (ptr != content.end() && *ptr != ' ' && *ptr != ';' && *ptr != '=')
|
||||
++ptr;
|
||||
|
||||
return ptr == content.end() ? std::string()
|
||||
: content.substr ((size_t) std::distance (content.begin(), start),
|
||||
(size_t) std::distance (start, ptr));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::string parseBracedContent (std::string& content, std::string::iterator& ptr)
|
||||
{
|
||||
jassert (*ptr == '{');
|
||||
auto start = ++ptr;
|
||||
auto braceDepth = 1;
|
||||
|
||||
while (ptr++ != content.end())
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case '{':
|
||||
++braceDepth;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if (--braceDepth == 0)
|
||||
return content.substr ((size_t) std::distance (content.begin(), start),
|
||||
(size_t) std::distance (start, ptr));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::string parseObjectItemValue (const std::string& source, const std::string& key)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *(.*?) *;")))
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
return match[1];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static StringArray parseObjectItemList (const std::string& source, const std::string& key)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *\\((.*?)\\)")))
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = StringArray::fromTokens (String (match[1]), ", ", "");
|
||||
result.removeEmptyStrings();
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user