|  | /*
  ==============================================================================
   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);
    }
}
}
 |