|
- /*
- ==============================================================================
-
- 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.
-
- ==============================================================================
- */
-
- #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 ("/\\*.*?\\*/");
- std::string empty ("");
- content = (std::regex_replace (content, comments, empty));
-
- std::regex whitespace ("\\s+");
- std::string space (" ");
- content = (std::regex_replace (content, whitespace, space));
-
- 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 {};
- }
-
- //==============================================================================
- struct BuildProduct
- {
- String name;
- String path;
- };
-
- static std::vector<BuildProduct> 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<BuildProduct> 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));
-
- default:
- break;
- }
- }
-
- 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;
- }
- };
|