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.

243 lines
7.4KB

  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. content = (std::regex_replace (content, comments, ""));
  37. std::regex whitespace ("\\s+");
  38. content = (std::regex_replace (content, whitespace, " "));
  39. auto objects = std::make_unique<HashMap<std::string, std::string>>();
  40. std::smatch objectsStartMatch;
  41. if (! std::regex_search (content, objectsStartMatch, std::regex ("[ ;{]+objects *= *\\{")))
  42. {
  43. jassertfalse;
  44. return nullptr;
  45. }
  46. auto strPtr = content.begin() + objectsStartMatch.position() + objectsStartMatch.length();
  47. while (strPtr++ != content.end())
  48. {
  49. if (*strPtr == ' ' || *strPtr == ';')
  50. continue;
  51. if (*strPtr == '}')
  52. break;
  53. auto groupReference = parseObjectID (content, strPtr);
  54. if (groupReference.empty())
  55. {
  56. jassertfalse;
  57. return nullptr;
  58. }
  59. while (*strPtr == ' ' || *strPtr == '=')
  60. {
  61. if (++strPtr == content.end())
  62. {
  63. jassertfalse;
  64. return nullptr;
  65. }
  66. }
  67. auto bracedContent = parseBracedContent (content, strPtr);
  68. if (bracedContent.empty())
  69. return nullptr;
  70. objects->set (groupReference, bracedContent);
  71. }
  72. jassert (strPtr <= content.end());
  73. return objects;
  74. }
  75. static std::pair<std::string, std::string> findObjectMatching (const HashMap<std::string, std::string>& objects,
  76. const std::regex& rgx)
  77. {
  78. HashMap<std::string, std::string>::Iterator it (objects);
  79. std::smatch match;
  80. while (it.next())
  81. {
  82. auto key = it.getValue();
  83. if (std::regex_search (key, match, rgx))
  84. return { it.getKey(), it.getValue() };
  85. }
  86. return {};
  87. }
  88. //==============================================================================
  89. static std::vector<std::pair<String, String>> parseBuildProducts (const File& projectFile)
  90. {
  91. auto objects = parseObjects (projectFile);
  92. if (objects == nullptr)
  93. return {};
  94. auto mainObject = findObjectMatching (*objects, std::regex ("[ ;{]+isa *= *PBXProject[ ;}]+"));
  95. jassert (! mainObject.first.empty());
  96. auto targetRefs = parseObjectItemList (mainObject.second, "targets");
  97. jassert (! targetRefs.isEmpty());
  98. std::vector<std::pair<String, String>> results;
  99. for (auto& t : targetRefs)
  100. {
  101. auto targetRef = t.toStdString();
  102. if (! objects->contains (targetRef))
  103. {
  104. jassertfalse;
  105. continue;
  106. }
  107. auto name = parseObjectItemValue (objects->getReference (targetRef), "name");
  108. if (name.empty())
  109. continue;
  110. auto productRef = parseObjectItemValue (objects->getReference (targetRef), "productReference");
  111. if (productRef.empty())
  112. continue;
  113. if (! objects->contains (productRef))
  114. {
  115. jassertfalse;
  116. continue;
  117. }
  118. auto path = parseObjectItemValue (objects->getReference (productRef), "path");
  119. if (path.empty())
  120. continue;
  121. results.push_back ({ String (name).unquoted(), String (path).unquoted() });
  122. }
  123. return results;
  124. }
  125. private:
  126. //==============================================================================
  127. static std::string parseObjectID (std::string& content, std::string::iterator& ptr)
  128. {
  129. auto start = ptr;
  130. while (ptr != content.end() && *ptr != ' ' && *ptr != ';' && *ptr != '=')
  131. ++ptr;
  132. return ptr == content.end() ? std::string()
  133. : content.substr ((size_t) std::distance (content.begin(), start),
  134. (size_t) std::distance (start, ptr));
  135. }
  136. //==============================================================================
  137. static std::string parseBracedContent (std::string& content, std::string::iterator& ptr)
  138. {
  139. jassert (*ptr == '{');
  140. auto start = ++ptr;
  141. auto braceDepth = 1;
  142. while (ptr++ != content.end())
  143. {
  144. switch (*ptr)
  145. {
  146. case '{':
  147. ++braceDepth;
  148. break;
  149. case '}':
  150. if (--braceDepth == 0)
  151. return content.substr ((size_t) std::distance (content.begin(), start),
  152. (size_t) std::distance (start, ptr));
  153. }
  154. }
  155. jassertfalse;
  156. return {};
  157. }
  158. //==============================================================================
  159. static std::string parseObjectItemValue (const std::string& source, const std::string& key)
  160. {
  161. std::smatch match;
  162. if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *(.*?)[ ;]+")))
  163. {
  164. jassertfalse;
  165. return {};
  166. }
  167. return match[1];
  168. }
  169. //==============================================================================
  170. static StringArray parseObjectItemList (const std::string& source, const std::string& key)
  171. {
  172. std::smatch match;
  173. if (! std::regex_search (source, match, std::regex ("[ ;{]+" + key + " *= *\\((.*?)\\)")))
  174. {
  175. jassertfalse;
  176. return {};
  177. }
  178. auto result = StringArray::fromTokens (String (match[1]), ", ", "");
  179. result.removeEmptyStrings();
  180. return result;
  181. }
  182. };