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.

241 lines
7.3KB

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