mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Projucer: Enabled adding Xcode subprojects
This commit is contained in:
parent
9829a8ad09
commit
885168568d
11 changed files with 459 additions and 32 deletions
|
|
@ -374,6 +374,13 @@
|
|||
path = "../../Source/Application/jucer_CommandLine.cpp";
|
||||
sourceTree = "SOURCE_ROOT";
|
||||
};
|
||||
044478BB994878E35D30154A = {
|
||||
isa = PBXFileReference;
|
||||
lastKnownFileType = sourcecode.c.h;
|
||||
name = "jucer_XcodeProjectParser.h";
|
||||
path = "../../Source/ProjectSaving/jucer_XcodeProjectParser.h";
|
||||
sourceTree = "SOURCE_ROOT";
|
||||
};
|
||||
0462692BAA9CD1BE6DFBCC33 = {
|
||||
isa = PBXFileReference;
|
||||
lastKnownFileType = sourcecode.cpp.objcpp;
|
||||
|
|
@ -2985,6 +2992,7 @@
|
|||
9EE3141E20C9CE3EA182FA04,
|
||||
E13A54A6D3A1895EACE53E51,
|
||||
25BE1265FE6C6EA3473A3A0A,
|
||||
044478BB994878E35D30154A,
|
||||
);
|
||||
name = ProjectSaving;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
|||
|
|
@ -1611,6 +1611,7 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_StoredSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.h"/>
|
||||
|
|
|
|||
|
|
@ -2283,6 +2283,9 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h">
|
||||
<Filter>Projucer\Settings</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -1611,6 +1611,7 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_StoredSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.h"/>
|
||||
|
|
|
|||
|
|
@ -2283,6 +2283,9 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h">
|
||||
<Filter>Projucer\Settings</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -1613,6 +1613,7 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectExporter.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ProjectSaver.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h"/>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_StoredSettings.h"/>
|
||||
<ClInclude Include="..\..\Source\Utility\Helpers\jucer_CodeHelpers.h"/>
|
||||
|
|
|
|||
|
|
@ -2283,6 +2283,9 @@
|
|||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_ResourceFile.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\ProjectSaving\jucer_XcodeProjectParser.h">
|
||||
<Filter>Projucer\ProjectSaving</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\Source\Settings\jucer_AppearanceSettings.h">
|
||||
<Filter>Projucer\Settings</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -608,6 +608,8 @@
|
|||
file="Source/ProjectSaving/jucer_ResourceFile.cpp"/>
|
||||
<FILE id="d4INvx" name="jucer_ResourceFile.h" compile="0" resource="0"
|
||||
file="Source/ProjectSaving/jucer_ResourceFile.h"/>
|
||||
<FILE id="kw0nyx" name="jucer_XcodeProjectParser.h" compile="0" resource="0"
|
||||
file="Source/ProjectSaving/jucer_XcodeProjectParser.h"/>
|
||||
</GROUP>
|
||||
<GROUP id="{DC7C1395-DD88-0C6C-2D8A-EC48A63D31E8}" name="Settings">
|
||||
<FILE id="DsopUB" name="jucer_AppearanceSettings.cpp" compile="1" resource="0"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_XcodeProjectParser.h"
|
||||
|
||||
//==============================================================================
|
||||
namespace
|
||||
|
|
@ -61,6 +62,7 @@ public:
|
|||
customPListValue (settings, Ids::customPList, getUndoManager()),
|
||||
pListPrefixHeaderValue (settings, Ids::pListPrefixHeader, getUndoManager()),
|
||||
pListPreprocessValue (settings, Ids::pListPreprocess, getUndoManager()),
|
||||
subprojectsValue (settings, Ids::xcodeSubprojects, getUndoManager()),
|
||||
extraFrameworksValue (settings, Ids::extraFrameworks, getUndoManager()),
|
||||
frameworkSearchPathsValue (settings, Ids::frameworkSearchPaths, getUndoManager()),
|
||||
extraCustomFrameworksValue (settings, Ids::extraCustomFrameworks, getUndoManager()),
|
||||
|
|
@ -112,6 +114,8 @@ public:
|
|||
String getPListPrefixHeaderString() const { return pListPrefixHeaderValue.get(); }
|
||||
bool isPListPreprocessEnabled() const { return pListPreprocessValue.get(); }
|
||||
|
||||
String getSubprojectsString() const { return subprojectsValue.get(); }
|
||||
|
||||
String getExtraFrameworksString() const { return extraFrameworksValue.get(); }
|
||||
String getFrameworkSearchPathsString() const { return frameworkSearchPathsValue.get(); }
|
||||
String getExtraCustomFrameworksString() const { return extraCustomFrameworksValue.get(); }
|
||||
|
|
@ -317,6 +321,12 @@ public:
|
|||
"If you are adding a framework here then you do not need to specify it in Extra Custom Frameworks too. "
|
||||
"You will probably need to add an entry to the Framework Search Paths for each unique directory.");
|
||||
|
||||
props.add (new TextPropertyComponent (subprojectsValue, "Xcode Subprojects", 8192, true),
|
||||
"Paths to Xcode projects that should be added to the build (one per line). "
|
||||
"The names of the required build products can be specified after a colon, comma seperated, "
|
||||
"e.g. \"path/to/MySubProject.xcodeproj: MySubProject, OtherTarget\". "
|
||||
"If no build products are specified, all build products associated with a subproject will be added.");
|
||||
|
||||
props.add (new TextPropertyComponent (prebuildCommandValue, "Pre-Build Shell Script", 32768, true),
|
||||
"Some shell-script that will be run before a build starts.");
|
||||
|
||||
|
|
@ -1105,8 +1115,8 @@ public:
|
|||
if (type == Target::SharedCodeTarget)
|
||||
s.set ("SKIP_INSTALL", "YES");
|
||||
|
||||
if (! owner.getEmbeddedFrameworks().isEmpty())
|
||||
s.set ("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks\"");
|
||||
if (! owner.embeddedFrameworkIDs.isEmpty())
|
||||
s.set ("LD_RUNPATH_SEARCH_PATHS", "\"$(inherited) @executable_path/Frameworks @executable_path/../Frameworks\"");
|
||||
|
||||
if (xcodeCopyToProductInstallPathAfterBuild)
|
||||
{
|
||||
|
|
@ -1769,14 +1779,18 @@ private:
|
|||
|
||||
mutable OwnedArray<ValueTree> pbxBuildFiles, pbxFileReferences, pbxGroups, misc, projectConfigs, targetConfigs;
|
||||
mutable StringArray resourceIDs, sourceIDs, targetIDs;
|
||||
mutable StringArray frameworkFileIDs, rezFileIDs, resourceFileRefs;
|
||||
mutable StringArray frameworkFileIDs, embeddedFrameworkIDs, rezFileIDs, resourceFileRefs, subprojectFileIDs;
|
||||
mutable Array<std::pair<String, String>> subprojectReferences;
|
||||
mutable File menuNibFile, iconFile;
|
||||
mutable StringArray buildProducts;
|
||||
|
||||
const bool iOS;
|
||||
|
||||
ValueWithDefault customPListValue, pListPrefixHeaderValue, pListPreprocessValue, extraFrameworksValue, frameworkSearchPathsValue, extraCustomFrameworksValue, embeddedFrameworksValue,
|
||||
postbuildCommandValue, prebuildCommandValue, duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue,
|
||||
ValueWithDefault customPListValue, pListPrefixHeaderValue, pListPreprocessValue,
|
||||
subprojectsValue,
|
||||
extraFrameworksValue, frameworkSearchPathsValue, extraCustomFrameworksValue, embeddedFrameworksValue,
|
||||
postbuildCommandValue, prebuildCommandValue,
|
||||
duplicateAppExResourcesFolderValue, iosDeviceFamilyValue, iPhoneScreenOrientationValue,
|
||||
iPadScreenOrientationValue, customXcodeResourceFoldersValue, customXcassetsFolderValue,
|
||||
microphonePermissionNeededValue, microphonePermissionsTextValue, cameraPermissionNeededValue, cameraPermissionTextValue,
|
||||
uiFileSharingEnabledValue, uiSupportsDocumentBrowserValue, uiStatusBarHiddenValue, documentExtensionsValue, iosInAppPurchasesValue,
|
||||
|
|
@ -1803,9 +1817,14 @@ private:
|
|||
{
|
||||
prepareTargets();
|
||||
|
||||
// Must be called before adding embedded frameworks, as we want to
|
||||
// embed any frameworks found in subprojects.
|
||||
addSubprojects();
|
||||
|
||||
addFrameworks();
|
||||
addCustomFrameworks();
|
||||
addEmbeddedFrameworks();
|
||||
|
||||
addCustomResourceFolders();
|
||||
addPlistFileReferences();
|
||||
|
||||
|
|
@ -1948,6 +1967,13 @@ private:
|
|||
addGroup (productsGroupID, "Products", buildProducts);
|
||||
topLevelGroupIDs.add (productsGroupID);
|
||||
}
|
||||
|
||||
if (! subprojectFileIDs.isEmpty())
|
||||
{
|
||||
auto subprojectLibrariesGroupID = createID ("__subprojects");
|
||||
addGroup (subprojectLibrariesGroupID, "Subprojects", subprojectFileIDs);
|
||||
topLevelGroupIDs.add (subprojectLibrariesGroupID);
|
||||
}
|
||||
}
|
||||
|
||||
void addBuildPhases() const
|
||||
|
|
@ -2479,12 +2505,9 @@ private:
|
|||
|
||||
void addEmbeddedFrameworks() const
|
||||
{
|
||||
auto frameworks = getEmbeddedFrameworks();
|
||||
|
||||
if (frameworks.isEmpty())
|
||||
return;
|
||||
|
||||
StringArray embeddedFrameworkIDs;
|
||||
StringArray frameworks;
|
||||
frameworks.addTokens (getEmbeddedFrameworksString(), true);
|
||||
frameworks.trim();
|
||||
|
||||
for (auto& framework : frameworks)
|
||||
{
|
||||
|
|
@ -2498,17 +2521,9 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
for (auto& target : targets)
|
||||
target->addCopyFilesPhase ("Embed Frameworks", embeddedFrameworkIDs, kFrameworksFolder);
|
||||
}
|
||||
|
||||
StringArray getEmbeddedFrameworks() const
|
||||
{
|
||||
StringArray frameworks;
|
||||
frameworks.addTokens (getEmbeddedFrameworksString(), true);
|
||||
frameworks.trim();
|
||||
|
||||
return frameworks;
|
||||
if (! embeddedFrameworkIDs.isEmpty())
|
||||
for (auto& target : targets)
|
||||
target->addCopyFilesPhase ("Embed Frameworks", embeddedFrameworkIDs, kFrameworksFolder);
|
||||
}
|
||||
|
||||
void addCustomResourceFolders() const
|
||||
|
|
@ -2523,6 +2538,104 @@ private:
|
|||
addCustomResourceFolder (crf);
|
||||
}
|
||||
|
||||
void addSubprojects() const
|
||||
{
|
||||
auto subprojectLines = StringArray::fromLines (getSubprojectsString());
|
||||
subprojectLines.removeEmptyStrings (true);
|
||||
|
||||
Array<std::pair<String, StringArray>> subprojects;
|
||||
|
||||
for (auto& line : subprojectLines)
|
||||
{
|
||||
String subprojectName (line.upToFirstOccurrenceOf (":", false, false));
|
||||
StringArray requestedBuildProducts (StringArray::fromTokens (line.fromFirstOccurrenceOf (":", false, false), ",;|", "\"'"));
|
||||
requestedBuildProducts.trim();
|
||||
subprojects.add ({ subprojectName, requestedBuildProducts });
|
||||
}
|
||||
|
||||
for (const auto& subprojectInfo : subprojects)
|
||||
{
|
||||
String subprojectPath (subprojectInfo.first);
|
||||
|
||||
if (! subprojectPath.endsWith (".xcodeproj"))
|
||||
subprojectPath += ".xcodeproj";
|
||||
|
||||
File subprojectFile;
|
||||
|
||||
if (File::isAbsolutePath (subprojectPath))
|
||||
{
|
||||
subprojectFile = subprojectPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
subprojectFile = getProject().getProjectFolder().getChildFile (subprojectPath);
|
||||
|
||||
RelativePath p (subprojectPath, RelativePath::projectFolder);
|
||||
subprojectPath = p.rebased (getProject().getProjectFolder(), getTargetFolder(), RelativePath::buildTargetFolder).toUnixStyle();
|
||||
}
|
||||
|
||||
if (! subprojectFile.isDirectory())
|
||||
continue;
|
||||
|
||||
auto availableBuildProducts = XcodeProjectParser::parseBuildProducts (subprojectFile);
|
||||
|
||||
// If no build products have been specified then we'll take everything
|
||||
if (! subprojectInfo.second.isEmpty())
|
||||
{
|
||||
auto newEnd = std::remove_if (availableBuildProducts.begin(), availableBuildProducts.end(),
|
||||
[&subprojectInfo](const std::pair<String, String> &item)
|
||||
{
|
||||
return ! subprojectInfo.second.contains (item.first);
|
||||
});
|
||||
availableBuildProducts.erase (newEnd, availableBuildProducts.end());
|
||||
}
|
||||
|
||||
if (availableBuildProducts.empty())
|
||||
continue;
|
||||
|
||||
auto subprojectFileType = getFileType (RelativePath (subprojectPath, RelativePath::projectFolder));
|
||||
auto subprojectFileID = addFileOrFolderReference (subprojectPath, "<group>", subprojectFileType);
|
||||
subprojectFileIDs.add (subprojectFileID);
|
||||
|
||||
StringArray proxyIDs;
|
||||
|
||||
for (auto& buildProduct : availableBuildProducts)
|
||||
{
|
||||
auto buildProductFileType = getFileType (RelativePath (buildProduct.second, RelativePath::projectFolder));
|
||||
|
||||
auto containerID = addContainerItemProxy (subprojectFileID, buildProduct.first);
|
||||
auto proxyID = addReferenceProxy (containerID, buildProduct.second, buildProductFileType);
|
||||
proxyIDs.add (proxyID);
|
||||
|
||||
if (buildProductFileType == "archive.ar" || buildProductFileType == "wrapper.framework")
|
||||
{
|
||||
auto buildFileID = addBuildFile (buildProduct.second, proxyID, false, true);
|
||||
|
||||
for (auto& target : targets)
|
||||
target->frameworkIDs.add (buildFileID);
|
||||
|
||||
if (buildProductFileType == "wrapper.framework")
|
||||
{
|
||||
auto fileID = createID (buildProduct.second + "buildref");
|
||||
|
||||
auto* v = new ValueTree (fileID);
|
||||
v->setProperty ("isa", "PBXBuildFile", nullptr);
|
||||
v->setProperty ("fileRef", proxyID, nullptr);
|
||||
v->setProperty ("settings", "{ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }", nullptr);
|
||||
pbxBuildFiles.add (v);
|
||||
|
||||
embeddedFrameworkIDs.add (fileID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto productGroupID = createFileRefID (subprojectPath + "_products");
|
||||
addGroup (productGroupID, "Products", proxyIDs);
|
||||
|
||||
subprojectReferences.add ({ productGroupID, subprojectFileID });
|
||||
}
|
||||
}
|
||||
|
||||
void addXcassets() const
|
||||
{
|
||||
auto customXcassetsPath = getCustomXcassetsFolderString();
|
||||
|
|
@ -2635,17 +2748,8 @@ private:
|
|||
return addFileOrFolderReference (pathString, sourceTree, fileType);
|
||||
}
|
||||
|
||||
String addFileOrFolderReference (String pathString, String sourceTree, String fileType) const
|
||||
void checkAndAddFileReference (std::unique_ptr<ValueTree> v) const
|
||||
{
|
||||
auto fileRefID = createFileRefID (pathString);
|
||||
|
||||
std::unique_ptr<ValueTree> v (new ValueTree (fileRefID));
|
||||
v->setProperty ("isa", "PBXFileReference", nullptr);
|
||||
v->setProperty ("lastKnownFileType", fileType, nullptr);
|
||||
v->setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr);
|
||||
v->setProperty ("path", pathString, nullptr);
|
||||
v->setProperty ("sourceTree", sourceTree, nullptr);
|
||||
|
||||
auto existing = pbxFileReferences.indexOfSorted (*this, v.get());
|
||||
|
||||
if (existing >= 0)
|
||||
|
|
@ -2657,6 +2761,53 @@ private:
|
|||
{
|
||||
pbxFileReferences.addSorted (*this, v.release());
|
||||
}
|
||||
}
|
||||
|
||||
String addFileOrFolderReference (const String& pathString, String sourceTree, String fileType) const
|
||||
{
|
||||
auto fileRefID = createFileRefID (pathString);
|
||||
|
||||
std::unique_ptr<ValueTree> v (new ValueTree (fileRefID));
|
||||
v->setProperty ("isa", "PBXFileReference", nullptr);
|
||||
v->setProperty ("lastKnownFileType", fileType, nullptr);
|
||||
v->setProperty (Ids::name, pathString.fromLastOccurrenceOf ("/", false, false), nullptr);
|
||||
v->setProperty ("path", pathString, nullptr);
|
||||
v->setProperty ("sourceTree", sourceTree, nullptr);
|
||||
|
||||
checkAndAddFileReference (std::move (v));
|
||||
|
||||
return fileRefID;
|
||||
}
|
||||
|
||||
String addContainerItemProxy (const String& subprojectID, const String& itemName) const
|
||||
{
|
||||
auto uniqueString = subprojectID + "_" + itemName;
|
||||
auto fileRefID = createFileRefID (uniqueString);
|
||||
|
||||
std::unique_ptr<ValueTree> v (new ValueTree (fileRefID));
|
||||
v->setProperty ("isa", "PBXContainerItemProxy", nullptr);
|
||||
v->setProperty ("containerPortal", subprojectID, nullptr);
|
||||
v->setProperty ("proxyType", 2, nullptr);
|
||||
v->setProperty ("remoteGlobalIDString", createFileRefID (uniqueString + "_global"), nullptr);
|
||||
v->setProperty ("remoteInfo", itemName, nullptr);
|
||||
|
||||
checkAndAddFileReference (std::move (v));
|
||||
|
||||
return fileRefID;
|
||||
}
|
||||
|
||||
String addReferenceProxy (const String& containerItemID, const String& proxyPath, const String& fileType) const
|
||||
{
|
||||
auto fileRefID = createFileRefID (containerItemID + "_" + proxyPath);
|
||||
|
||||
std::unique_ptr<ValueTree> v (new ValueTree (fileRefID));
|
||||
v->setProperty ("isa", "PBXReferenceProxy", nullptr);
|
||||
v->setProperty ("fileType", fileType, nullptr);
|
||||
v->setProperty ("path", proxyPath, nullptr);
|
||||
v->setProperty ("remoteRef", containerItemID, nullptr);
|
||||
v->setProperty ("sourceTree", "BUILT_PRODUCTS_DIR", nullptr);
|
||||
|
||||
checkAndAddFileReference (std::move (v));
|
||||
|
||||
return fileRefID;
|
||||
}
|
||||
|
|
@ -2996,6 +3147,17 @@ private:
|
|||
v->setProperty ("hasScannedForEncodings", (int) 0, nullptr);
|
||||
v->setProperty ("mainGroup", createID ("__mainsourcegroup"), nullptr);
|
||||
v->setProperty ("projectDirPath", "\"\"", nullptr);
|
||||
|
||||
if (! subprojectReferences.isEmpty())
|
||||
{
|
||||
StringArray projectReferences;
|
||||
|
||||
for (auto& reference : subprojectReferences)
|
||||
projectReferences.add (indentBracedList ({ "ProductGroup = " + reference.first, "ProjectRef = " + reference.second }, 1));
|
||||
|
||||
v->setProperty ("projectReferences", indentParenthesisedList (projectReferences), nullptr);
|
||||
}
|
||||
|
||||
v->setProperty ("projectRoot", "\"\"", nullptr);
|
||||
|
||||
auto targetString = "(" + targetIDs.joinIntoString (", ") + ")";
|
||||
|
|
|
|||
242
extras/Projucer/Source/ProjectSaving/jucer_XcodeProjectParser.h
Normal file
242
extras/Projucer/Source/ProjectSaving/jucer_XcodeProjectParser.h
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 5 End-User License
|
||||
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
|
||||
27th April 2017).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-5-licence
|
||||
Privacy Policy: www.juce.com/juce-5-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
|
||||
//==============================================================================
|
||||
class XcodeProjectParser
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
static std::unique_ptr<HashMap<std::string, std::string>> parseObjects (const File& projectFile)
|
||||
{
|
||||
auto pbxprojs = projectFile.findChildFiles (File::TypesOfFileToFind::findFiles, false, "*.pbxproj");
|
||||
|
||||
if (pbxprojs.isEmpty())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto content = pbxprojs[0].loadFileAsString().toStdString();
|
||||
|
||||
std::regex comments ("/\\*.*?\\*/");
|
||||
content = (std::regex_replace (content, comments, ""));
|
||||
|
||||
std::regex whitespace ("\\s+");
|
||||
content = (std::regex_replace (content, whitespace, " "));
|
||||
|
||||
auto objects = std::make_unique<HashMap<std::string, std::string>>();
|
||||
std::smatch objectsStartMatch;
|
||||
|
||||
if (! std::regex_search (content, objectsStartMatch, std::regex ("[ ;{]+objects *= *\\{")))
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto strPtr = content.begin() + objectsStartMatch.position() + objectsStartMatch.length();
|
||||
|
||||
while (strPtr++ != content.end())
|
||||
{
|
||||
if (*strPtr == ' ' || *strPtr == ';')
|
||||
continue;
|
||||
|
||||
if (*strPtr == '}')
|
||||
break;
|
||||
|
||||
auto groupReference = parseObjectID (content, strPtr);
|
||||
|
||||
if (groupReference.empty())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (*strPtr == ' ' || *strPtr == '=')
|
||||
{
|
||||
if (++strPtr == content.end())
|
||||
{
|
||||
jassertfalse;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto bracedContent = parseBracedContent (content, strPtr);
|
||||
|
||||
if (bracedContent.empty())
|
||||
return nullptr;
|
||||
|
||||
objects->set (groupReference, bracedContent);
|
||||
}
|
||||
|
||||
jassert (strPtr <= content.end());
|
||||
|
||||
return objects;
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> findObjectMatching (const HashMap<std::string, std::string>& objects,
|
||||
const std::regex& rgx)
|
||||
{
|
||||
HashMap<std::string, std::string>::Iterator it (objects);
|
||||
std::smatch match;
|
||||
|
||||
while (it.next())
|
||||
{
|
||||
auto key = it.getValue();
|
||||
|
||||
if (std::regex_search (key, match, rgx))
|
||||
return { it.getKey(), it.getValue() };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::vector<std::pair<String, String>> parseBuildProducts (const File& projectFile)
|
||||
{
|
||||
auto objects = parseObjects (projectFile);
|
||||
|
||||
if (objects == nullptr)
|
||||
return {};
|
||||
|
||||
auto mainObject = findObjectMatching (*objects, std::regex ("[ ;{]+isa *= *PBXProject[ ;}]+"));
|
||||
jassert (! mainObject.first.empty());
|
||||
|
||||
auto targetRefs = parseObjectItemList (mainObject.second, "targets");
|
||||
jassert (! targetRefs.isEmpty());
|
||||
|
||||
std::vector<std::pair<String, String>> results;
|
||||
|
||||
for (auto& t : targetRefs)
|
||||
{
|
||||
auto targetRef = t.toStdString();
|
||||
|
||||
if (! objects->contains (targetRef))
|
||||
{
|
||||
jassertfalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto name = parseObjectItemValue (objects->getReference (targetRef), "name");
|
||||
|
||||
if (name.empty())
|
||||
continue;
|
||||
|
||||
auto productRef = parseObjectItemValue (objects->getReference (targetRef), "productReference");
|
||||
|
||||
if (productRef.empty())
|
||||
continue;
|
||||
|
||||
if (! objects->contains (productRef))
|
||||
{
|
||||
jassertfalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto path = parseObjectItemValue (objects->getReference (productRef), "path");
|
||||
|
||||
if (path.empty())
|
||||
continue;
|
||||
|
||||
results.push_back ({ String (name).unquoted(), String (path).unquoted() });
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static std::string parseObjectID (std::string& content, std::string::iterator& ptr)
|
||||
{
|
||||
auto start = ptr;
|
||||
|
||||
while (ptr != content.end() && *ptr != ' ' && *ptr != ';' && *ptr != '=')
|
||||
++ptr;
|
||||
|
||||
return ptr == content.end() ? std::string()
|
||||
: content.substr ((size_t) std::distance (content.begin(), start),
|
||||
(size_t) std::distance (start, ptr));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::string parseBracedContent (std::string& content, std::string::iterator& ptr)
|
||||
{
|
||||
jassert (*ptr == '{');
|
||||
auto start = ++ptr;
|
||||
auto braceDepth = 1;
|
||||
|
||||
while (ptr++ != content.end())
|
||||
{
|
||||
switch (*ptr)
|
||||
{
|
||||
case '{':
|
||||
++braceDepth;
|
||||
break;
|
||||
case '}':
|
||||
if (--braceDepth == 0)
|
||||
return content.substr ((size_t) std::distance (content.begin(), start),
|
||||
(size_t) std::distance (start, ptr));
|
||||
}
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static std::string parseObjectItemValue (const std::string& source, const std::string& key)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *(.*?)[ ;]+")))
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
return match[1];
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static StringArray parseObjectItemList (const std::string& source, const std::string& key)
|
||||
{
|
||||
std::smatch match;
|
||||
|
||||
if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *\\((.*?)\\)")))
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
auto result = StringArray::fromTokens (String (match[1]), ", ", "");
|
||||
result.removeEmptyStrings();
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
|
@ -125,6 +125,7 @@ namespace Ids
|
|||
DECLARE_ID (osxCompatibility);
|
||||
DECLARE_ID (osxArchitecture);
|
||||
DECLARE_ID (iosCompatibility);
|
||||
DECLARE_ID (xcodeSubprojects);
|
||||
DECLARE_ID (extraFrameworks);
|
||||
DECLARE_ID (frameworkSearchPaths);
|
||||
DECLARE_ID (extraCustomFrameworks);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue