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

View File

@ -0,0 +1,47 @@
/*
==============================================================================
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.
==============================================================================
*/
#ifdef JUCE_BUILD_TOOLS_H_INCLUDED
/* When you add this cpp file to your project, you mustn't include it in a file where you've
already included any other headers - just put it inside a file on its own, possibly with your config
flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix
header files that the compiler may be using.
*/
#error "Incorrect use of JUCE cpp file"
#endif
#include "juce_build_tools.h"
#include <numeric>
#include "utils/juce_CppTokeniserFunctions.cpp"
#include "utils/juce_BuildHelperFunctions.cpp"
#include "utils/juce_BinaryResourceFile.cpp"
#include "utils/juce_Icons.cpp"
#include "utils/juce_PlistOptions.cpp"
#include "utils/juce_ResourceFileHelpers.cpp"
#include "utils/juce_ResourceRc.cpp"
#include "utils/juce_VersionNumbers.cpp"
#include "utils/juce_Entitlements.cpp"

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.
==============================================================================
*/
/*******************************************************************************
The block below describes the properties of this module, and is read by
the Projucer to automatically generate project code that uses it.
For details about the syntax and how to create or use a module, see the
JUCE Module Format.md file.
BEGIN_JUCE_MODULE_DECLARATION
ID: juce_build_tools
vendor: juce
version: 6.1.2
name: JUCE Build Tools
description: Classes for generating intermediate files for JUCE projects.
website: http://www.juce.com/juce
license: GPL/Commercial
dependencies: juce_gui_basics
END_JUCE_MODULE_DECLARATION
*******************************************************************************/
// This module is shared by juceaide and the Projucer, but should not be
// considered 'public'. That is, its API, functionality, and contents (and
// existence!) may change between releases without warning.
#pragma once
#define JUCE_BUILD_TOOLS_H_INCLUDED
#include <juce_gui_basics/juce_gui_basics.h>
#include "utils/juce_ProjectType.h"
#include "utils/juce_BuildHelperFunctions.h"
#include "utils/juce_BinaryResourceFile.h"
#include "utils/juce_RelativePath.h"
#include "utils/juce_Icons.h"
#include "utils/juce_PlistOptions.h"
#include "utils/juce_ResourceFileHelpers.h"
#include "utils/juce_ResourceRc.h"
#include "utils/juce_VersionNumbers.h"
#include "utils/juce_Entitlements.h"

View File

@ -0,0 +1,289 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
static const char* resourceFileIdentifierString = "JUCER_BINARY_RESOURCE";
//==============================================================================
void ResourceFile::setClassName (const String& name)
{
className = name;
}
void ResourceFile::addFile (const File& file)
{
files.add (file);
auto variableNameRoot = makeBinaryDataIdentifierName (file);
auto variableName = variableNameRoot;
int suffix = 2;
while (variableNames.contains (variableName))
variableName = variableNameRoot + String (suffix++);
variableNames.add (variableName);
}
String ResourceFile::getDataVariableFor (const File& file) const
{
const auto index = files.indexOf (file);
jassert (index >= 0);
return variableNames[index];
}
String ResourceFile::getSizeVariableFor (const File& file) const
{
return getDataVariableFor (file) + "Size";
}
int64 ResourceFile::getTotalDataSize() const
{
return std::accumulate (files.begin(),
files.end(),
int64 { 0 },
[] (int64 acc, const File& f) { return acc + f.getSize(); });
}
static void writeComment (MemoryOutputStream& mo)
{
mo << newLine << newLine
<< " This is an auto-generated file: Any edits you make may be overwritten!" << newLine
<< newLine
<< "*/" << newLine
<< newLine;
}
Result ResourceFile::writeHeader (MemoryOutputStream& header)
{
header << "/* =========================================================================================";
writeComment (header);
header << "#pragma once" << newLine
<< newLine
<< "namespace " << className << newLine
<< "{" << newLine;
for (int i = 0; i < files.size(); ++i)
{
auto& file = files.getReference(i);
if (! file.existsAsFile())
return Result::fail ("Can't open resource file: " + file.getFullPathName());
auto dataSize = file.getSize();
auto variableName = variableNames[i];
FileInputStream fileStream (file);
if (fileStream.openedOk())
{
header << " extern const char* " << variableName << ";" << newLine;
header << " const int " << variableName << "Size = " << (int) dataSize << ";" << newLine << newLine;
}
}
header << " // Number of elements in the namedResourceList and originalFileNames arrays." << newLine
<< " const int namedResourceListSize = " << files.size() << ";" << newLine
<< newLine
<< " // Points to the start of a list of resource names." << newLine
<< " extern const char* namedResourceList[];" << newLine
<< newLine
<< " // Points to the start of a list of resource filenames." << newLine
<< " extern const char* originalFilenames[];" << newLine
<< newLine
<< " // If you provide the name of one of the binary resource variables above, this function will" << newLine
<< " // return the corresponding data and its size (or a null pointer if the name isn't found)." << newLine
<< " const char* getNamedResource (const char* resourceNameUTF8, int& dataSizeInBytes);" << newLine
<< newLine
<< " // If you provide the name of one of the binary resource variables above, this function will" << newLine
<< " // return the corresponding original, non-mangled filename (or a null pointer if the name isn't found)." << newLine
<< " const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8);" << newLine
<< "}" << newLine;
return Result::ok();
}
Result ResourceFile::writeCpp (MemoryOutputStream& cpp, const File& headerFile, int& i, const int maxFileSize)
{
bool isFirstFile = (i == 0);
cpp << "/* ==================================== " << resourceFileIdentifierString << " ====================================";
writeComment (cpp);
cpp << "namespace " << className << newLine
<< "{" << newLine;
while (i < files.size())
{
auto& file = files.getReference(i);
auto variableName = variableNames[i];
FileInputStream fileStream (file);
if (fileStream.openedOk())
{
auto tempVariable = "temp_binary_data_" + String (i);
cpp << newLine << "//================== " << file.getFileName() << " ==================" << newLine
<< "static const unsigned char " << tempVariable << "[] =" << newLine;
{
MemoryBlock data;
fileStream.readIntoMemoryBlock (data);
writeDataAsCppLiteral (data, cpp, true, true);
}
cpp << newLine << newLine
<< "const char* " << variableName << " = (const char*) " << tempVariable << ";" << newLine;
}
++i;
if (cpp.getPosition() > maxFileSize)
break;
}
if (isFirstFile)
{
if (i < files.size())
{
cpp << newLine
<< "}" << newLine
<< newLine
<< "#include \"" << headerFile.getFileName() << "\"" << newLine
<< newLine
<< "namespace " << className << newLine
<< "{";
}
cpp << newLine
<< newLine
<< "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes);" << newLine
<< "const char* getNamedResource (const char* resourceNameUTF8, int& numBytes)" << newLine
<< "{" << newLine;
StringArray returnCodes;
for (auto& file : files)
{
auto dataSize = file.getSize();
returnCodes.add ("numBytes = " + String (dataSize) + "; return " + variableNames[files.indexOf (file)] + ";");
}
createStringMatcher (cpp, "resourceNameUTF8", variableNames, returnCodes, 4);
cpp << " numBytes = 0;" << newLine
<< " return nullptr;" << newLine
<< "}" << newLine
<< newLine;
cpp << "const char* namedResourceList[] =" << newLine
<< "{" << newLine;
for (int j = 0; j < files.size(); ++j)
cpp << " " << variableNames[j].quoted() << (j < files.size() - 1 ? "," : "") << newLine;
cpp << "};" << newLine << newLine;
cpp << "const char* originalFilenames[] =" << newLine
<< "{" << newLine;
for (auto& f : files)
cpp << " " << f.getFileName().quoted() << (files.indexOf (f) < files.size() - 1 ? "," : "") << newLine;
cpp << "};" << newLine << newLine;
cpp << "const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8);" << newLine
<< "const char* getNamedResourceOriginalFilename (const char* resourceNameUTF8)" << newLine
<< "{" << newLine
<< " for (unsigned int i = 0; i < (sizeof (namedResourceList) / sizeof (namedResourceList[0])); ++i)" << newLine
<< " {" << newLine
<< " if (namedResourceList[i] == resourceNameUTF8)" << newLine
<< " return originalFilenames[i];" << newLine
<< " }" << newLine
<< newLine
<< " return nullptr;" << newLine
<< "}" << newLine
<< newLine;
}
cpp << "}" << newLine;
return Result::ok();
}
ResourceFile::WriteResult ResourceFile::write (int maxFileSize,
String projectLineFeed,
File headerFile,
std::function<File (int)> getCppFile)
{
Array<File> filesCreated;
{
MemoryOutputStream mo;
mo.setNewLineString (projectLineFeed);
auto r = writeHeader (mo);
if (r.failed())
return { r, {} };
if (! overwriteFileWithNewDataIfDifferent (headerFile, mo))
return { Result::fail ("Can't write to file: " + headerFile.getFullPathName()), {} };
filesCreated.add (headerFile);
}
int i = 0;
int fileIndex = 0;
for (;;)
{
auto cpp = getCppFile (fileIndex);
MemoryOutputStream mo;
mo.setNewLineString (projectLineFeed);
auto r = writeCpp (mo, headerFile, i, maxFileSize);
if (r.failed())
return { r, std::move (filesCreated) };
if (! overwriteFileWithNewDataIfDifferent (cpp, mo))
return { Result::fail ("Can't write to file: " + cpp.getFullPathName()), std::move (filesCreated) };
filesCreated.add (cpp);
++fileIndex;
if (i >= files.size())
break;
}
return { Result::ok(), std::move (filesCreated) };
}
}
}

View File

@ -0,0 +1,74 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
class ResourceFile
{
public:
ResourceFile() = default;
void setClassName (const String& className);
String getClassName() const { return className; }
void addFile (const File& file);
String getDataVariableFor (const File& file) const;
String getSizeVariableFor (const File& file) const;
int getNumFiles() const { return files.size (); }
const File& getFile (int index) const { return files.getReference (index); }
int64 getTotalDataSize() const;
struct WriteResult
{
Result result;
Array<File> filesCreated;
};
WriteResult write (int maxFileSize,
String projectLineFeed,
File headerFile,
std::function<File (int)> getCppFile);
private:
Array<File> files;
StringArray variableNames;
String className { "BinaryData" };
Result writeHeader (MemoryOutputStream&);
Result writeCpp (MemoryOutputStream&, const File&, int&, int);
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResourceFile)
};
}
}

View File

@ -0,0 +1,355 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
void overwriteFileIfDifferentOrThrow (const File& file, const MemoryOutputStream& newData)
{
if (! overwriteFileWithNewDataIfDifferent (file, newData))
throw SaveError (file);
}
void overwriteFileIfDifferentOrThrow (const File& file, const String& newData)
{
if (! overwriteFileWithNewDataIfDifferent (file, newData))
throw SaveError (file);
}
String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString)
{
for (int i = 0; i < definitions.size(); ++i)
{
const String key (definitions.getAllKeys()[i]);
const String value (definitions.getAllValues()[i]);
sourceString = sourceString.replace ("${" + key + "}", value);
}
return sourceString;
}
String getXcodePackageType (ProjectType::Target::Type type)
{
using Type = ProjectType::Target::Type;
switch (type)
{
case Type::GUIApp:
case Type::StandalonePlugIn:
return "APPL";
case Type::VSTPlugIn:
case Type::VST3PlugIn:
case Type::AudioUnitPlugIn:
case Type::UnityPlugIn:
return "BNDL";
case Type::AudioUnitv3PlugIn:
return "XPC!";
case Type::AAXPlugIn:
case Type::RTASPlugIn:
return "TDMw";
case Type::ConsoleApp:
case Type::StaticLibrary:
case Type::DynamicLibrary:
case Type::SharedCodeTarget:
case Type::AggregateTarget:
case Type::unspecified:
default:
return {};
}
}
String getXcodeBundleSignature (ProjectType::Target::Type type)
{
using Type = ProjectType::Target::Type;
switch (type)
{
case Type::GUIApp:
case Type::VSTPlugIn:
case Type::VST3PlugIn:
case Type::AudioUnitPlugIn:
case Type::StandalonePlugIn:
case Type::AudioUnitv3PlugIn:
case Type::UnityPlugIn:
return "????";
case Type::AAXPlugIn:
case Type::RTASPlugIn:
return "PTul";
case Type::ConsoleApp:
case Type::StaticLibrary:
case Type::DynamicLibrary:
case Type::SharedCodeTarget:
case Type::AggregateTarget:
case Type::unspecified:
default:
return {};
}
}
static unsigned int calculateHash (const String& s, const unsigned int hashMultiplier)
{
auto t = s.toUTF8();
unsigned int hash = 0;
while (*t != 0)
hash = hashMultiplier * hash + (unsigned int) *t++;
return hash;
}
static unsigned int findBestHashMultiplier (const StringArray& strings)
{
unsigned int v = 31;
for (;;)
{
SortedSet<unsigned int> hashes;
bool collision = false;
for (int i = strings.size(); --i >= 0;)
{
auto hash = calculateHash (strings[i], v);
if (hashes.contains (hash))
{
collision = true;
break;
}
hashes.add (hash);
}
if (! collision)
break;
v += 2;
}
return v;
}
String makeValidIdentifier (String s, bool makeCamelCase, bool removeColons, bool allowTemplates, bool allowAsterisks)
{
if (s.isEmpty())
return "unknown";
if (removeColons)
s = s.replaceCharacters (".,;:/@", "______");
else
s = s.replaceCharacters (".,;/@", "_____");
for (int i = s.length(); --i > 0;)
if (CharacterFunctions::isLetter (s[i])
&& CharacterFunctions::isLetter (s[i - 1])
&& CharacterFunctions::isUpperCase (s[i])
&& ! CharacterFunctions::isUpperCase (s[i - 1]))
s = s.substring (0, i) + " " + s.substring (i);
String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
if (allowTemplates)
allowedChars += "<>";
if (! removeColons)
allowedChars += ":";
if (allowAsterisks)
allowedChars += "*";
StringArray words;
words.addTokens (s.retainCharacters (allowedChars), false);
words.trim();
auto n = words[0];
if (makeCamelCase)
n = n.toLowerCase();
for (int i = 1; i < words.size(); ++i)
{
if (makeCamelCase && words[i].length() > 1)
n << words[i].substring (0, 1).toUpperCase()
<< words[i].substring (1).toLowerCase();
else
n << words[i];
}
if (CharacterFunctions::isDigit (n[0]))
n = "_" + n;
if (isReservedKeyword (n))
n << '_';
return n;
}
String makeBinaryDataIdentifierName (const File& file)
{
return makeValidIdentifier (file.getFileName()
.replaceCharacters (" .", "__")
.retainCharacters ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789"),
false, true, false);
}
void writeDataAsCppLiteral (const MemoryBlock& mb, OutputStream& out,
bool breakAtNewLines, bool allowStringBreaks)
{
const int maxCharsOnLine = 250;
auto data = (const unsigned char*) mb.getData();
int charsOnLine = 0;
bool canUseStringLiteral = mb.getSize() < 32768; // MS compilers can't handle big string literals..
if (canUseStringLiteral)
{
unsigned int numEscaped = 0;
for (size_t i = 0; i < mb.getSize(); ++i)
{
auto num = (unsigned int) data[i];
if (! ((num >= 32 && num < 127) || num == '\t' || num == '\r' || num == '\n'))
{
if (++numEscaped > mb.getSize() / 4)
{
canUseStringLiteral = false;
break;
}
}
}
}
if (! canUseStringLiteral)
{
out << "{ ";
for (size_t i = 0; i < mb.getSize(); ++i)
{
auto num = (int) (unsigned int) data[i];
out << num << ',';
charsOnLine += 2;
if (num >= 10)
{
++charsOnLine;
if (num >= 100)
++charsOnLine;
}
if (charsOnLine >= maxCharsOnLine)
{
charsOnLine = 0;
out << newLine;
}
}
out << "0,0 };";
}
else
{
out << "\"";
writeEscapeChars (out, (const char*) data, (int) mb.getSize(),
maxCharsOnLine, breakAtNewLines, false, allowStringBreaks);
out << "\";";
}
}
void createStringMatcher (OutputStream& out, const String& utf8PointerVariable,
const StringArray& strings, const StringArray& codeToExecute, const int indentLevel)
{
jassert (strings.size() == codeToExecute.size());
auto indent = String::repeatedString (" ", indentLevel);
auto hashMultiplier = findBestHashMultiplier (strings);
out << indent << "unsigned int hash = 0;" << newLine
<< newLine
<< indent << "if (" << utf8PointerVariable << " != nullptr)" << newLine
<< indent << " while (*" << utf8PointerVariable << " != 0)" << newLine
<< indent << " hash = " << (int) hashMultiplier << " * hash + (unsigned int) *" << utf8PointerVariable << "++;" << newLine
<< newLine
<< indent << "switch (hash)" << newLine
<< indent << "{" << newLine;
for (int i = 0; i < strings.size(); ++i)
{
out << indent << " case 0x" << hexString8Digits ((int) calculateHash (strings[i], hashMultiplier))
<< ": " << codeToExecute[i] << newLine;
}
out << indent << " default: break;" << newLine
<< indent << "}" << newLine << newLine;
}
String unixStylePath (const String& path) { return path.replaceCharacter ('\\', '/'); }
String windowsStylePath (const String& path) { return path.replaceCharacter ('/', '\\'); }
String currentOSStylePath (const String& path)
{
#if JUCE_WINDOWS
return windowsStylePath (path);
#else
return unixStylePath (path);
#endif
}
bool isAbsolutePath (const String& path)
{
return File::isAbsolutePath (path)
|| path.startsWithChar ('/') // (needed because File::isAbsolutePath will ignore forward-slashes on Windows)
|| path.startsWithChar ('$')
|| path.startsWithChar ('~')
|| (CharacterFunctions::isLetter (path[0]) && path[1] == ':')
|| path.startsWithIgnoreCase ("smb:");
}
String getRelativePathFrom (const File& file, const File& sourceFolder)
{
#if ! JUCE_WINDOWS
// On a non-windows machine, we can't know if a drive-letter path may be relative or not.
if (CharacterFunctions::isLetter (file.getFullPathName()[0]) && file.getFullPathName()[1] == ':')
return file.getFullPathName();
#endif
return file.getRelativePathFrom (sourceFolder);
}
void writeStreamToFile (const File& file, const std::function<void (MemoryOutputStream&)>& writer)
{
MemoryOutputStream mo;
writer (mo);
overwriteFileIfDifferentOrThrow (file, mo);
}
}
}

View File

@ -0,0 +1,86 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
void overwriteFileIfDifferentOrThrow (const File& file, const MemoryOutputStream& newData);
void overwriteFileIfDifferentOrThrow (const File& file, const String& newData);
class SaveError
{
public:
SaveError (const String& error) : message (error)
{}
SaveError (const File& fileThatFailedToWrite)
: message ("Can't write to the file: " + fileThatFailedToWrite.getFullPathName())
{}
String message;
};
String replacePreprocessorDefs (const StringPairArray& definitions, String sourceString);
String getXcodePackageType (ProjectType::Target::Type);
String getXcodeBundleSignature (ProjectType::Target::Type);
inline String hexString8Digits (int value)
{
return String::toHexString (value).paddedLeft ('0', 8);
}
String makeValidIdentifier (String s,
bool makeCamelCase,
bool removeColons,
bool allowTemplates,
bool allowAsterisks = false);
String makeBinaryDataIdentifierName (const File& file);
void writeDataAsCppLiteral (const MemoryBlock& mb,
OutputStream& out,
bool breakAtNewLines,
bool allowStringBreaks);
void createStringMatcher (OutputStream& out,
const String& utf8PointerVariable,
const StringArray& strings,
const StringArray& codeToExecute,
const int indentLevel);
String unixStylePath (const String& path);
String windowsStylePath (const String& path);
String currentOSStylePath (const String& path);
bool isAbsolutePath (const String& path);
// A windows-aware version of File::getRelativePath()
String getRelativePathFrom (const File& file, const File& sourceFolder);
void writeStreamToFile (const File& file, const std::function<void (MemoryOutputStream&)>& writer);
}
}

View File

@ -0,0 +1,206 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept
{
static const char* const keywords2Char[] =
{ "do", "if", "or", nullptr };
static const char* const keywords3Char[] =
{ "and", "asm", "for", "int", "new", "not", "try", "xor", nullptr };
static const char* const keywords4Char[] =
{ "auto", "bool", "case", "char", "else", "enum", "goto",
"long", "this", "true", "void", nullptr };
static const char* const keywords5Char[] =
{ "bitor", "break", "catch", "class", "compl", "const", "false", "final",
"float", "or_eq", "short", "throw", "union", "using", "while", nullptr };
static const char* const keywords6Char[] =
{ "and_eq", "bitand", "delete", "double", "export", "extern", "friend",
"import", "inline", "module", "not_eq", "public", "return", "signed",
"sizeof", "static", "struct", "switch", "typeid", "xor_eq", nullptr };
static const char* const keywords7Char[] =
{ "__cdecl", "_Pragma", "alignas", "alignof", "concept", "default",
"mutable", "nullptr", "private", "typedef", "uint8_t", "virtual",
"wchar_t", nullptr };
static const char* const keywordsOther[] =
{ "@class", "@dynamic", "@end", "@implementation", "@interface", "@public",
"@private", "@protected", "@property", "@synthesize", "__fastcall", "__stdcall",
"atomic_cancel", "atomic_commit", "atomic_noexcept", "char16_t", "char32_t",
"co_await", "co_return", "co_yield", "const_cast", "constexpr", "continue",
"decltype", "dynamic_cast", "explicit", "namespace", "noexcept", "operator", "override",
"protected", "register", "reinterpret_cast", "requires", "static_assert",
"static_cast", "synchronized", "template", "thread_local", "typename", "unsigned",
"volatile", nullptr };
const char* const* k;
switch (tokenLength)
{
case 2: k = keywords2Char; break;
case 3: k = keywords3Char; break;
case 4: k = keywords4Char; break;
case 5: k = keywords5Char; break;
case 6: k = keywords6Char; break;
case 7: k = keywords7Char; break;
default:
if (tokenLength < 2 || tokenLength > 16)
return false;
k = keywordsOther;
break;
}
for (int i = 0; k[i] != nullptr; ++i)
if (token.compare (CharPointer_ASCII (k[i])) == 0)
return true;
return false;
}
static bool isReservedKeyword (const String& token) noexcept
{
return isReservedKeyword (token.getCharPointer(), token.length());
}
//==============================================================================
/** Takes a UTF8 string and writes it to a stream using standard C++ escape sequences for any
non-ascii bytes.
Although not strictly a tokenising function, this is still a function that often comes in
handy when working with C++ code!
Note that addEscapeChars() is easier to use than this function if you're working with Strings.
@see addEscapeChars
*/
static void writeEscapeChars (OutputStream& out, const char* utf8, const int numBytesToRead,
const int maxCharsOnLine, const bool breakAtNewLines,
const bool replaceSingleQuotes, const bool allowStringBreaks)
{
int charsOnLine = 0;
bool lastWasHexEscapeCode = false;
bool trigraphDetected = false;
for (int i = 0; i < numBytesToRead || numBytesToRead < 0; ++i)
{
auto c = (unsigned char) utf8[i];
bool startNewLine = false;
switch (c)
{
case '\t': out << "\\t"; trigraphDetected = false; lastWasHexEscapeCode = false; charsOnLine += 2; break;
case '\r': out << "\\r"; trigraphDetected = false; lastWasHexEscapeCode = false; charsOnLine += 2; break;
case '\n': out << "\\n"; trigraphDetected = false; lastWasHexEscapeCode = false; charsOnLine += 2; startNewLine = breakAtNewLines; break;
case '\\': out << "\\\\"; trigraphDetected = false; lastWasHexEscapeCode = false; charsOnLine += 2; break;
case '\"': out << "\\\""; trigraphDetected = false; lastWasHexEscapeCode = false; charsOnLine += 2; break;
case '?':
if (trigraphDetected)
{
out << "\\?";
charsOnLine++;
trigraphDetected = false;
}
else
{
out << "?";
trigraphDetected = true;
}
lastWasHexEscapeCode = false;
charsOnLine++;
break;
case 0:
if (numBytesToRead < 0)
return;
out << "\\0";
lastWasHexEscapeCode = true;
trigraphDetected = false;
charsOnLine += 2;
break;
case '\'':
if (replaceSingleQuotes)
{
out << "\\\'";
lastWasHexEscapeCode = false;
trigraphDetected = false;
charsOnLine += 2;
break;
}
// deliberate fall-through...
default:
if (c >= 32 && c < 127 && ! (lastWasHexEscapeCode // (have to avoid following a hex escape sequence with a valid hex digit)
&& CharacterFunctions::getHexDigitValue (c) >= 0))
{
out << (char) c;
lastWasHexEscapeCode = false;
trigraphDetected = false;
++charsOnLine;
}
else if (allowStringBreaks && lastWasHexEscapeCode && c >= 32 && c < 127)
{
out << "\"\"" << (char) c;
lastWasHexEscapeCode = false;
trigraphDetected = false;
charsOnLine += 3;
}
else
{
out << (c < 16 ? "\\x0" : "\\x") << String::toHexString ((int) c);
lastWasHexEscapeCode = true;
trigraphDetected = false;
charsOnLine += 4;
}
break;
}
if ((startNewLine || (maxCharsOnLine > 0 && charsOnLine >= maxCharsOnLine))
&& (numBytesToRead < 0 || i < numBytesToRead - 1))
{
charsOnLine = 0;
out << "\"" << newLine << "\"";
lastWasHexEscapeCode = false;
}
}
}
}
}

View File

@ -0,0 +1,119 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
String EntitlementOptions::getEntitlementsFileContent() const
{
String content =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
"<dict>\n";
const auto entitlements = getEntitlements();
for (auto& key : entitlements.getAllKeys())
content += "\t<key>" + key + "</key>\n\t" + entitlements[key] + "\n";
return content + "</dict>\n</plist>\n";
}
StringPairArray EntitlementOptions::getEntitlements() const
{
StringPairArray entitlements;
if (isiOS)
{
if (isAudioPluginProject && shouldEnableIAA)
entitlements.set ("inter-app-audio", "<true/>");
if (isiCloudPermissionsEnabled)
{
entitlements.set ("com.apple.developer.icloud-container-identifiers",
"<array>\n"
" <string>iCloud.$(CFBundleIdentifier)</string>\n"
" </array>");
entitlements.set ("com.apple.developer.icloud-services",
"<array>\n"
" <string>CloudDocuments</string>\n"
" </array>");
entitlements.set ("com.apple.developer.ubiquity-container-identifiers",
"<array>\n"
" <string>iCloud.$(CFBundleIdentifier)</string>\n"
" </array>");
}
}
if (isPushNotificationsEnabled)
entitlements.set (isiOS ? "aps-environment"
: "com.apple.developer.aps-environment",
"<string>development</string>");
if (isAppGroupsEnabled)
{
auto appGroups = StringArray::fromTokens (appGroupIdString, ";", {});
auto groups = String ("<array>");
for (auto group : appGroups)
groups += "\n\t\t<string>" + group.trim() + "</string>";
groups += "\n\t</array>";
entitlements.set ("com.apple.security.application-groups", groups);
}
if (isHardenedRuntimeEnabled)
for (auto& option : hardenedRuntimeOptions)
entitlements.set (option, "<true/>");
if (isAppSandboxEnabled || (! isiOS && isAudioPluginProject && type == ProjectType::Target::AudioUnitv3PlugIn))
{
entitlements.set ("com.apple.security.app-sandbox", "<true/>");
if (isAppSandboxInhertianceEnabled)
{
// no other sandbox options can be specified if sandbox inheritance is enabled!
jassert (appSandboxOptions.isEmpty());
entitlements.set ("com.apple.security.inherit", "<true/>");
}
if (isAppSandboxEnabled)
for (auto& option : appSandboxOptions)
entitlements.set (option, "<true/>");
}
if (isNetworkingMulticastEnabled)
entitlements.set ("com.apple.developer.networking.multicast", "<true/>");
return entitlements;
}
}
}

View File

@ -0,0 +1,56 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
struct EntitlementOptions final
{
String getEntitlementsFileContent() const;
ProjectType::Target::Type type = ProjectType::Target::GUIApp;
bool isiOS = false;
bool isAudioPluginProject = false;
bool shouldEnableIAA = false;
bool isiCloudPermissionsEnabled = false;
bool isPushNotificationsEnabled = false;
bool isAppGroupsEnabled = false;
bool isHardenedRuntimeEnabled = false;
bool isAppSandboxEnabled = false;
bool isAppSandboxInhertianceEnabled = false;
bool isNetworkingMulticastEnabled = false;
String appGroupIdString;
StringArray hardenedRuntimeOptions;
StringArray appSandboxOptions;
private:
StringPairArray getEntitlements() const;
};
}
}

View File

@ -0,0 +1,505 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
Array<Drawable*> asArray (const Icons& icons)
{
Array<Drawable*> result;
if (icons.small != nullptr)
result.add (icons.small.get());
if (icons.big != nullptr)
result.add (icons.big.get());
return result;
}
namespace mac
{
static Image fixIconImageSize (Drawable& image)
{
const int validSizes[] = { 16, 32, 64, 128, 256, 512, 1024 };
auto w = image.getWidth();
auto h = image.getHeight();
int bestSize = 16;
for (int size : validSizes)
{
if (w == h && w == size)
{
bestSize = w;
break;
}
if (jmax (w, h) > size)
bestSize = size;
}
return rescaleImageForIcon (image, bestSize);
}
static void writeIconData (MemoryOutputStream& out, const Image& image, const char* type)
{
MemoryOutputStream pngData;
PNGImageFormat pngFormat;
pngFormat.writeImageToStream (image, pngData);
out.write (type, 4);
out.writeIntBigEndian (8 + (int) pngData.getDataSize());
out << pngData;
}
} // namespace mac
static void writeMacIcon (const Icons& icons, OutputStream& out)
{
MemoryOutputStream data;
auto smallest = std::numeric_limits<int>::max();
Drawable* smallestImage = nullptr;
const auto images = asArray (icons);
for (int i = 0; i < images.size(); ++i)
{
auto image = mac::fixIconImageSize (*images[i]);
jassert (image.getWidth() == image.getHeight());
if (image.getWidth() < smallest)
{
smallest = image.getWidth();
smallestImage = images[i];
}
switch (image.getWidth())
{
case 16: mac::writeIconData (data, image, "icp4"); break;
case 32: mac::writeIconData (data, image, "icp5"); break;
case 64: mac::writeIconData (data, image, "icp6"); break;
case 128: mac::writeIconData (data, image, "ic07"); break;
case 256: mac::writeIconData (data, image, "ic08"); break;
case 512: mac::writeIconData (data, image, "ic09"); break;
case 1024: mac::writeIconData (data, image, "ic10"); break;
default: break;
}
}
jassert (data.getDataSize() > 0); // no suitable sized images?
// If you only supply a 1024 image, the file doesn't work on 10.8, so we need
// to force a smaller one in there too..
if (smallest > 512 && smallestImage != nullptr)
mac::writeIconData (data, rescaleImageForIcon (*smallestImage, 512), "ic09");
out.write ("icns", 4);
out.writeIntBigEndian ((int) data.getDataSize() + 8);
out << data;
}
Image getBestIconForSize (const Icons& icons,
int size,
bool returnNullIfNothingBigEnough)
{
auto* const im = [&]() -> Drawable*
{
if ((icons.small != nullptr) != (icons.big != nullptr))
return icons.small != nullptr ? icons.small.get() : icons.big.get();
if (icons.small != nullptr && icons.big != nullptr)
{
if (icons.small->getWidth() >= size && icons.big->getWidth() >= size)
return icons.small->getWidth() < icons.big->getWidth() ? icons.small.get() : icons.big.get();
if (icons.small->getWidth() >= size)
return icons.small.get();
if (icons.big->getWidth() >= size)
return icons.big.get();
}
return nullptr;
}();
if (im == nullptr)
return {};
if (returnNullIfNothingBigEnough && im->getWidth() < size && im->getHeight() < size)
return {};
return rescaleImageForIcon (*im, size);
}
namespace win
{
static void writeBMPImage (const Image& image, const int w, const int h, MemoryOutputStream& out)
{
int maskStride = (w / 8 + 3) & ~3;
out.writeInt (40); // bitmapinfoheader size
out.writeInt (w);
out.writeInt (h * 2);
out.writeShort (1); // planes
out.writeShort (32); // bits
out.writeInt (0); // compression
out.writeInt ((h * w * 4) + (h * maskStride)); // size image
out.writeInt (0); // x pixels per meter
out.writeInt (0); // y pixels per meter
out.writeInt (0); // clr used
out.writeInt (0); // clr important
Image::BitmapData bitmap (image, Image::BitmapData::readOnly);
int alphaThreshold = 5;
int y;
for (y = h; --y >= 0;)
{
for (int x = 0; x < w; ++x)
{
auto pixel = bitmap.getPixelColour (x, y);
if (pixel.getAlpha() <= alphaThreshold)
{
out.writeInt (0);
}
else
{
out.writeByte ((char) pixel.getBlue());
out.writeByte ((char) pixel.getGreen());
out.writeByte ((char) pixel.getRed());
out.writeByte ((char) pixel.getAlpha());
}
}
}
for (y = h; --y >= 0;)
{
int mask = 0, count = 0;
for (int x = 0; x < w; ++x)
{
auto pixel = bitmap.getPixelColour (x, y);
mask <<= 1;
if (pixel.getAlpha() <= alphaThreshold)
mask |= 1;
if (++count == 8)
{
out.writeByte ((char) mask);
count = 0;
mask = 0;
}
}
if (mask != 0)
out.writeByte ((char) mask);
for (int i = maskStride - w / 8; --i >= 0;)
out.writeByte (0);
}
}
static void writeIcon (const Array<Image>& images, OutputStream& out)
{
out.writeShort (0); // reserved
out.writeShort (1); // .ico tag
out.writeShort ((short) images.size());
MemoryOutputStream dataBlock;
int imageDirEntrySize = 16;
int dataBlockStart = 6 + images.size() * imageDirEntrySize;
for (int i = 0; i < images.size(); ++i)
{
auto oldDataSize = dataBlock.getDataSize();
auto& image = images.getReference (i);
auto w = image.getWidth();
auto h = image.getHeight();
if (w >= 256 || h >= 256)
{
PNGImageFormat pngFormat;
pngFormat.writeImageToStream (image, dataBlock);
}
else
{
writeBMPImage (image, w, h, dataBlock);
}
out.writeByte ((char) w);
out.writeByte ((char) h);
out.writeByte (0);
out.writeByte (0);
out.writeShort (1); // colour planes
out.writeShort (32); // bits per pixel
out.writeInt ((int) (dataBlock.getDataSize() - oldDataSize));
out.writeInt (dataBlockStart + (int) oldDataSize);
}
jassert (out.getPosition() == dataBlockStart);
out << dataBlock;
}
} // namespace win
static void writeWinIcon (const Icons& icons, OutputStream& os)
{
Array<Image> images;
int sizes[] = { 16, 32, 48, 256 };
for (int size : sizes)
{
auto im = getBestIconForSize (icons, size, true);
if (im.isValid())
images.add (im);
}
if (images.size() > 0)
win::writeIcon (images, os);
}
void writeMacIcon (const Icons& icons, const File& file)
{
writeStreamToFile (file, [&] (MemoryOutputStream& mo) { writeMacIcon (icons, mo); });
}
void writeWinIcon (const Icons& icons, const File& file)
{
writeStreamToFile (file, [&] (MemoryOutputStream& mo) { writeWinIcon (icons, mo); });
}
Image rescaleImageForIcon (Drawable& d, const int size)
{
if (auto* drawableImage = dynamic_cast<DrawableImage*> (&d))
{
auto im = SoftwareImageType().convert (drawableImage->getImage());
if (im.getWidth() == size && im.getHeight() == size)
return im;
// (scale it down in stages for better resampling)
while (im.getWidth() > 2 * size && im.getHeight() > 2 * size)
im = im.rescaled (im.getWidth() / 2,
im.getHeight() / 2);
Image newIm (Image::ARGB, size, size, true, SoftwareImageType());
Graphics g (newIm);
g.drawImageWithin (im, 0, 0, size, size,
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false);
return newIm;
}
Image im (Image::ARGB, size, size, true, SoftwareImageType());
Graphics g (im);
d.drawWithin (g, im.getBounds().toFloat(), RectanglePlacement::centred, 1.0f);
return im;
}
struct AppIconType
{
const char* idiom;
const char* sizeString;
const char* filename;
const char* scale;
int size;
};
static const AppIconType iOSAppIconTypes[]
{
{ "iphone", "20x20", "Icon-Notification-20@2x.png", "2x", 40 },
{ "iphone", "20x20", "Icon-Notification-20@3x.png", "3x", 60 },
{ "iphone", "29x29", "Icon-29.png", "1x", 29 },
{ "iphone", "29x29", "Icon-29@2x.png", "2x", 58 },
{ "iphone", "29x29", "Icon-29@3x.png", "3x", 87 },
{ "iphone", "40x40", "Icon-Spotlight-40@2x.png", "2x", 80 },
{ "iphone", "40x40", "Icon-Spotlight-40@3x.png", "3x", 120 },
{ "iphone", "57x57", "Icon.png", "1x", 57 },
{ "iphone", "57x57", "Icon@2x.png", "2x", 114 },
{ "iphone", "60x60", "Icon-60@2x.png", "2x", 120 },
{ "iphone", "60x60", "Icon-@3x.png", "3x", 180 },
{ "ipad", "20x20", "Icon-Notifications-20.png", "1x", 20 },
{ "ipad", "20x20", "Icon-Notifications-20@2x.png", "2x", 40 },
{ "ipad", "29x29", "Icon-Small-1.png", "1x", 29 },
{ "ipad", "29x29", "Icon-Small@2x-1.png", "2x", 58 },
{ "ipad", "40x40", "Icon-Spotlight-40.png", "1x", 40 },
{ "ipad", "40x40", "Icon-Spotlight-40@2x-1.png", "2x", 80 },
{ "ipad", "50x50", "Icon-Small-50.png", "1x", 50 },
{ "ipad", "50x50", "Icon-Small-50@2x.png", "2x", 100 },
{ "ipad", "72x72", "Icon-72.png", "1x", 72 },
{ "ipad", "72x72", "Icon-72@2x.png", "2x", 144 },
{ "ipad", "76x76", "Icon-76.png", "1x", 76 },
{ "ipad", "76x76", "Icon-76@2x.png", "2x", 152 },
{ "ipad", "83.5x83.5", "Icon-83.5@2x.png", "2x", 167 },
{ "ios-marketing", "1024x1024", "Icon-AppStore-1024.png", "1x", 1024 }
};
static void createiOSIconFiles (const Icons& icons, File appIconSet)
{
auto* imageToUse = icons.big != nullptr ? icons.big.get()
: icons.small.get();
if (imageToUse != nullptr)
{
for (auto& type : iOSAppIconTypes)
{
auto image = rescaleImageForIcon (*imageToUse, type.size);
if (image.hasAlphaChannel())
{
Image background (Image::RGB, image.getWidth(), image.getHeight(), false);
Graphics g (background);
g.fillAll (Colours::white);
g.drawImageWithin (image, 0, 0, image.getWidth(), image.getHeight(),
RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize);
image = background;
}
MemoryOutputStream pngData;
PNGImageFormat pngFormat;
pngFormat.writeImageToStream (image, pngData);
overwriteFileIfDifferentOrThrow (appIconSet.getChildFile (type.filename), pngData);
}
}
}
static String getiOSAssetContents (var images)
{
DynamicObject::Ptr v (new DynamicObject());
var info (new DynamicObject());
info.getDynamicObject()->setProperty ("version", 1);
info.getDynamicObject()->setProperty ("author", "xcode");
v->setProperty ("images", images);
v->setProperty ("info", info);
return JSON::toString (var (v.get()));
}
//==============================================================================
static String getiOSAppIconContents()
{
var images;
for (auto& type : iOSAppIconTypes)
{
DynamicObject::Ptr d (new DynamicObject());
d->setProperty ("idiom", type.idiom);
d->setProperty ("size", type.sizeString);
d->setProperty ("filename", type.filename);
d->setProperty ("scale", type.scale);
images.append (var (d.get()));
}
return getiOSAssetContents (images);
}
struct ImageType
{
const char* orientation;
const char* idiom;
const char* subtype;
const char* extent;
const char* scale;
const char* filename;
int width;
int height;
};
static const ImageType iOSLaunchImageTypes[]
{
{ "portrait", "iphone", nullptr, "full-screen", "2x", "LaunchImage-iphone-2x.png", 640, 960 },
{ "portrait", "iphone", "retina4", "full-screen", "2x", "LaunchImage-iphone-retina4.png", 640, 1136 },
{ "portrait", "ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-portrait-1x.png", 768, 1024 },
{ "landscape","ipad", nullptr, "full-screen", "1x", "LaunchImage-ipad-landscape-1x.png", 1024, 768 },
{ "portrait", "ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-portrait-2x.png", 1536, 2048 },
{ "landscape","ipad", nullptr, "full-screen", "2x", "LaunchImage-ipad-landscape-2x.png", 2048, 1536 }
};
static void createiOSLaunchImageFiles (const File& launchImageSet)
{
for (auto& type : iOSLaunchImageTypes)
{
Image image (Image::ARGB, type.width, type.height, true); // (empty black image)
image.clear (image.getBounds(), Colours::black);
MemoryOutputStream pngData;
PNGImageFormat pngFormat;
pngFormat.writeImageToStream (image, pngData);
build_tools::overwriteFileIfDifferentOrThrow (launchImageSet.getChildFile (type.filename), pngData);
}
}
static String getiOSLaunchImageContents()
{
var images;
for (auto& type : iOSLaunchImageTypes)
{
DynamicObject::Ptr d (new DynamicObject());
d->setProperty ("orientation", type.orientation);
d->setProperty ("idiom", type.idiom);
d->setProperty ("extent", type.extent);
d->setProperty ("minimum-system-version", "7.0");
d->setProperty ("scale", type.scale);
d->setProperty ("filename", type.filename);
if (type.subtype != nullptr)
d->setProperty ("subtype", type.subtype);
images.append (var (d.get()));
}
return getiOSAssetContents (images);
}
RelativePath createXcassetsFolderFromIcons (const Icons& icons,
const File& targetFolder,
String projectFilenameRootString)
{
const auto assets = targetFolder.getChildFile (projectFilenameRootString)
.getChildFile ("Images.xcassets");
const auto iconSet = assets.getChildFile ("AppIcon.appiconset");
const auto launchImage = assets.getChildFile ("LaunchImage.launchimage");
overwriteFileIfDifferentOrThrow (iconSet.getChildFile ("Contents.json"), getiOSAppIconContents());
createiOSIconFiles (icons, iconSet);
overwriteFileIfDifferentOrThrow (launchImage.getChildFile ("Contents.json"), getiOSLaunchImageContents());
createiOSLaunchImageFiles (launchImage);
return { assets, targetFolder, RelativePath::buildTargetFolder };
}
}
}

View File

@ -0,0 +1,49 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
struct Icons
{
std::unique_ptr<Drawable> small;
std::unique_ptr<Drawable> big;
};
Array<Drawable*> asArray (const Icons&);
void writeMacIcon (const Icons&, const File&);
void writeWinIcon (const Icons&, const File&);
Image getBestIconForSize (const Icons& icons,
int size,
bool returnNullIfNothingBigEnough);
Image rescaleImageForIcon (Drawable& d, const int size);
RelativePath createXcassetsFolderFromIcons (const Icons& icons,
const File& targetFolder,
String projectFilenameRootString);
}
}

View File

@ -0,0 +1,370 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
//==============================================================================
static XmlElement* getKeyWithName (XmlElement& xml, const String& key)
{
for (auto* element : xml.getChildWithTagNameIterator ("key"))
if (element->getAllSubText().trim().equalsIgnoreCase (key))
return element;
return nullptr;
}
static bool keyFoundAndNotSequentialDuplicate (XmlElement& xml, const String& key)
{
if (auto* element = getKeyWithName (xml, key))
{
if (element->getNextElement() != nullptr && element->getNextElement()->hasTagName ("key"))
{
// found broken plist format (sequential duplicate), fix by removing
xml.removeChildElement (element, true);
return false;
}
// key found (not sequential duplicate)
return true;
}
// key not found
return false;
}
static bool addKeyIfNotFound (XmlElement& xml, const String& key)
{
if (! keyFoundAndNotSequentialDuplicate (xml, key))
{
xml.createNewChildElement ("key")->addTextElement (key);
return true;
}
return false;
}
static void addPlistDictionaryKey (XmlElement& xml, const String& key, const String& value)
{
if (addKeyIfNotFound (xml, key))
xml.createNewChildElement ("string")->addTextElement (value);
}
template <size_t N>
static void addPlistDictionaryKey (XmlElement& xml, const String& key, const char (&value) [N])
{
addPlistDictionaryKey (xml, key, String { value });
}
static void addPlistDictionaryKey (XmlElement& xml, const String& key, const bool value)
{
if (addKeyIfNotFound (xml, key))
xml.createNewChildElement (value ? "true" : "false");
}
static void addPlistDictionaryKey (XmlElement& xml, const String& key, int value)
{
if (addKeyIfNotFound (xml, key))
xml.createNewChildElement ("integer")->addTextElement (String (value));
}
static void addArrayToPlist (XmlElement& dict, String arrayKey, const StringArray& arrayElements)
{
if (getKeyWithName (dict, arrayKey) != nullptr)
return;
dict.createNewChildElement ("key")->addTextElement (arrayKey);
auto* plistStringArray = dict.createNewChildElement ("array");
for (auto& e : arrayElements)
plistStringArray->createNewChildElement ("string")->addTextElement (e);
}
static int getAUVersionAsHexInteger (const PlistOptions& opts)
{
const auto segments = getVersionSegments (opts.marketingVersion);
const StringArray trimmed (segments.strings.getRawDataPointer(), jmin (segments.size(), 3));
return getVersionAsHexIntegerFromParts (trimmed);
}
//==============================================================================
void PlistOptions::write (const File& infoPlistFile) const
{
writeStreamToFile (infoPlistFile, [&] (MemoryOutputStream& mo) { write (mo); });
}
void PlistOptions::write (MemoryOutputStream& mo) const
{
XmlElement::TextFormat format;
format.dtd = "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
createXML()->writeTo (mo, format);
}
std::unique_ptr<XmlElement> PlistOptions::createXML() const
{
auto plist = parseXML (plistToMerge);
if (plist == nullptr || ! plist->hasTagName ("plist"))
plist.reset (new XmlElement ("plist"));
auto* dict = plist->getChildByName ("dict");
if (dict == nullptr)
dict = plist->createNewChildElement ("dict");
if (microphonePermissionEnabled)
addPlistDictionaryKey (*dict, "NSMicrophoneUsageDescription", microphonePermissionText);
if (cameraPermissionEnabled)
addPlistDictionaryKey (*dict, "NSCameraUsageDescription", cameraPermissionText);
if (bluetoothPermissionEnabled)
addPlistDictionaryKey (*dict, "NSBluetoothAlwaysUsageDescription", bluetoothPermissionText);
if (iOS)
{
if (bluetoothPermissionEnabled)
addPlistDictionaryKey (*dict, "NSBluetoothPeripheralUsageDescription", bluetoothPermissionText); // needed for pre iOS 13.0
addPlistDictionaryKey (*dict, "LSRequiresIPhoneOS", true);
addPlistDictionaryKey (*dict, "UIViewControllerBasedStatusBarAppearance", true);
if (shouldAddStoryboardToProject)
addPlistDictionaryKey (*dict, "UILaunchStoryboardName", storyboardName);
}
else
{
if (sendAppleEventsPermissionEnabled)
addPlistDictionaryKey (*dict, "NSAppleEventsUsageDescription", sendAppleEventsPermissionText);
}
addPlistDictionaryKey (*dict, "CFBundleExecutable", executableName);
if (! iOS) // (NB: on iOS this causes error ITMS-90032 during publishing)
addPlistDictionaryKey (*dict, "CFBundleIconFile", iconFile.exists() ? iconFile.getFileName() : String());
addPlistDictionaryKey (*dict, "CFBundleIdentifier", bundleIdentifier);
addPlistDictionaryKey (*dict, "CFBundleName", projectName);
// needed by NSExtension on iOS
addPlistDictionaryKey (*dict, "CFBundleDisplayName", projectName);
addPlistDictionaryKey (*dict, "CFBundlePackageType", getXcodePackageType (type));
addPlistDictionaryKey (*dict, "CFBundleSignature", getXcodeBundleSignature (type));
addPlistDictionaryKey (*dict, "CFBundleShortVersionString", marketingVersion);
addPlistDictionaryKey (*dict, "CFBundleVersion", currentProjectVersion);
addPlistDictionaryKey (*dict, "NSHumanReadableCopyright", companyCopyright);
addPlistDictionaryKey (*dict, "NSHighResolutionCapable", true);
if (applicationCategory.isNotEmpty())
addPlistDictionaryKey (*dict, "LSApplicationCategoryType", applicationCategory);
auto replacedDocExtensions = StringArray::fromTokens (replacePreprocessorDefs (allPreprocessorDefs,
documentExtensions), ",", {});
replacedDocExtensions.trim();
replacedDocExtensions.removeEmptyStrings (true);
if (! replacedDocExtensions.isEmpty() && type != ProjectType::Target::AudioUnitv3PlugIn)
{
dict->createNewChildElement ("key")->addTextElement ("CFBundleDocumentTypes");
auto* dict2 = dict->createNewChildElement ("array")->createNewChildElement ("dict");
XmlElement* arrayTag = nullptr;
for (auto ex : replacedDocExtensions)
{
if (ex.startsWithChar ('.'))
ex = ex.substring (1);
if (arrayTag == nullptr)
{
dict2->createNewChildElement ("key")->addTextElement ("CFBundleTypeExtensions");
arrayTag = dict2->createNewChildElement ("array");
addPlistDictionaryKey (*dict2, "CFBundleTypeName", ex);
addPlistDictionaryKey (*dict2, "CFBundleTypeRole", "Editor");
addPlistDictionaryKey (*dict2, "CFBundleTypeIconFile", "Icon");
addPlistDictionaryKey (*dict2, "NSPersistentStoreTypeKey", "XML");
}
arrayTag->createNewChildElement ("string")->addTextElement (ex);
}
}
if (fileSharingEnabled && type != ProjectType::Target::AudioUnitv3PlugIn)
addPlistDictionaryKey (*dict, "UIFileSharingEnabled", true);
if (documentBrowserEnabled)
addPlistDictionaryKey (*dict, "UISupportsDocumentBrowser", true);
if (iOS)
{
if (type != ProjectType::Target::AudioUnitv3PlugIn)
{
if (statusBarHidden)
addPlistDictionaryKey (*dict, "UIStatusBarHidden", true);
addPlistDictionaryKey (*dict, "UIRequiresFullScreen", requiresFullScreen);
addIosScreenOrientations (*dict);
addIosBackgroundModes (*dict);
}
if (type == ProjectType::Target::StandalonePlugIn && enableIAA)
{
XmlElement audioComponentsPlistKey ("key");
audioComponentsPlistKey.addTextElement ("AudioComponents");
dict->addChildElement (new XmlElement (audioComponentsPlistKey));
XmlElement audioComponentsPlistEntry ("array");
auto* audioComponentsDict = audioComponentsPlistEntry.createNewChildElement ("dict");
addPlistDictionaryKey (*audioComponentsDict, "name", IAAPluginName);
addPlistDictionaryKey (*audioComponentsDict, "manufacturer", pluginManufacturerCode.substring (0, 4));
addPlistDictionaryKey (*audioComponentsDict, "type", IAATypeCode);
addPlistDictionaryKey (*audioComponentsDict, "subtype", pluginCode.substring (0, 4));
addPlistDictionaryKey (*audioComponentsDict, "version", getVersionAsHexInteger (marketingVersion));
dict->addChildElement (new XmlElement (audioComponentsPlistEntry));
}
}
const auto extraOptions = [&]() -> Array<XmlElement>
{
if (type == ProjectType::Target::Type::AudioUnitPlugIn)
return createExtraAudioUnitTargetPlistOptions();
if (type == ProjectType::Target::Type::AudioUnitv3PlugIn)
return createExtraAudioUnitV3TargetPlistOptions();
return {};
}();
for (auto& e : extraOptions)
dict->addChildElement (new XmlElement (e));
return plist;
}
void PlistOptions::addIosScreenOrientations (XmlElement& dict) const
{
addArrayToPlist (dict, "UISupportedInterfaceOrientations", iPhoneScreenOrientations);
if (iPadScreenOrientations != iPhoneScreenOrientations)
addArrayToPlist (dict, "UISupportedInterfaceOrientations~ipad", iPadScreenOrientations);
}
void PlistOptions::addIosBackgroundModes (XmlElement& dict) const
{
StringArray iosBackgroundModes;
if (backgroundAudioEnabled) iosBackgroundModes.add ("audio");
if (backgroundBleEnabled) iosBackgroundModes.add ("bluetooth-central");
if (pushNotificationsEnabled) iosBackgroundModes.add ("remote-notification");
addArrayToPlist (dict, "UIBackgroundModes", iosBackgroundModes);
}
Array<XmlElement> PlistOptions::createExtraAudioUnitTargetPlistOptions() const
{
XmlElement plistKey ("key");
plistKey.addTextElement ("AudioComponents");
XmlElement plistEntry ("array");
auto* dict = plistEntry.createNewChildElement ("dict");
auto truncatedCode = pluginManufacturerCode.substring (0, 4);
auto pluginSubType = pluginCode.substring (0, 4);
if (truncatedCode.toLowerCase() == truncatedCode)
{
throw SaveError ("AudioUnit plugin code identifiers invalid!\n\n"
"You have used only lower case letters in your AU plugin manufacturer identifier. "
"You must have at least one uppercase letter in your AU plugin manufacturer "
"identifier code.");
}
addPlistDictionaryKey (*dict, "name", pluginManufacturer + ": " + pluginName);
addPlistDictionaryKey (*dict, "description", pluginDescription);
addPlistDictionaryKey (*dict, "factoryFunction", pluginAUExportPrefix + "Factory");
addPlistDictionaryKey (*dict, "manufacturer", truncatedCode);
addPlistDictionaryKey (*dict, "type", auMainType.removeCharacters ("'"));
addPlistDictionaryKey (*dict, "subtype", pluginSubType);
addPlistDictionaryKey (*dict, "version", getAUVersionAsHexInteger (*this));
if (isAuSandboxSafe)
{
addPlistDictionaryKey (*dict, "sandboxSafe", true);
}
else if (! suppressResourceUsage)
{
dict->createNewChildElement ("key")->addTextElement ("resourceUsage");
auto* resourceUsageDict = dict->createNewChildElement ("dict");
addPlistDictionaryKey (*resourceUsageDict, "network.client", true);
addPlistDictionaryKey (*resourceUsageDict, "temporary-exception.files.all.read-write", true);
}
return { plistKey, plistEntry };
}
Array<XmlElement> PlistOptions::createExtraAudioUnitV3TargetPlistOptions() const
{
XmlElement plistKey ("key");
plistKey.addTextElement ("NSExtension");
XmlElement plistEntry ("dict");
addPlistDictionaryKey (plistEntry, "NSExtensionPrincipalClass", pluginAUExportPrefix + "FactoryAUv3");
addPlistDictionaryKey (plistEntry, "NSExtensionPointIdentifier", "com.apple.AudioUnit-UI");
plistEntry.createNewChildElement ("key")->addTextElement ("NSExtensionAttributes");
auto* dict = plistEntry.createNewChildElement ("dict");
dict->createNewChildElement ("key")->addTextElement ("AudioComponents");
auto* componentArray = dict->createNewChildElement ("array");
auto* componentDict = componentArray->createNewChildElement ("dict");
addPlistDictionaryKey (*componentDict, "name", pluginManufacturer + ": " + pluginName);
addPlistDictionaryKey (*componentDict, "description", pluginDescription);
addPlistDictionaryKey (*componentDict, "factoryFunction", pluginAUExportPrefix + "FactoryAUv3");
addPlistDictionaryKey (*componentDict, "manufacturer", pluginManufacturerCode.substring (0, 4));
addPlistDictionaryKey (*componentDict, "type", auMainType.removeCharacters ("'"));
addPlistDictionaryKey (*componentDict, "subtype", pluginCode.substring (0, 4));
addPlistDictionaryKey (*componentDict, "version", getAUVersionAsHexInteger (*this));
addPlistDictionaryKey (*componentDict, "sandboxSafe", true);
componentDict->createNewChildElement ("key")->addTextElement ("tags");
auto* tagsArray = componentDict->createNewChildElement ("array");
tagsArray->createNewChildElement ("string")
->addTextElement (isPluginSynth ? "Synth" : "Effects");
if (auMainType.removeCharacters ("'") == "aumi")
tagsArray->createNewChildElement ("string")->addTextElement ("MIDI");
return { plistKey, plistEntry };
}
}
}

View File

@ -0,0 +1,106 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
class PlistOptions final
{
public:
void write (const File& infoPlistFile) const;
//==============================================================================
ProjectType::Target::Type type = ProjectType::Target::Type::GUIApp;
String executableName;
String bundleIdentifier;
String plistToMerge;
bool iOS = false;
bool microphonePermissionEnabled = false;
String microphonePermissionText;
bool cameraPermissionEnabled = false;
String cameraPermissionText;
bool bluetoothPermissionEnabled = false;
String bluetoothPermissionText;
bool sendAppleEventsPermissionEnabled = false;
String sendAppleEventsPermissionText;
bool shouldAddStoryboardToProject = false;
String storyboardName;
File iconFile;
String projectName;
String marketingVersion;
String currentProjectVersion;
String companyCopyright;
String applicationCategory;
StringPairArray allPreprocessorDefs;
String documentExtensions;
bool fileSharingEnabled = false;
bool documentBrowserEnabled = false;
bool statusBarHidden = false;
bool requiresFullScreen = false;
bool backgroundAudioEnabled = false;
bool backgroundBleEnabled = false;
bool pushNotificationsEnabled = false;
bool enableIAA = false;
String IAAPluginName;
String pluginManufacturerCode;
String IAATypeCode;
String pluginCode;
StringArray iPhoneScreenOrientations;
StringArray iPadScreenOrientations;
String pluginName;
String pluginManufacturer;
String pluginDescription;
String pluginAUExportPrefix;
String auMainType;
bool isAuSandboxSafe = false;
bool isPluginSynth = false;
bool suppressResourceUsage = false;
private:
void write (MemoryOutputStream&) const;
std::unique_ptr<XmlElement> createXML() const;
void addIosScreenOrientations (XmlElement&) const;
void addIosBackgroundModes (XmlElement&) const;
Array<XmlElement> createExtraAudioUnitTargetPlistOptions() const;
Array<XmlElement> createExtraAudioUnitV3TargetPlistOptions() const;
};
}
}

View File

@ -0,0 +1,275 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
//==============================================================================
class ProjectType
{
public:
//==============================================================================
virtual ~ProjectType() { getAllTypes().removeFirstMatchingValue (this); }
const String& getType() const noexcept { return type; }
const String& getDescription() const noexcept { return desc; }
//==============================================================================
static Array<ProjectType*> getAllTypes();
static const ProjectType* findType (const String& typeCode)
{
const auto& types = getAllTypes();
for (auto i = types.size(); --i >= 0;)
if (types.getUnchecked(i)->getType() == typeCode)
return types.getUnchecked(i);
jassertfalse;
return nullptr;
}
//==============================================================================
virtual bool isStaticLibrary() const { return false; }
virtual bool isDynamicLibrary() const { return false; }
virtual bool isGUIApplication() const { return false; }
virtual bool isCommandLineApp() const { return false; }
virtual bool isAudioPlugin() const { return false; }
//==============================================================================
struct Target
{
enum Type
{
GUIApp = 0,
ConsoleApp = 1,
StaticLibrary = 2,
DynamicLibrary = 3,
VSTPlugIn = 10,
VST3PlugIn = 11,
AAXPlugIn = 12,
RTASPlugIn = 13,
AudioUnitPlugIn = 14,
AudioUnitv3PlugIn = 15,
StandalonePlugIn = 16,
UnityPlugIn = 17,
SharedCodeTarget = 20, // internal
AggregateTarget = 21,
unspecified = 30
};
enum TargetFileType
{
executable = 0,
staticLibrary = 1,
sharedLibraryOrDLL = 2,
pluginBundle = 3,
macOSAppex = 4,
unknown = 5
};
//==============================================================================
explicit Target (Type targetType) : type (targetType) {}
const char* getName() const noexcept
{
switch (type)
{
case GUIApp: return "App";
case ConsoleApp: return "ConsoleApp";
case StaticLibrary: return "Static Library";
case DynamicLibrary: return "Dynamic Library";
case VSTPlugIn: return "VST";
case VST3PlugIn: return "VST3";
case AudioUnitPlugIn: return "AU";
case StandalonePlugIn: return "Standalone Plugin";
case AudioUnitv3PlugIn: return "AUv3 AppExtension";
case AAXPlugIn: return "AAX";
case RTASPlugIn: return "RTAS";
case UnityPlugIn: return "Unity Plugin";
case SharedCodeTarget: return "Shared Code";
case AggregateTarget: return "All";
case unspecified:
default: break;
}
return "undefined";
}
static Type typeFromName (const String& name)
{
if (name == "App") return Type::GUIApp;
if (name == "ConsoleApp") return Type::ConsoleApp;
if (name == "Static Library") return Type::StaticLibrary;
if (name == "Dynamic Library") return Type::DynamicLibrary;
if (name == "VST") return Type::VSTPlugIn;
if (name == "VST3") return Type::VST3PlugIn;
if (name == "AU") return Type::AudioUnitPlugIn;
if (name == "Standalone Plugin") return Type::StandalonePlugIn;
if (name == "AUv3 AppExtension") return Type::AudioUnitv3PlugIn;
if (name == "AAX") return Type::AAXPlugIn;
if (name == "RTAS") return Type::RTASPlugIn;
if (name == "Unity Plugin") return Type::UnityPlugIn;
if (name == "Shared Code") return Type::SharedCodeTarget;
if (name == "All") return Type::AggregateTarget;
jassertfalse;
return Type::ConsoleApp;
}
TargetFileType getTargetFileType() const noexcept
{
switch (type)
{
case GUIApp: return executable;
case ConsoleApp: return executable;
case StaticLibrary: return staticLibrary;
case DynamicLibrary: return sharedLibraryOrDLL;
case VSTPlugIn: return pluginBundle;
case VST3PlugIn: return pluginBundle;
case AudioUnitPlugIn: return pluginBundle;
case StandalonePlugIn: return executable;
case AudioUnitv3PlugIn: return macOSAppex;
case AAXPlugIn: return pluginBundle;
case RTASPlugIn: return pluginBundle;
case UnityPlugIn: return pluginBundle;
case SharedCodeTarget: return staticLibrary;
case AggregateTarget:
case unspecified:
default: break;
}
return unknown;
}
const Type type;
private:
//==============================================================================
Target& operator= (const Target&) = delete;
};
virtual bool supportsTargetType (Target::Type /*targetType*/) const { return false; }
protected:
ProjectType (const String& t, const String& d)
: type (t), desc (d)
{}
private:
const String type, desc;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ProjectType)
};
//==============================================================================
struct ProjectType_GUIApp : public ProjectType
{
ProjectType_GUIApp() : ProjectType (getTypeName(), "GUI Application") {}
static const char* getTypeName() noexcept { return "guiapp"; }
bool isGUIApplication() const override { return true; }
bool supportsTargetType (Target::Type targetType) const override { return (targetType == Target::GUIApp); }
};
struct ProjectType_ConsoleApp : public ProjectType
{
ProjectType_ConsoleApp() : ProjectType (getTypeName(), "Console Application") {}
static const char* getTypeName() noexcept { return "consoleapp"; }
bool isCommandLineApp() const override { return true; }
bool supportsTargetType (Target::Type targetType) const override { return (targetType == Target::ConsoleApp); }
};
struct ProjectType_StaticLibrary : public ProjectType
{
ProjectType_StaticLibrary() : ProjectType (getTypeName(), "Static Library") {}
static const char* getTypeName() noexcept { return "library"; }
bool isStaticLibrary() const override { return true; }
bool supportsTargetType (Target::Type targetType) const override { return (targetType == Target::StaticLibrary); }
};
struct ProjectType_DLL : public ProjectType
{
ProjectType_DLL() : ProjectType (getTypeName(), "Dynamic Library") {}
static const char* getTypeName() noexcept { return "dll"; }
bool isDynamicLibrary() const override { return true; }
bool supportsTargetType (Target::Type targetType) const override { return (targetType == Target::DynamicLibrary); }
};
struct ProjectType_AudioPlugin : public ProjectType
{
ProjectType_AudioPlugin() : ProjectType (getTypeName(), "Audio Plug-in") {}
static const char* getTypeName() noexcept { return "audioplug"; }
bool isAudioPlugin() const override { return true; }
bool supportsTargetType (Target::Type targetType) const override
{
switch (targetType)
{
case Target::VSTPlugIn:
case Target::VST3PlugIn:
case Target::AAXPlugIn:
case Target::RTASPlugIn:
case Target::AudioUnitPlugIn:
case Target::AudioUnitv3PlugIn:
case Target::StandalonePlugIn:
case Target::UnityPlugIn:
case Target::SharedCodeTarget:
case Target::AggregateTarget:
return true;
case Target::GUIApp:
case Target::ConsoleApp:
case Target::StaticLibrary:
case Target::DynamicLibrary:
case Target::unspecified:
default:
break;
}
return false;
}
};
//==============================================================================
inline Array<ProjectType*> ProjectType::getAllTypes()
{
static ProjectType_GUIApp guiApp;
static ProjectType_ConsoleApp consoleApp;
static ProjectType_StaticLibrary staticLib;
static ProjectType_DLL dll;
static ProjectType_AudioPlugin plugin;
return Array<ProjectType*>(&guiApp, &consoleApp, &staticLib, &dll, &plugin);
}
}
}

View File

@ -0,0 +1,129 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
//==============================================================================
/** Manipulates a cross-platform partial file path. (Needed because File is designed
for absolute paths on the active OS)
*/
class RelativePath
{
public:
//==============================================================================
enum RootFolder
{
unknown,
projectFolder,
buildTargetFolder
};
//==============================================================================
RelativePath()
: root (unknown)
{}
RelativePath (const String& relPath, const RootFolder rootType)
: path (unixStylePath (relPath)), root (rootType)
{}
RelativePath (const File& file, const File& rootFolder, const RootFolder rootType)
: path (unixStylePath (getRelativePathFrom (file, rootFolder))), root (rootType)
{}
RootFolder getRoot() const { return root; }
String toUnixStyle() const { return unixStylePath (path); }
String toWindowsStyle() const { return windowsStylePath (path); }
String getFileName() const { return getFakeFile().getFileName(); }
String getFileNameWithoutExtension() const { return getFakeFile().getFileNameWithoutExtension(); }
String getFileExtension() const { return getFakeFile().getFileExtension(); }
bool hasFileExtension (StringRef extension) const { return getFakeFile().hasFileExtension (extension); }
bool isAbsolute() const { return isAbsolutePath (path); }
RelativePath withFileExtension (const String& extension) const
{
return RelativePath (path.upToLastOccurrenceOf (".", ! extension.startsWithChar ('.'), false) + extension, root);
}
RelativePath getParentDirectory() const
{
String p (path);
if (path.endsWithChar ('/'))
p = p.dropLastCharacters (1);
return RelativePath (p.upToLastOccurrenceOf ("/", false, false), root);
}
RelativePath getChildFile (const String& subpath) const
{
if (isAbsolutePath (subpath))
return RelativePath (subpath, root);
String p (toUnixStyle());
if (! p.endsWithChar ('/'))
p << '/';
return RelativePath (p + subpath, root);
}
RelativePath rebased (const File& originalRoot, const File& newRoot, const RootFolder newRootType) const
{
if (isAbsolute())
return RelativePath (path, newRootType);
return RelativePath (getRelativePathFrom (originalRoot.getChildFile (toUnixStyle()), newRoot), newRootType);
}
private:
//==============================================================================
String path;
RootFolder root;
File getFakeFile() const
{
#if JUCE_WINDOWS
if (isAbsolutePath (path))
{
// This is a hack to convert unix-style absolute paths into valid absolute Windows paths to avoid hitting
// an assertion in File::parseAbsolutePath().
if (path.startsWithChar (L'/') || path.startsWithChar (L'$') || path.startsWithChar (L'~'))
return File (String ("C:\\") + windowsStylePath (path.substring (1)));
return File (path);
}
#endif
// This method gets called very often, so we'll cache this directory.
static const File currentWorkingDirectory (File::getCurrentWorkingDirectory());
return currentWorkingDirectory.getChildFile (path);
}
};
}
}

View File

@ -0,0 +1,91 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
uint64 calculateStreamHashCode (InputStream& in)
{
uint64 t = 0;
const int bufferSize = 4096;
HeapBlock<uint8> buffer;
buffer.malloc (bufferSize);
for (;;)
{
auto num = in.read (buffer, bufferSize);
if (num <= 0)
break;
for (int i = 0; i < num; ++i)
t = t * 65599 + buffer[i];
}
return t;
}
uint64 calculateFileHashCode (const File& file)
{
std::unique_ptr<FileInputStream> stream (file.createInputStream());
return stream != nullptr ? calculateStreamHashCode (*stream) : 0;
}
uint64 calculateMemoryHashCode (const void* data, size_t numBytes)
{
uint64 t = 0;
for (size_t i = 0; i < numBytes; ++i)
t = t * 65599 + static_cast<const uint8*> (data)[i];
return t;
}
bool overwriteFileWithNewDataIfDifferent (const File& file, const void* data, size_t numBytes)
{
if (file.getSize() == (int64) numBytes
&& calculateMemoryHashCode (data, numBytes) == calculateFileHashCode (file))
return true;
if (file.exists())
return file.replaceWithData (data, numBytes);
return file.getParentDirectory().createDirectory() && file.appendData (data, numBytes);
}
bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData)
{
return overwriteFileWithNewDataIfDifferent (file, newData.getData(), newData.getDataSize());
}
bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData)
{
const char* const utf8 = newData.toUTF8();
return overwriteFileWithNewDataIfDifferent (file, utf8, strlen (utf8));
}
}
}

View File

@ -0,0 +1,38 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
uint64 calculateStreamHashCode (InputStream& in);
uint64 calculateFileHashCode (const File& file);
uint64 calculateMemoryHashCode (const void* data, size_t numBytes);
bool overwriteFileWithNewDataIfDifferent (const File& file, const void* data, size_t numBytes);
bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData);
bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData);
}
}

View File

@ -0,0 +1,97 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
static String getCommaSeparatedVersionNumber (const String& version)
{
auto versionParts = StringArray::fromTokens (version, ",.", "");
versionParts.trim();
versionParts.removeEmptyStrings();
while (versionParts.size() < 4)
versionParts.add ("0");
return versionParts.joinIntoString (",");
}
void ResourceRcOptions::write (const File& resourceRcFile) const
{
MemoryOutputStream mo;
mo << "#pragma code_page(65001)" << newLine
<< newLine
<< "#ifdef JUCE_USER_DEFINED_RC_FILE" << newLine
<< " #include JUCE_USER_DEFINED_RC_FILE" << newLine
<< "#else" << newLine
<< newLine
<< "#undef WIN32_LEAN_AND_MEAN" << newLine
<< "#define WIN32_LEAN_AND_MEAN" << newLine
<< "#include <windows.h>" << newLine
<< newLine
<< "VS_VERSION_INFO VERSIONINFO" << newLine
<< "FILEVERSION " << getCommaSeparatedVersionNumber (version) << newLine
<< "BEGIN" << newLine
<< " BLOCK \"StringFileInfo\"" << newLine
<< " BEGIN" << newLine
<< " BLOCK \"040904E4\"" << newLine
<< " BEGIN" << newLine;
const auto writeRCValue = [&] (const String& n, const String& value)
{
if (value.isNotEmpty())
mo << " VALUE \"" << n << "\", \""
<< value.replace ("\"", "\"\"") << "\\0\"" << newLine;
};
writeRCValue ("CompanyName", companyName);
writeRCValue ("LegalCopyright", companyCopyright);
writeRCValue ("FileDescription", projectName);
writeRCValue ("FileVersion", version);
writeRCValue ("ProductName", projectName);
writeRCValue ("ProductVersion", version);
mo << " END" << newLine
<< " END" << newLine
<< newLine
<< " BLOCK \"VarFileInfo\"" << newLine
<< " BEGIN" << newLine
<< " VALUE \"Translation\", 0x409, 1252" << newLine
<< " END" << newLine
<< "END" << newLine
<< newLine
<< "#endif" << newLine;
if (icon.existsAsFile())
mo << newLine
<< "IDI_ICON1 ICON DISCARDABLE " << icon.getFileName().quoted()
<< newLine
<< "IDI_ICON2 ICON DISCARDABLE " << icon.getFileName().quoted();
overwriteFileIfDifferentOrThrow (resourceRcFile, mo);
}
}
}

View File

@ -0,0 +1,43 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
class ResourceRcOptions final
{
public:
void write (const File& resourceRcFile) const;
//==============================================================================
String version;
String companyName;
String companyCopyright;
String projectName;
File icon;
};
}
}

View File

@ -0,0 +1,60 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
StringArray getVersionSegments (StringRef p)
{
auto segments = StringArray::fromTokens (p, ",.", "");
segments.trim();
segments.removeEmptyStrings();
return segments;
}
int getVersionAsHexIntegerFromParts (const StringArray& segments)
{
auto value = (segments[0].getIntValue() << 16)
+ (segments[1].getIntValue() << 8)
+ segments[2].getIntValue();
if (segments.size() > 3)
value = (value << 8) + segments[3].getIntValue();
return value;
}
int getVersionAsHexInteger (StringRef versionString)
{
return getVersionAsHexIntegerFromParts (getVersionSegments (versionString));
}
String getVersionAsHex (StringRef versionString)
{
return "0x" + String::toHexString (getVersionAsHexInteger (versionString));
}
}
}

View File

@ -0,0 +1,37 @@
/*
==============================================================================
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.
==============================================================================
*/
namespace juce
{
namespace build_tools
{
StringArray getVersionSegments (StringRef p);
int getVersionAsHexIntegerFromParts (const StringArray& versionString);
int getVersionAsHexInteger (StringRef versionString);
String getVersionAsHex (StringRef versionString);
}
}