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.

245 lines
7.5KB

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