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