cmXCodeObject.cxx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmXCodeObject.h"
  4. #include <CoreFoundation/CoreFoundation.h>
  5. #include <ostream>
  6. #include "cmSystemTools.h"
  7. const char* cmXCodeObject::PBXTypeNames[] = {
  8. /* clang-format needs this comment to break after the opening brace */
  9. "PBXGroup",
  10. "PBXBuildStyle",
  11. "PBXProject",
  12. "PBXHeadersBuildPhase",
  13. "PBXSourcesBuildPhase",
  14. "PBXFrameworksBuildPhase",
  15. "PBXNativeTarget",
  16. "PBXFileReference",
  17. "PBXBuildFile",
  18. "PBXContainerItemProxy",
  19. "PBXTargetDependency",
  20. "PBXShellScriptBuildPhase",
  21. "PBXResourcesBuildPhase",
  22. "PBXApplicationReference",
  23. "PBXExecutableFileReference",
  24. "PBXLibraryReference",
  25. "PBXToolTarget",
  26. "PBXLibraryTarget",
  27. "PBXAggregateTarget",
  28. "XCBuildConfiguration",
  29. "XCConfigurationList",
  30. "PBXCopyFilesBuildPhase",
  31. "None"
  32. };
  33. cmXCodeObject::~cmXCodeObject()
  34. {
  35. this->Version = 15;
  36. }
  37. cmXCodeObject::cmXCodeObject(PBXType ptype, Type type)
  38. {
  39. this->Version = 15;
  40. this->Target = nullptr;
  41. this->Object = nullptr;
  42. this->IsA = ptype;
  43. if (type == OBJECT) {
  44. // Set the Id of an Xcode object to a unique string for each instance.
  45. // However the Xcode user file references certain Ids: for those cases,
  46. // override the generated Id using SetId().
  47. //
  48. char cUuid[40] = { 0 };
  49. CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
  50. CFStringRef s = CFUUIDCreateString(kCFAllocatorDefault, uuid);
  51. CFStringGetCString(s, cUuid, sizeof(cUuid), kCFStringEncodingUTF8);
  52. this->Id = cUuid;
  53. CFRelease(s);
  54. CFRelease(uuid);
  55. } else {
  56. this->Id =
  57. "Temporary cmake object, should not be referred to in Xcode file";
  58. }
  59. cmSystemTools::ReplaceString(this->Id, "-", "");
  60. if (this->Id.size() > 24) {
  61. this->Id = this->Id.substr(0, 24);
  62. }
  63. this->TypeValue = type;
  64. if (this->TypeValue == OBJECT) {
  65. this->AddAttribute("isa", nullptr);
  66. }
  67. }
  68. bool cmXCodeObject::IsEmpty() const
  69. {
  70. switch (this->TypeValue) {
  71. case OBJECT_LIST:
  72. return this->List.empty();
  73. case STRING:
  74. return this->String.empty();
  75. case ATTRIBUTE_GROUP:
  76. return this->ObjectAttributes.empty();
  77. case OBJECT_REF:
  78. case OBJECT:
  79. return this->Object == nullptr;
  80. }
  81. return true; // unreachable, but quiets warnings
  82. }
  83. void cmXCodeObject::Indent(int level, std::ostream& out)
  84. {
  85. while (level) {
  86. out << "\t";
  87. level--;
  88. }
  89. }
  90. void cmXCodeObject::Print(std::ostream& out)
  91. {
  92. std::string separator = "\n";
  93. int indentFactor = 1;
  94. cmXCodeObject::Indent(2 * indentFactor, out);
  95. if (this->Version > 15 &&
  96. (this->IsA == PBXFileReference || this->IsA == PBXBuildFile)) {
  97. separator = " ";
  98. indentFactor = 0;
  99. }
  100. out << this->Id;
  101. this->PrintComment(out);
  102. out << " = {";
  103. if (separator == "\n") {
  104. out << separator;
  105. }
  106. cmXCodeObject::Indent(3 * indentFactor, out);
  107. out << "isa = " << PBXTypeNames[this->IsA] << ";" << separator;
  108. for (const auto& keyVal : this->ObjectAttributes) {
  109. if (keyVal.first == "isa") {
  110. continue;
  111. }
  112. PrintAttribute(out, 3, separator, indentFactor, keyVal.first,
  113. keyVal.second, this);
  114. }
  115. cmXCodeObject::Indent(2 * indentFactor, out);
  116. out << "};\n";
  117. }
  118. void cmXCodeObject::PrintAttribute(std::ostream& out, int level,
  119. const std::string& separator, int factor,
  120. const std::string& name,
  121. const cmXCodeObject* object,
  122. const cmXCodeObject* parent)
  123. {
  124. cmXCodeObject::Indent(level * factor, out);
  125. switch (object->TypeValue) {
  126. case OBJECT_LIST: {
  127. out << name << " = (";
  128. if (parent->TypeValue != ATTRIBUTE_GROUP) {
  129. out << separator;
  130. }
  131. for (unsigned int i = 0; i < object->List.size(); ++i) {
  132. if (object->List[i]->TypeValue == STRING) {
  133. object->List[i]->PrintString(out);
  134. if (i + 1 < object->List.size()) {
  135. out << ",";
  136. }
  137. } else {
  138. cmXCodeObject::Indent((level + 1) * factor, out);
  139. out << object->List[i]->Id;
  140. object->List[i]->PrintComment(out);
  141. out << "," << separator;
  142. }
  143. }
  144. if (parent->TypeValue != ATTRIBUTE_GROUP) {
  145. cmXCodeObject::Indent(level * factor, out);
  146. }
  147. out << ");" << separator;
  148. } break;
  149. case ATTRIBUTE_GROUP: {
  150. out << name << " = {";
  151. if (separator == "\n") {
  152. out << separator;
  153. }
  154. for (const auto& keyVal : object->ObjectAttributes) {
  155. PrintAttribute(out, (level + 1) * factor, separator, factor,
  156. keyVal.first, keyVal.second, object);
  157. }
  158. cmXCodeObject::Indent(level * factor, out);
  159. out << "};" << separator;
  160. } break;
  161. case OBJECT_REF: {
  162. cmXCodeObject::PrintString(out, name);
  163. out << " = " << object->Object->Id;
  164. if (object->Object->HasComment() && name != "remoteGlobalIDString") {
  165. object->Object->PrintComment(out);
  166. }
  167. out << ";" << separator;
  168. } break;
  169. case STRING: {
  170. cmXCodeObject::PrintString(out, name);
  171. out << " = ";
  172. object->PrintString(out);
  173. out << ";" << separator;
  174. } break;
  175. default: {
  176. break;
  177. }
  178. }
  179. }
  180. void cmXCodeObject::PrintList(std::vector<cmXCodeObject*> const& objs,
  181. std::ostream& out)
  182. {
  183. cmXCodeObject::Indent(1, out);
  184. out << "objects = {\n";
  185. for (auto obj : objs) {
  186. if (obj->TypeValue == OBJECT) {
  187. obj->Print(out);
  188. }
  189. }
  190. cmXCodeObject::Indent(1, out);
  191. out << "};\n";
  192. }
  193. void cmXCodeObject::CopyAttributes(cmXCodeObject* copy)
  194. {
  195. this->ObjectAttributes = copy->ObjectAttributes;
  196. this->List = copy->List;
  197. this->String = copy->String;
  198. this->Object = copy->Object;
  199. }
  200. void cmXCodeObject::PrintString(std::ostream& os, const std::string& String)
  201. {
  202. // The string needs to be quoted if it contains any characters
  203. // considered special by the Xcode project file parser.
  204. bool needQuote = (String.empty() || String.find("//") != std::string::npos ||
  205. String.find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  206. "abcdefghijklmnopqrstuvwxyz"
  207. "0123456789"
  208. "$_./") != std::string::npos);
  209. const char* quote = needQuote ? "\"" : "";
  210. // Print the string, quoted and escaped as necessary.
  211. os << quote;
  212. for (auto c : String) {
  213. if (c == '"' || c == '\\') {
  214. // Escape double-quotes and backslashes.
  215. os << '\\';
  216. }
  217. os << c;
  218. }
  219. os << quote;
  220. }
  221. void cmXCodeObject::PrintString(std::ostream& os) const
  222. {
  223. cmXCodeObject::PrintString(os, this->String);
  224. }
  225. void cmXCodeObject::SetString(const std::string& s)
  226. {
  227. this->String = s;
  228. }