| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2022 - 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 7 End-User License
 -    Agreement and JUCE Privacy Policy.
 - 
 -    End User License Agreement: www.juce.com/juce-7-licence
 -    Privacy Policy: www.juce.com/juce-privacy-policy
 - 
 -    Or: You may also use this code under the terms of the GPL v3 (see
 -    www.gnu.org/licenses).
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - #include "../../Application/jucer_Headers.h"
 - #include "../../ProjectSaving/jucer_ProjectExporter.h"
 - #include "../../ProjectSaving/jucer_ProjectExport_Xcode.h"
 - #include "../../ProjectSaving/jucer_ProjectExport_Android.h"
 - #include "jucer_PIPGenerator.h"
 - 
 - //==============================================================================
 - static void ensureSingleNewLineAfterIncludes (StringArray& lines)
 - {
 -     int lastIncludeIndex = -1;
 - 
 -     for (int i = 0; i < lines.size(); ++i)
 -     {
 -         if (lines[i].contains ("#include"))
 -             lastIncludeIndex = i;
 -     }
 - 
 -     if (lastIncludeIndex != -1)
 -     {
 -         auto index = lastIncludeIndex;
 -         int numNewLines = 0;
 - 
 -         while (++index < lines.size() && lines[index].isEmpty())
 -             ++numNewLines;
 - 
 -         if (numNewLines > 1)
 -             lines.removeRange (lastIncludeIndex + 1, numNewLines - 1);
 -     }
 - }
 - 
 - static String ensureCorrectWhitespace (StringRef input)
 - {
 -     auto lines = StringArray::fromLines (input);
 -     ensureSingleNewLineAfterIncludes (lines);
 -     return joinLinesIntoSourceFile (lines);
 - }
 - 
 - static bool isJUCEExample (const File& pipFile)
 - {
 -     const auto numLinesToTest = 10; // license should be at the top of the file so no need to check all lines
 -     const auto lines = StringArray::fromLines (pipFile.loadFileAsString());
 - 
 -     return std::any_of (lines.begin(),
 -                         lines.begin() + (std::min (lines.size(), numLinesToTest)),
 -                         [] (const auto& line)
 -                         {
 -                             return line.contains ("This file is part of the JUCE examples.");
 -                         });
 - }
 - 
 - static bool isValidExporterIdentifier (const Identifier& exporterIdentifier)
 - {
 -     return ProjectExporter::getTypeInfoForExporter (exporterIdentifier).identifier.toString().isNotEmpty();
 - }
 - 
 - static bool exporterRequiresExampleAssets (const Identifier& exporterIdentifier, const String& projectName)
 - {
 -     return (exporterIdentifier.toString() == XcodeProjectExporter::getValueTreeTypeNameiOS()
 -             || exporterIdentifier.toString() == AndroidProjectExporter::getValueTreeTypeName())
 -             || (exporterIdentifier.toString() == XcodeProjectExporter::getValueTreeTypeNameMac() && projectName == "AUv3SynthPlugin");
 - }
 - 
 - //==============================================================================
 - PIPGenerator::PIPGenerator (const File& pip, const File& output, const File& jucePath, const File& userPath)
 -     : pipFile (pip),
 -       juceModulesPath (jucePath),
 -       userModulesPath (userPath),
 -       metadata (parseJUCEHeaderMetadata (pipFile))
 - {
 -     if (output != File())
 -     {
 -         outputDirectory = output;
 -         isTemp = false;
 -     }
 -     else
 -     {
 -         outputDirectory = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs");
 -         isTemp = true;
 -     }
 - 
 -     auto isClipboard = (pip.getParentDirectory().getFileName() == "Clipboard"
 -                         && pip.getParentDirectory().getParentDirectory().getFileName() == "PIPs");
 - 
 -     outputDirectory = outputDirectory.getChildFile (metadata[Ids::name].toString()).getNonexistentSibling();
 -     useLocalCopy = metadata[Ids::useLocalCopy].toString().trim().getIntValue() == 1 || isClipboard;
 - 
 -     if (userModulesPath != File())
 -     {
 -         availableUserModules.reset (new AvailableModulesList());
 -         availableUserModules->scanPaths ({ userModulesPath });
 -     }
 - }
 - 
 - //==============================================================================
 - Result PIPGenerator::createJucerFile()
 - {
 -     ValueTree root (Ids::JUCERPROJECT);
 - 
 -     auto result = setProjectSettings (root);
 - 
 -     if (result != Result::ok())
 -         return result;
 - 
 -     addModules     (root);
 -     addExporters   (root);
 -     createFiles    (root);
 -     setModuleFlags (root);
 - 
 -     auto outputFile = outputDirectory.getChildFile (metadata[Ids::name].toString() + ".jucer");
 - 
 -     if (auto xml = root.createXml())
 -         if (xml->writeTo (outputFile, {}))
 -             return Result::ok();
 - 
 -     return Result::fail ("Failed to create .jucer file in " + outputDirectory.getFullPathName());
 - }
 - 
 - Result PIPGenerator::createMainCpp()
 - {
 -     auto outputFile = outputDirectory.getChildFile ("Source").getChildFile ("Main.cpp");
 - 
 -     if (! outputFile.existsAsFile() && (outputFile.create() != Result::ok()))
 -         return Result::fail ("Failed to create Main.cpp - " + outputFile.getFullPathName());
 - 
 -     outputFile.replaceWithText (getMainFileTextForType());
 - 
 -     return Result::ok();
 - }
 - 
 - //==============================================================================
 - void PIPGenerator::addFileToTree (ValueTree& groupTree, const String& name, bool compile, const String& path)
 - {
 -     ValueTree file (Ids::FILE);
 -     file.setProperty (Ids::ID, createAlphaNumericUID(), nullptr);
 -     file.setProperty (Ids::name, name, nullptr);
 -     file.setProperty (Ids::compile, compile, nullptr);
 -     file.setProperty (Ids::resource, 0, nullptr);
 -     file.setProperty (Ids::file, path, nullptr);
 - 
 -     groupTree.addChild (file, -1, nullptr);
 - }
 - 
 - void PIPGenerator::createFiles (ValueTree& jucerTree)
 - {
 -     auto sourceDir = outputDirectory.getChildFile ("Source");
 - 
 -     if (! sourceDir.exists())
 -         sourceDir.createDirectory();
 - 
 -     if (useLocalCopy)
 -         pipFile.copyFileTo (sourceDir.getChildFile (pipFile.getFileName()));
 - 
 -     ValueTree mainGroup (Ids::MAINGROUP);
 -     mainGroup.setProperty (Ids::ID, createAlphaNumericUID(), nullptr);
 -     mainGroup.setProperty (Ids::name, metadata[Ids::name], nullptr);
 - 
 -     ValueTree group (Ids::GROUP);
 -     group.setProperty (Ids::ID, createGUID (sourceDir.getFullPathName() + "_guidpathsaltxhsdf"), nullptr);
 -     group.setProperty (Ids::name, "Source", nullptr);
 - 
 -     addFileToTree (group, "Main.cpp", true, "Source/Main.cpp");
 -     addFileToTree (group, pipFile.getFileName(), false, useLocalCopy ? "Source/" + pipFile.getFileName()
 -                                                                      : pipFile.getFullPathName());
 - 
 -     mainGroup.addChild (group, -1, nullptr);
 - 
 -     if (useLocalCopy)
 -     {
 -         auto relativeFiles = replaceRelativeIncludesAndGetFilesToMove();
 - 
 -         if (relativeFiles.size() > 0)
 -         {
 -             ValueTree assets (Ids::GROUP);
 -             assets.setProperty (Ids::ID, createAlphaNumericUID(), nullptr);
 -             assets.setProperty (Ids::name, "Assets", nullptr);
 - 
 -             for (auto& f : relativeFiles)
 -                 if (copyRelativeFileToLocalSourceDirectory (f))
 -                     addFileToTree (assets, f.getFileName(), f.getFileExtension() == ".cpp", "Source/" + f.getFileName());
 - 
 -             mainGroup.addChild (assets, -1, nullptr);
 -         }
 -     }
 - 
 -     jucerTree.addChild (mainGroup, 0, nullptr);
 - }
 - 
 - String PIPGenerator::getDocumentControllerClass() const
 - {
 -     return metadata.getProperty (Ids::documentControllerClass, "").toString();
 - }
 - 
 - ValueTree PIPGenerator::createModulePathChild (const String& moduleID)
 - {
 -     ValueTree modulePath (Ids::MODULEPATH);
 - 
 -     modulePath.setProperty (Ids::ID, moduleID, nullptr);
 -     modulePath.setProperty (Ids::path, getPathForModule (moduleID), nullptr);
 - 
 -     return modulePath;
 - }
 - 
 - ValueTree PIPGenerator::createBuildConfigChild (bool isDebug)
 - {
 -     ValueTree child (Ids::CONFIGURATION);
 - 
 -     child.setProperty (Ids::name, isDebug ? "Debug" : "Release", nullptr);
 -     child.setProperty (Ids::isDebug, isDebug ? 1 : 0, nullptr);
 -     child.setProperty (Ids::optimisation, isDebug ? 1 : 3, nullptr);
 -     child.setProperty (Ids::targetName, metadata[Ids::name], nullptr);
 - 
 -     return child;
 - }
 - 
 - ValueTree PIPGenerator::createExporterChild (const Identifier& exporterIdentifier)
 - {
 -     ValueTree exporter (exporterIdentifier);
 - 
 -     exporter.setProperty (Ids::targetFolder, "Builds/" + ProjectExporter::getTypeInfoForExporter (exporterIdentifier).targetFolder, nullptr);
 - 
 -     if (isJUCEExample (pipFile) && exporterRequiresExampleAssets (exporterIdentifier, metadata[Ids::name]))
 -     {
 -         auto examplesDir = getExamplesDirectory();
 - 
 -         if (examplesDir != File())
 -         {
 -             auto assetsDirectoryPath = examplesDir.getChildFile ("Assets").getFullPathName();
 - 
 -             exporter.setProperty (exporterIdentifier.toString() == AndroidProjectExporter::getValueTreeTypeName() ? Ids::androidExtraAssetsFolder
 -                                                                                                                   : Ids::customXcodeResourceFolders,
 -                                   assetsDirectoryPath, nullptr);
 -         }
 -         else
 -         {
 -             // invalid JUCE path
 -             jassertfalse;
 -         }
 -     }
 - 
 -     if (exporterIdentifier.toString() == AndroidProjectExporter::getValueTreeTypeName())
 -         exporter.setProperty (Ids::androidBluetoothNeeded, true, nullptr);
 - 
 -     {
 -         ValueTree configs (Ids::CONFIGURATIONS);
 - 
 -         configs.addChild (createBuildConfigChild (true), -1, nullptr);
 -         configs.addChild (createBuildConfigChild (false), -1, nullptr);
 - 
 -         exporter.addChild (configs, -1, nullptr);
 -     }
 - 
 -     {
 -         ValueTree modulePaths (Ids::MODULEPATHS);
 - 
 -         auto modules = StringArray::fromTokens (metadata[Ids::dependencies_].toString(), ",", {});
 - 
 -         for (auto m : modules)
 -             modulePaths.addChild (createModulePathChild (m.trim()), -1, nullptr);
 - 
 -         exporter.addChild (modulePaths, -1, nullptr);
 -     }
 - 
 -     return exporter;
 - }
 - 
 - ValueTree PIPGenerator::createModuleChild (const String& moduleID)
 - {
 -     ValueTree module (Ids::MODULE);
 - 
 -     module.setProperty (Ids::ID, moduleID, nullptr);
 -     module.setProperty (Ids::showAllCode, 1, nullptr);
 -     module.setProperty (Ids::useLocalCopy, 0, nullptr);
 -     module.setProperty (Ids::useGlobalPath, (getPathForModule (moduleID).isEmpty() ? 1 : 0), nullptr);
 - 
 -     return module;
 - }
 - 
 - void PIPGenerator::addExporters (ValueTree& jucerTree)
 - {
 -     ValueTree exportersTree (Ids::EXPORTFORMATS);
 - 
 -     auto exporters = StringArray::fromTokens (metadata[Ids::exporters].toString(), ",", {});
 - 
 -     for (auto& e : exporters)
 -     {
 -         e = e.trim().toUpperCase();
 - 
 -         if (isValidExporterIdentifier (e))
 -             exportersTree.addChild (createExporterChild (e), -1, nullptr);
 -     }
 - 
 -     jucerTree.addChild (exportersTree, -1, nullptr);
 - }
 - 
 - void PIPGenerator::addModules (ValueTree& jucerTree)
 - {
 -     ValueTree modulesTree (Ids::MODULES);
 - 
 -     auto modules = StringArray::fromTokens (metadata[Ids::dependencies_].toString(), ",", {});
 -     modules.trim();
 - 
 -     for (auto& m : modules)
 -         modulesTree.addChild (createModuleChild (m.trim()), -1, nullptr);
 - 
 -     jucerTree.addChild (modulesTree, -1, nullptr);
 - }
 - 
 - Result PIPGenerator::setProjectSettings (ValueTree& jucerTree)
 - {
 -     auto setPropertyIfNotEmpty = [&jucerTree] (const Identifier& name, const var& value)
 -     {
 -         if (value != var())
 -             jucerTree.setProperty (name, value, nullptr);
 -     };
 - 
 -     setPropertyIfNotEmpty (Ids::name, metadata[Ids::name]);
 -     setPropertyIfNotEmpty (Ids::companyName, metadata[Ids::vendor]);
 -     setPropertyIfNotEmpty (Ids::version, metadata[Ids::version]);
 -     setPropertyIfNotEmpty (Ids::userNotes, metadata[Ids::description]);
 -     setPropertyIfNotEmpty (Ids::companyWebsite, metadata[Ids::website]);
 - 
 -     auto defines = metadata[Ids::defines].toString();
 - 
 -     if (isJUCEExample (pipFile))
 -     {
 -         auto examplesDir = getExamplesDirectory();
 - 
 -         if (examplesDir != File())
 -         {
 -              defines += ((defines.isEmpty() ? "" : " ") + String ("PIP_JUCE_EXAMPLES_DIRECTORY=")
 -                          + Base64::toBase64 (examplesDir.getFullPathName()));
 -         }
 -         else
 -         {
 -             return Result::fail (String ("Invalid JUCE path. Set path to JUCE via ") +
 -                                  (TargetOS::getThisOS() == TargetOS::osx ? "\"Projucer->Global Paths...\""
 -                                                                          : "\"File->Global Paths...\"")
 -                                  + " menu item.");
 -         }
 - 
 -         jucerTree.setProperty (Ids::displaySplashScreen, true, nullptr);
 -     }
 - 
 -     setPropertyIfNotEmpty (Ids::defines, defines);
 - 
 -     auto type = metadata[Ids::type].toString();
 - 
 -     if (type == "Console")
 -     {
 -         jucerTree.setProperty (Ids::projectType, build_tools::ProjectType_ConsoleApp::getTypeName(), nullptr);
 -     }
 -     else if (type == "Component")
 -     {
 -         jucerTree.setProperty (Ids::projectType, build_tools::ProjectType_GUIApp::getTypeName(), nullptr);
 -     }
 -     else if (type == "AudioProcessor")
 -     {
 -         jucerTree.setProperty (Ids::projectType, build_tools::ProjectType_AudioPlugin::getTypeName(), nullptr);
 -         jucerTree.setProperty (Ids::pluginAUIsSandboxSafe, "1", nullptr);
 - 
 -         setPropertyIfNotEmpty (Ids::pluginManufacturer, metadata[Ids::vendor]);
 - 
 -         StringArray pluginFormatsToBuild (Ids::buildVST3.toString(), Ids::buildAU.toString(), Ids::buildStandalone.toString());
 -         pluginFormatsToBuild.addArray (getExtraPluginFormatsToBuild());
 - 
 -         if (getDocumentControllerClass().isNotEmpty())
 -             pluginFormatsToBuild.add (Ids::enableARA.toString());
 - 
 -         jucerTree.setProperty (Ids::pluginFormats, pluginFormatsToBuild.joinIntoString (","), nullptr);
 - 
 -         const auto characteristics = metadata[Ids::pluginCharacteristics].toString();
 - 
 -         if (characteristics.isNotEmpty())
 -             jucerTree.setProperty (Ids::pluginCharacteristicsValue,
 -                                    characteristics.removeCharacters (" \t\n\r"),
 -                                    nullptr);
 -     }
 - 
 -     jucerTree.setProperty (Ids::useAppConfig, false, nullptr);
 -     jucerTree.setProperty (Ids::addUsingNamespaceToJuceHeader, true, nullptr);
 - 
 -     return Result::ok();
 - }
 - 
 - void PIPGenerator::setModuleFlags (ValueTree& jucerTree)
 - {
 -     ValueTree options ("JUCEOPTIONS");
 - 
 -     for (auto& option : StringArray::fromTokens (metadata[Ids::moduleFlags].toString(), ",", {}))
 -     {
 -         auto name  = option.upToFirstOccurrenceOf ("=", false, true).trim();
 -         auto value = option.fromFirstOccurrenceOf ("=", false, true).trim();
 - 
 -         options.setProperty (name, (value == "1" ? 1 : 0), nullptr);
 -     }
 - 
 -     if (metadata[Ids::type].toString() == "AudioProcessor"
 -           && options.getPropertyPointer ("JUCE_VST3_CAN_REPLACE_VST2") == nullptr)
 -         options.setProperty ("JUCE_VST3_CAN_REPLACE_VST2", 0, nullptr);
 - 
 -     jucerTree.addChild (options, -1, nullptr);
 - }
 - 
 - String PIPGenerator::getMainFileTextForType()
 - {
 -     const auto type = metadata[Ids::type].toString();
 -     const auto documentControllerClass = getDocumentControllerClass();
 - 
 -     const auto mainTemplate = [&]
 -     {
 -         if (type == "Console")
 -             return String (BinaryData::PIPConsole_cpp_in);
 - 
 -         if (type == "Component")
 -             return String (BinaryData::PIPComponent_cpp_in)
 -                    .replace ("${JUCE_PIP_NAME}",       metadata[Ids::name].toString())
 -                    .replace ("${PROJECT_VERSION}",     metadata[Ids::version].toString())
 -                    .replace ("${JUCE_PIP_MAIN_CLASS}", metadata[Ids::mainClass].toString());
 - 
 -         if (type == "AudioProcessor")
 -         {
 -             if (documentControllerClass.isNotEmpty())
 -             {
 -                 return String (BinaryData::PIPAudioProcessorWithARA_cpp_in)
 -                        .replace ("${JUCE_PIP_MAIN_CLASS}", metadata[Ids::mainClass].toString())
 -                        .replace ("${JUCE_PIP_DOCUMENTCONTROLLER_CLASS}", documentControllerClass);
 -             }
 - 
 -             return String (BinaryData::PIPAudioProcessor_cpp_in)
 -                    .replace ("${JUCE_PIP_MAIN_CLASS}", metadata[Ids::mainClass].toString());
 -         }
 - 
 -         return String{};
 -     }();
 - 
 -     if (mainTemplate.isEmpty())
 -         return {};
 - 
 -     const auto includeFilename = [&]
 -     {
 -         if (useLocalCopy) return pipFile.getFileName();
 -         if (isTemp)       return pipFile.getFullPathName();
 - 
 -         return build_tools::RelativePath (pipFile,
 -                                           outputDirectory.getChildFile ("Source"),
 -                                           build_tools::RelativePath::unknown).toUnixStyle();
 -     }();
 - 
 -     return ensureCorrectWhitespace (mainTemplate.replace ("${JUCE_PIP_HEADER}", includeFilename));
 - }
 - 
 - //==============================================================================
 - Array<File> PIPGenerator::replaceRelativeIncludesAndGetFilesToMove()
 - {
 -     StringArray lines;
 -     pipFile.readLines (lines);
 -     Array<File> files;
 - 
 -     for (auto& line : lines)
 -     {
 -         if (line.contains ("#include") && ! line.contains ("JuceLibraryCode"))
 -         {
 -             auto path = line.fromFirstOccurrenceOf ("#include", false, false);
 -             path = path.removeCharacters ("\"").trim();
 - 
 -             if (path.startsWith ("<") && path.endsWith (">"))
 -                 continue;
 - 
 -             auto file = pipFile.getParentDirectory().getChildFile (path);
 -             files.add (file);
 - 
 -             line = line.replace (path, file.getFileName());
 -         }
 -     }
 - 
 -     outputDirectory.getChildFile ("Source")
 -                    .getChildFile (pipFile.getFileName())
 -                    .replaceWithText (joinLinesIntoSourceFile (lines));
 - 
 -     return files;
 - }
 - 
 - bool PIPGenerator::copyRelativeFileToLocalSourceDirectory (const File& fileToCopy) const noexcept
 - {
 -     return fileToCopy.copyFileTo (outputDirectory.getChildFile ("Source")
 -                                                  .getChildFile (fileToCopy.getFileName()));
 - }
 - 
 - StringArray PIPGenerator::getExtraPluginFormatsToBuild() const
 - {
 -     auto tokens = StringArray::fromTokens (metadata[Ids::extraPluginFormats].toString(), ",", {});
 - 
 -     for (auto& token : tokens)
 -     {
 -         token = [&]
 -         {
 -             if (token == "IAA")
 -                 return Ids::enableIAA.toString();
 - 
 -             return "build" + token;
 -         }();
 -     }
 - 
 -     return tokens;
 - }
 - 
 - String PIPGenerator::getPathForModule (const String& moduleID) const
 - {
 -     if (isJUCEModule (moduleID))
 -     {
 -         if (juceModulesPath != File())
 -         {
 -             if (isTemp)
 -                 return juceModulesPath.getFullPathName();
 - 
 -             return build_tools::RelativePath (juceModulesPath,
 -                                               outputDirectory,
 -                                               build_tools::RelativePath::projectFolder).toUnixStyle();
 -         }
 -     }
 -     else if (availableUserModules != nullptr)
 -     {
 -         auto moduleRoot = availableUserModules->getModuleWithID (moduleID).second.getParentDirectory();
 - 
 -         if (isTemp)
 -             return moduleRoot.getFullPathName();
 - 
 -         return build_tools::RelativePath (moduleRoot,
 -                                           outputDirectory,
 -                                           build_tools::RelativePath::projectFolder).toUnixStyle();
 -     }
 - 
 -     return {};
 - }
 - 
 - File PIPGenerator::getExamplesDirectory() const
 - {
 -     if (juceModulesPath != File())
 -     {
 -         auto examples = juceModulesPath.getSiblingFile ("examples");
 - 
 -         if (isValidJUCEExamplesDirectory (examples))
 -             return examples;
 -     }
 - 
 -     auto examples = File (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString()).getChildFile ("examples");
 - 
 -     if (isValidJUCEExamplesDirectory (examples))
 -         return examples;
 - 
 -     return {};
 - }
 
 
  |