The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

248 lines
7.6KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. #pragma once
  19. #include <regex>
  20. //==============================================================================
  21. class XcodeProjectParser
  22. {
  23. public:
  24. //==============================================================================
  25. static std::unique_ptr<HashMap<std::string, std::string>> parseObjects (const File& projectFile)
  26. {
  27. auto pbxprojs = projectFile.findChildFiles (File::TypesOfFileToFind::findFiles, false, "*.pbxproj");
  28. if (pbxprojs.isEmpty())
  29. {
  30. jassertfalse;
  31. return nullptr;
  32. }
  33. auto content = pbxprojs[0].loadFileAsString().toStdString();
  34. std::regex comments ("/\\*.*?\\*/");
  35. std::string empty ("");
  36. content = (std::regex_replace (content, comments, empty));
  37. std::regex whitespace ("\\s+");
  38. std::string space (" ");
  39. content = (std::regex_replace (content, whitespace, space));
  40. auto objects = std::make_unique<HashMap<std::string, std::string>>();
  41. std::smatch objectsStartMatch;
  42. if (! std::regex_search (content, objectsStartMatch, std::regex ("[ ;{]+objects *= *\\{")))
  43. {
  44. jassertfalse;
  45. return nullptr;
  46. }
  47. auto strPtr = content.begin() + objectsStartMatch.position() + objectsStartMatch.length();
  48. while (strPtr++ != content.end())
  49. {
  50. if (*strPtr == ' ' || *strPtr == ';')
  51. continue;
  52. if (*strPtr == '}')
  53. break;
  54. auto groupReference = parseObjectID (content, strPtr);
  55. if (groupReference.empty())
  56. {
  57. jassertfalse;
  58. return nullptr;
  59. }
  60. while (*strPtr == ' ' || *strPtr == '=')
  61. {
  62. if (++strPtr == content.end())
  63. {
  64. jassertfalse;
  65. return nullptr;
  66. }
  67. }
  68. auto bracedContent = parseBracedContent (content, strPtr);
  69. if (bracedContent.empty())
  70. return nullptr;
  71. objects->set (groupReference, bracedContent);
  72. }
  73. jassert (strPtr <= content.end());
  74. return objects;
  75. }
  76. static std::pair<std::string, std::string> findObjectMatching (const HashMap<std::string, std::string>& objects,
  77. const std::regex& rgx)
  78. {
  79. HashMap<std::string, std::string>::Iterator it (objects);
  80. std::smatch match;
  81. while (it.next())
  82. {
  83. auto key = it.getValue();
  84. if (std::regex_search (key, match, rgx))
  85. return { it.getKey(), it.getValue() };
  86. }
  87. return {};
  88. }
  89. //==============================================================================
  90. static std::vector<std::pair<String, String>> parseBuildProducts (const File& projectFile)
  91. {
  92. auto objects = parseObjects (projectFile);
  93. if (objects == nullptr)
  94. return {};
  95. auto mainObject = findObjectMatching (*objects, std::regex ("[ ;{]+isa *= *PBXProject[ ;}]+"));
  96. jassert (! mainObject.first.empty());
  97. auto targetRefs = parseObjectItemList (mainObject.second, "targets");
  98. jassert (! targetRefs.isEmpty());
  99. std::vector<std::pair<String, String>> results;
  100. for (auto& t : targetRefs)
  101. {
  102. auto targetRef = t.toStdString();
  103. if (! objects->contains (targetRef))
  104. {
  105. jassertfalse;
  106. continue;
  107. }
  108. auto name = parseObjectItemValue (objects->getReference (targetRef), "name");
  109. if (name.empty())
  110. continue;
  111. auto productRef = parseObjectItemValue (objects->getReference (targetRef), "productReference");
  112. if (productRef.empty())
  113. continue;
  114. if (! objects->contains (productRef))
  115. {
  116. jassertfalse;
  117. continue;
  118. }
  119. auto path = parseObjectItemValue (objects->getReference (productRef), "path");
  120. if (path.empty())
  121. continue;
  122. results.push_back ({ String (name).unquoted(), String (path).unquoted() });
  123. }
  124. return results;
  125. }
  126. private:
  127. //==============================================================================
  128. static std::string parseObjectID (std::string& content, std::string::iterator& ptr)
  129. {
  130. auto start = ptr;
  131. while (ptr != content.end() && *ptr != ' ' && *ptr != ';' && *ptr != '=')
  132. ++ptr;
  133. return ptr == content.end() ? std::string()
  134. : content.substr ((size_t) std::distance (content.begin(), start),
  135. (size_t) std::distance (start, ptr));
  136. }
  137. //==============================================================================
  138. static std::string parseBracedContent (std::string& content, std::string::iterator& ptr)
  139. {
  140. jassert (*ptr == '{');
  141. auto start = ++ptr;
  142. auto braceDepth = 1;
  143. while (ptr++ != content.end())
  144. {
  145. switch (*ptr)
  146. {
  147. case '{':
  148. ++braceDepth;
  149. break;
  150. case '}':
  151. if (--braceDepth == 0)
  152. return content.substr ((size_t) std::distance (content.begin(), start),
  153. (size_t) std::distance (start, ptr));
  154. default:
  155. break;
  156. }
  157. }
  158. jassertfalse;
  159. return {};
  160. }
  161. //==============================================================================
  162. static std::string parseObjectItemValue (const std::string& source, const std::string& key)
  163. {
  164. std::smatch match;
  165. if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *(.*?)[ ;]+")))
  166. {
  167. jassertfalse;
  168. return {};
  169. }
  170. return match[1];
  171. }
  172. //==============================================================================
  173. static StringArray parseObjectItemList (const std::string& source, const std::string& key)
  174. {
  175. std::smatch match;
  176. if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *\\((.*?)\\)")))
  177. {
  178. jassertfalse;
  179. return {};
  180. }
  181. auto result = StringArray::fromTokens (String (match[1]), ", ", "");
  182. result.removeEmptyStrings();
  183. return result;
  184. }
  185. };