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:
essej
2022-04-18 17:51:22 -04:00
parent 63e175fee6
commit 25bd5d8adb
3210 changed files with 1045392 additions and 0 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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)
};

View 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);
}
}

View 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)
};

View 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());
}
}

View 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)
};

View 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;
}
};