lounge_lizard 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (348) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.gitmodules +3 -0
  4. data/.rspec +2 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +33 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +50 -0
  10. data/Rakefile +43 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/circle.yml +14 -0
  14. data/ext/drafter/CHANGELOG.md +278 -0
  15. data/ext/drafter/Dockerfile +17 -0
  16. data/ext/drafter/Makefile +62 -0
  17. data/ext/drafter/appveyor.yml +17 -0
  18. data/ext/drafter/bin/drafter +0 -0
  19. data/ext/drafter/build/Makefile +387 -0
  20. data/ext/drafter/build/drafter.Makefile +6 -0
  21. data/ext/drafter/build/drafter.target.mk +159 -0
  22. data/ext/drafter/build/ext/snowcrash/libmarkdownparser.target.mk +141 -0
  23. data/ext/drafter/build/ext/snowcrash/libsnowcrash.target.mk +154 -0
  24. data/ext/drafter/build/ext/snowcrash/libsundown.target.mk +149 -0
  25. data/ext/drafter/build/ext/snowcrash/perf-libsnowcrash.target.mk +147 -0
  26. data/ext/drafter/build/ext/snowcrash/snowcrash.Makefile +6 -0
  27. data/ext/drafter/build/gyp-mac-tool +606 -0
  28. data/ext/drafter/build/libdrafter.target.mk +186 -0
  29. data/ext/drafter/build/libsos.target.mk +137 -0
  30. data/ext/drafter/build/out/Release/drafter +0 -0
  31. data/ext/drafter/build/out/Release/libdrafter.dylib +0 -0
  32. data/ext/drafter/build/out/Release/libmarkdownparser.a +0 -0
  33. data/ext/drafter/build/out/Release/libsnowcrash.a +0 -0
  34. data/ext/drafter/build/out/Release/libsos.a +0 -0
  35. data/ext/drafter/build/out/Release/libsundown.a +0 -0
  36. data/ext/drafter/build/out/Release/obj.target/drafter/src/config.o +0 -0
  37. data/ext/drafter/build/out/Release/obj.target/drafter/src/main.o +0 -0
  38. data/ext/drafter/build/out/Release/obj.target/drafter/src/reporting.o +0 -0
  39. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/ConversionContext.o +0 -0
  40. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/NamedTypesRegistry.o +0 -0
  41. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/RefractAPI.o +0 -0
  42. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/RefractDataStructure.o +0 -0
  43. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/RefractElementFactory.o +0 -0
  44. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/RefractSourceMap.o +0 -0
  45. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/Render.o +0 -0
  46. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/Serialize.o +0 -0
  47. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/SerializeAST.o +0 -0
  48. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/SerializeResult.o +0 -0
  49. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/SerializeSourcemap.o +0 -0
  50. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/drafter.o +0 -0
  51. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/drafter_private.o +0 -0
  52. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/ComparableVisitor.o +0 -0
  53. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/Element.o +0 -0
  54. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/ExpandVisitor.o +0 -0
  55. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/IsExpandableVisitor.o +0 -0
  56. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/JSONSchemaVisitor.o +0 -0
  57. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/PrintVisitor.o +0 -0
  58. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/Query.o +0 -0
  59. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/Registry.o +0 -0
  60. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/RenderJSONVisitor.o +0 -0
  61. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/SerializeCompactVisitor.o +0 -0
  62. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/SerializeVisitor.o +0 -0
  63. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/TypeQueryVisitor.o +0 -0
  64. data/ext/drafter/build/out/Release/obj.target/libdrafter/src/refract/VisitorUtils.o +0 -0
  65. data/ext/drafter/build/out/Release/obj.target/libmarkdownparser/ext/snowcrash/ext/markdown-parser/src/ByteBuffer.o +0 -0
  66. data/ext/drafter/build/out/Release/obj.target/libmarkdownparser/ext/snowcrash/ext/markdown-parser/src/MarkdownNode.o +0 -0
  67. data/ext/drafter/build/out/Release/obj.target/libmarkdownparser/ext/snowcrash/ext/markdown-parser/src/MarkdownParser.o +0 -0
  68. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/Blueprint.o +0 -0
  69. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/BlueprintSourcemap.o +0 -0
  70. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/HTTP.o +0 -0
  71. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/HeadersParser.o +0 -0
  72. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/MSON.o +0 -0
  73. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/MSONOneOfParser.o +0 -0
  74. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/MSONSourcemap.o +0 -0
  75. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/MSONTypeSectionParser.o +0 -0
  76. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/MSONValueMemberParser.o +0 -0
  77. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/Section.o +0 -0
  78. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/Signature.o +0 -0
  79. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/UriTemplateParser.o +0 -0
  80. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/posix/RegexMatch.o +0 -0
  81. data/ext/drafter/build/out/Release/obj.target/libsnowcrash/ext/snowcrash/src/snowcrash.o +0 -0
  82. data/ext/drafter/build/out/Release/obj.target/libsos/ext/sos/src/sos.o +0 -0
  83. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/html/houdini_href_e.o +0 -0
  84. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/html/houdini_html_e.o +0 -0
  85. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/html/html.o +0 -0
  86. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/html/html_smartypants.o +0 -0
  87. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/src/autolink.o +0 -0
  88. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/src/buffer.o +0 -0
  89. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/src/markdown.o +0 -0
  90. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/src/src_map.o +0 -0
  91. data/ext/drafter/build/out/Release/obj.target/libsundown/ext/snowcrash/ext/markdown-parser/ext/sundown/src/stack.o +0 -0
  92. data/ext/drafter/circle.yml +32 -0
  93. data/ext/drafter/config.gypi +10 -0
  94. data/ext/drafter/config.mk +5 -0
  95. data/ext/drafter/configure +224 -0
  96. data/ext/drafter/drafter.gyp +189 -0
  97. data/ext/drafter/drafter.xcworkspace/contents.xcworkspacedata +13 -0
  98. data/ext/drafter/ext/snowcrash/Makefile +58 -0
  99. data/ext/drafter/ext/snowcrash/appveyor.yml +7 -0
  100. data/ext/drafter/ext/snowcrash/common.gypi +165 -0
  101. data/ext/drafter/ext/snowcrash/configure +197 -0
  102. data/ext/drafter/ext/snowcrash/ext/markdown-parser/Makefile +90 -0
  103. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/CONTRIBUTING.md +10 -0
  104. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/Makefile +84 -0
  105. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/Makefile.win +33 -0
  106. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/examples/smartypants.c +72 -0
  107. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/examples/sundown.c +80 -0
  108. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/houdini.h +37 -0
  109. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/houdini_href_e.c +108 -0
  110. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/houdini_html_e.c +84 -0
  111. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/html.c +647 -0
  112. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/html.h +77 -0
  113. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html/html_smartypants.c +389 -0
  114. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/html_block_names.txt +25 -0
  115. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/autolink.c +297 -0
  116. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/autolink.h +51 -0
  117. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/buffer.c +225 -0
  118. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/buffer.h +96 -0
  119. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/html_blocks.h +206 -0
  120. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/markdown.c +2726 -0
  121. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/markdown.h +147 -0
  122. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/src_map.c +204 -0
  123. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/src_map.h +58 -0
  124. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/stack.c +81 -0
  125. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/src/stack.h +29 -0
  126. data/ext/drafter/ext/snowcrash/ext/markdown-parser/ext/sundown/sundown.def +20 -0
  127. data/ext/drafter/ext/snowcrash/ext/markdown-parser/msvc/markdown/markdown.vcproj +188 -0
  128. data/ext/drafter/ext/snowcrash/ext/markdown-parser/msvc/msvc.sln +38 -0
  129. data/ext/drafter/ext/snowcrash/ext/markdown-parser/msvc/sundown/sundown.vcproj +206 -0
  130. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/ByteBuffer.cc +160 -0
  131. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/ByteBuffer.h +90 -0
  132. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/MarkdownNode.cc +152 -0
  133. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/MarkdownNode.h +103 -0
  134. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/MarkdownParser.cc +388 -0
  135. data/ext/drafter/ext/snowcrash/ext/markdown-parser/src/MarkdownParser.h +106 -0
  136. data/ext/drafter/ext/snowcrash/snowcrash.gyp +196 -0
  137. data/ext/drafter/ext/snowcrash/src/ActionParser.h +560 -0
  138. data/ext/drafter/ext/snowcrash/src/AssetParser.h +123 -0
  139. data/ext/drafter/ext/snowcrash/src/AttributesParser.h +123 -0
  140. data/ext/drafter/ext/snowcrash/src/Blueprint.cc +90 -0
  141. data/ext/drafter/ext/snowcrash/src/Blueprint.h +489 -0
  142. data/ext/drafter/ext/snowcrash/src/BlueprintParser.h +845 -0
  143. data/ext/drafter/ext/snowcrash/src/BlueprintSourcemap.cc +81 -0
  144. data/ext/drafter/ext/snowcrash/src/BlueprintSourcemap.h +345 -0
  145. data/ext/drafter/ext/snowcrash/src/BlueprintUtility.h +111 -0
  146. data/ext/drafter/ext/snowcrash/src/CodeBlockUtility.h +276 -0
  147. data/ext/drafter/ext/snowcrash/src/DataStructureGroupParser.h +157 -0
  148. data/ext/drafter/ext/snowcrash/src/HTTP.cc +49 -0
  149. data/ext/drafter/ext/snowcrash/src/HTTP.h +108 -0
  150. data/ext/drafter/ext/snowcrash/src/HeadersParser.cc +117 -0
  151. data/ext/drafter/ext/snowcrash/src/HeadersParser.h +377 -0
  152. data/ext/drafter/ext/snowcrash/src/MSON.cc +272 -0
  153. data/ext/drafter/ext/snowcrash/src/MSON.h +405 -0
  154. data/ext/drafter/ext/snowcrash/src/MSONMixinParser.h +103 -0
  155. data/ext/drafter/ext/snowcrash/src/MSONNamedTypeParser.h +135 -0
  156. data/ext/drafter/ext/snowcrash/src/MSONOneOfParser.cc +132 -0
  157. data/ext/drafter/ext/snowcrash/src/MSONOneOfParser.h +80 -0
  158. data/ext/drafter/ext/snowcrash/src/MSONParameterParser.h +166 -0
  159. data/ext/drafter/ext/snowcrash/src/MSONPropertyMemberParser.h +106 -0
  160. data/ext/drafter/ext/snowcrash/src/MSONSourcemap.cc +141 -0
  161. data/ext/drafter/ext/snowcrash/src/MSONSourcemap.h +181 -0
  162. data/ext/drafter/ext/snowcrash/src/MSONTypeSectionParser.cc +209 -0
  163. data/ext/drafter/ext/snowcrash/src/MSONTypeSectionParser.h +213 -0
  164. data/ext/drafter/ext/snowcrash/src/MSONUtility.h +506 -0
  165. data/ext/drafter/ext/snowcrash/src/MSONValueMemberParser.cc +214 -0
  166. data/ext/drafter/ext/snowcrash/src/MSONValueMemberParser.h +390 -0
  167. data/ext/drafter/ext/snowcrash/src/ModelTable.h +87 -0
  168. data/ext/drafter/ext/snowcrash/src/ParameterParser.h +516 -0
  169. data/ext/drafter/ext/snowcrash/src/ParametersParser.h +222 -0
  170. data/ext/drafter/ext/snowcrash/src/PayloadParser.h +733 -0
  171. data/ext/drafter/ext/snowcrash/src/Platform.h +33 -0
  172. data/ext/drafter/ext/snowcrash/src/RegexMatch.h +32 -0
  173. data/ext/drafter/ext/snowcrash/src/RelationParser.h +87 -0
  174. data/ext/drafter/ext/snowcrash/src/ResourceGroupParser.h +297 -0
  175. data/ext/drafter/ext/snowcrash/src/ResourceParser.h +536 -0
  176. data/ext/drafter/ext/snowcrash/src/Section.cc +48 -0
  177. data/ext/drafter/ext/snowcrash/src/Section.h +60 -0
  178. data/ext/drafter/ext/snowcrash/src/SectionParser.h +246 -0
  179. data/ext/drafter/ext/snowcrash/src/SectionParserData.h +109 -0
  180. data/ext/drafter/ext/snowcrash/src/SectionProcessor.h +299 -0
  181. data/ext/drafter/ext/snowcrash/src/Signature.cc +75 -0
  182. data/ext/drafter/ext/snowcrash/src/Signature.h +103 -0
  183. data/ext/drafter/ext/snowcrash/src/SignatureSectionProcessor.h +442 -0
  184. data/ext/drafter/ext/snowcrash/src/SourceAnnotation.h +166 -0
  185. data/ext/drafter/ext/snowcrash/src/StringUtility.h +323 -0
  186. data/ext/drafter/ext/snowcrash/src/UriTemplateParser.cc +195 -0
  187. data/ext/drafter/ext/snowcrash/src/UriTemplateParser.h +240 -0
  188. data/ext/drafter/ext/snowcrash/src/ValuesParser.h +111 -0
  189. data/ext/drafter/ext/snowcrash/src/posix/RegexMatch.cc +99 -0
  190. data/ext/drafter/ext/snowcrash/src/snowcrash.cc +90 -0
  191. data/ext/drafter/ext/snowcrash/src/snowcrash.h +44 -0
  192. data/ext/drafter/ext/snowcrash/src/win/RegexMatch.cc +78 -0
  193. data/ext/drafter/ext/snowcrash/tools/gyp/AUTHORS +12 -0
  194. data/ext/drafter/ext/snowcrash/tools/gyp/DEPS +23 -0
  195. data/ext/drafter/ext/snowcrash/tools/gyp/OWNERS +1 -0
  196. data/ext/drafter/ext/snowcrash/tools/gyp/PRESUBMIT.py +137 -0
  197. data/ext/drafter/ext/snowcrash/tools/gyp/buildbot/buildbot_run.py +136 -0
  198. data/ext/drafter/ext/snowcrash/tools/gyp/buildbot/commit_queue/OWNERS +6 -0
  199. data/ext/drafter/ext/snowcrash/tools/gyp/buildbot/commit_queue/cq_config.json +15 -0
  200. data/ext/drafter/ext/snowcrash/tools/gyp/codereview.settings +10 -0
  201. data/ext/drafter/ext/snowcrash/tools/gyp/data/win/large-pdb-shim.cc +12 -0
  202. data/ext/drafter/ext/snowcrash/tools/gyp/gyp +8 -0
  203. data/ext/drafter/ext/snowcrash/tools/gyp/gyp.bat +5 -0
  204. data/ext/drafter/ext/snowcrash/tools/gyp/gyp_main.py +16 -0
  205. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSNew.py +340 -0
  206. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSProject.py +208 -0
  207. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSSettings.py +1096 -0
  208. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSToolFile.py +58 -0
  209. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUserFile.py +147 -0
  210. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.py +270 -0
  211. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.pyc +0 -0
  212. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.py +445 -0
  213. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.pyc +0 -0
  214. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/__init__.py +548 -0
  215. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/__init__.pyc +0 -0
  216. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/common.py +608 -0
  217. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/common.pyc +0 -0
  218. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/easy_xml.py +157 -0
  219. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/flock_tool.py +54 -0
  220. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.py +0 -0
  221. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.pyc +0 -0
  222. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/analyzer.py +741 -0
  223. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/android.py +1069 -0
  224. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/cmake.py +1248 -0
  225. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +99 -0
  226. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/eclipse.py +425 -0
  227. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypd.py +94 -0
  228. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypsh.py +56 -0
  229. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.py +2218 -0
  230. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.pyc +0 -0
  231. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/msvs.py +3467 -0
  232. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.py +2427 -0
  233. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.pyc +0 -0
  234. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.py +1300 -0
  235. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.pyc +0 -0
  236. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/input.py +2899 -0
  237. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/input.pyc +0 -0
  238. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/mac_tool.py +605 -0
  239. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.py +1093 -0
  240. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.pyc +0 -0
  241. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.py +160 -0
  242. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.pyc +0 -0
  243. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/ordered_dict.py +289 -0
  244. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/simple_copy.py +46 -0
  245. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/simple_copy.pyc +0 -0
  246. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/win_tool.py +314 -0
  247. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.py +1664 -0
  248. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.pyc +0 -0
  249. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcode_ninja.py +276 -0
  250. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcode_ninja.pyc +0 -0
  251. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.py +2927 -0
  252. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.pyc +0 -0
  253. data/ext/drafter/ext/snowcrash/tools/gyp/pylib/gyp/xml_fix.py +69 -0
  254. data/ext/drafter/ext/snowcrash/tools/gyp/pylintrc +307 -0
  255. data/ext/drafter/ext/snowcrash/tools/gyp/samples/samples +81 -0
  256. data/ext/drafter/ext/snowcrash/tools/gyp/samples/samples.bat +5 -0
  257. data/ext/drafter/ext/snowcrash/tools/gyp/setup.py +19 -0
  258. data/ext/drafter/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec +27 -0
  259. data/ext/drafter/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.xclangspec +226 -0
  260. data/ext/drafter/ext/snowcrash/tools/gyp/tools/emacs/gyp.el +275 -0
  261. data/ext/drafter/ext/snowcrash/tools/gyp/tools/graphviz.py +100 -0
  262. data/ext/drafter/ext/snowcrash/tools/gyp/tools/pretty_gyp.py +155 -0
  263. data/ext/drafter/ext/snowcrash/tools/gyp/tools/pretty_sln.py +169 -0
  264. data/ext/drafter/ext/snowcrash/tools/gyp/tools/pretty_vcproj.py +329 -0
  265. data/ext/drafter/ext/snowcrash/vcbuild.bat +139 -0
  266. data/ext/drafter/ext/sos/Makefile +62 -0
  267. data/ext/drafter/ext/sos/src/sos.cc +235 -0
  268. data/ext/drafter/ext/sos/src/sos.h +188 -0
  269. data/ext/drafter/ext/sos/src/sosJSON.h +121 -0
  270. data/ext/drafter/ext/sos/src/sosYAML.h +105 -0
  271. data/ext/drafter/src/ConversionContext.cc +39 -0
  272. data/ext/drafter/src/ConversionContext.h +34 -0
  273. data/ext/drafter/src/NamedTypesRegistry.cc +405 -0
  274. data/ext/drafter/src/NamedTypesRegistry.h +28 -0
  275. data/ext/drafter/src/NodeInfo.h +143 -0
  276. data/ext/drafter/src/RefractAPI.cc +579 -0
  277. data/ext/drafter/src/RefractAPI.h +28 -0
  278. data/ext/drafter/src/RefractDataStructure.cc +1199 -0
  279. data/ext/drafter/src/RefractDataStructure.h +26 -0
  280. data/ext/drafter/src/RefractElementFactory.cc +107 -0
  281. data/ext/drafter/src/RefractElementFactory.h +67 -0
  282. data/ext/drafter/src/RefractSourceMap.cc +29 -0
  283. data/ext/drafter/src/RefractSourceMap.h +57 -0
  284. data/ext/drafter/src/Render.cc +157 -0
  285. data/ext/drafter/src/Render.h +40 -0
  286. data/ext/drafter/src/Serialize.cc +160 -0
  287. data/ext/drafter/src/Serialize.h +289 -0
  288. data/ext/drafter/src/SerializeAST.cc +507 -0
  289. data/ext/drafter/src/SerializeAST.h +29 -0
  290. data/ext/drafter/src/SerializeResult.cc +170 -0
  291. data/ext/drafter/src/SerializeResult.h +34 -0
  292. data/ext/drafter/src/SerializeSourcemap.cc +331 -0
  293. data/ext/drafter/src/SerializeSourcemap.h +21 -0
  294. data/ext/drafter/src/Version.h +40 -0
  295. data/ext/drafter/src/config.cc +91 -0
  296. data/ext/drafter/src/config.h +38 -0
  297. data/ext/drafter/src/drafter.cc +137 -0
  298. data/ext/drafter/src/drafter.h +102 -0
  299. data/ext/drafter/src/drafter_private.cc +85 -0
  300. data/ext/drafter/src/drafter_private.h +34 -0
  301. data/ext/drafter/src/main.cc +137 -0
  302. data/ext/drafter/src/refract/AppendDecorator.h +58 -0
  303. data/ext/drafter/src/refract/Build.h +67 -0
  304. data/ext/drafter/src/refract/ComparableVisitor.cc +43 -0
  305. data/ext/drafter/src/refract/ComparableVisitor.h +62 -0
  306. data/ext/drafter/src/refract/Element.cc +409 -0
  307. data/ext/drafter/src/refract/Element.h +656 -0
  308. data/ext/drafter/src/refract/ElementFwd.h +37 -0
  309. data/ext/drafter/src/refract/ElementInserter.h +59 -0
  310. data/ext/drafter/src/refract/Exception.h +31 -0
  311. data/ext/drafter/src/refract/ExpandVisitor.cc +359 -0
  312. data/ext/drafter/src/refract/ExpandVisitor.h +58 -0
  313. data/ext/drafter/src/refract/FilterVisitor.h +52 -0
  314. data/ext/drafter/src/refract/IsExpandableVisitor.cc +140 -0
  315. data/ext/drafter/src/refract/IsExpandableVisitor.h +31 -0
  316. data/ext/drafter/src/refract/Iterate.h +160 -0
  317. data/ext/drafter/src/refract/JSONSchemaVisitor.cc +675 -0
  318. data/ext/drafter/src/refract/JSONSchemaVisitor.h +73 -0
  319. data/ext/drafter/src/refract/PrintVisitor.cc +164 -0
  320. data/ext/drafter/src/refract/PrintVisitor.h +50 -0
  321. data/ext/drafter/src/refract/Query.cc +13 -0
  322. data/ext/drafter/src/refract/Query.h +38 -0
  323. data/ext/drafter/src/refract/Registry.cc +114 -0
  324. data/ext/drafter/src/refract/Registry.h +43 -0
  325. data/ext/drafter/src/refract/RenderJSONVisitor.cc +255 -0
  326. data/ext/drafter/src/refract/RenderJSONVisitor.h +51 -0
  327. data/ext/drafter/src/refract/SerializeCompactVisitor.cc +167 -0
  328. data/ext/drafter/src/refract/SerializeCompactVisitor.h +56 -0
  329. data/ext/drafter/src/refract/SerializeVisitor.cc +214 -0
  330. data/ext/drafter/src/refract/SerializeVisitor.h +55 -0
  331. data/ext/drafter/src/refract/TypeQueryVisitor.cc +46 -0
  332. data/ext/drafter/src/refract/TypeQueryVisitor.h +110 -0
  333. data/ext/drafter/src/refract/Visitor.h +126 -0
  334. data/ext/drafter/src/refract/VisitorUtils.cc +63 -0
  335. data/ext/drafter/src/refract/VisitorUtils.h +231 -0
  336. data/ext/drafter/src/reporting.cc +263 -0
  337. data/ext/drafter/src/reporting.h +39 -0
  338. data/ext/drafter/src/stream.h +148 -0
  339. data/ext/drafter/tools/homebrew/drafter.rb +18 -0
  340. data/ext/drafter/tools/make-tarball.sh +39 -0
  341. data/ext/drafter/tools/refract-filter.py +96 -0
  342. data/ext/drafter/tools/release.sh +17 -0
  343. data/ext/drafter/vcbuild.bat +203 -0
  344. data/lib/lounge_lizard/binding.rb +29 -0
  345. data/lib/lounge_lizard/version.rb +3 -0
  346. data/lib/lounge_lizard.rb +18 -0
  347. data/lounge_lizard.gemspec +37 -0
  348. metadata +547 -0
@@ -0,0 +1,2927 @@
1
+ # Copyright (c) 2012 Google Inc. All rights reserved.
2
+ # Use of this source code is governed by a BSD-style license that can be
3
+ # found in the LICENSE file.
4
+
5
+ """Xcode project file generator.
6
+
7
+ This module is both an Xcode project file generator and a documentation of the
8
+ Xcode project file format. Knowledge of the project file format was gained
9
+ based on extensive experience with Xcode, and by making changes to projects in
10
+ Xcode.app and observing the resultant changes in the associated project files.
11
+
12
+ XCODE PROJECT FILES
13
+
14
+ The generator targets the file format as written by Xcode 3.2 (specifically,
15
+ 3.2.6), but past experience has taught that the format has not changed
16
+ significantly in the past several years, and future versions of Xcode are able
17
+ to read older project files.
18
+
19
+ Xcode project files are "bundled": the project "file" from an end-user's
20
+ perspective is actually a directory with an ".xcodeproj" extension. The
21
+ project file from this module's perspective is actually a file inside this
22
+ directory, always named "project.pbxproj". This file contains a complete
23
+ description of the project and is all that is needed to use the xcodeproj.
24
+ Other files contained in the xcodeproj directory are simply used to store
25
+ per-user settings, such as the state of various UI elements in the Xcode
26
+ application.
27
+
28
+ The project.pbxproj file is a property list, stored in a format almost
29
+ identical to the NeXTstep property list format. The file is able to carry
30
+ Unicode data, and is encoded in UTF-8. The root element in the property list
31
+ is a dictionary that contains several properties of minimal interest, and two
32
+ properties of immense interest. The most important property is a dictionary
33
+ named "objects". The entire structure of the project is represented by the
34
+ children of this property. The objects dictionary is keyed by unique 96-bit
35
+ values represented by 24 uppercase hexadecimal characters. Each value in the
36
+ objects dictionary is itself a dictionary, describing an individual object.
37
+
38
+ Each object in the dictionary is a member of a class, which is identified by
39
+ the "isa" property of each object. A variety of classes are represented in a
40
+ project file. Objects can refer to other objects by ID, using the 24-character
41
+ hexadecimal object key. A project's objects form a tree, with a root object
42
+ of class PBXProject at the root. As an example, the PBXProject object serves
43
+ as parent to an XCConfigurationList object defining the build configurations
44
+ used in the project, a PBXGroup object serving as a container for all files
45
+ referenced in the project, and a list of target objects, each of which defines
46
+ a target in the project. There are several different types of target object,
47
+ such as PBXNativeTarget and PBXAggregateTarget. In this module, this
48
+ relationship is expressed by having each target type derive from an abstract
49
+ base named XCTarget.
50
+
51
+ The project.pbxproj file's root dictionary also contains a property, sibling to
52
+ the "objects" dictionary, named "rootObject". The value of rootObject is a
53
+ 24-character object key referring to the root PBXProject object in the
54
+ objects dictionary.
55
+
56
+ In Xcode, every file used as input to a target or produced as a final product
57
+ of a target must appear somewhere in the hierarchy rooted at the PBXGroup
58
+ object referenced by the PBXProject's mainGroup property. A PBXGroup is
59
+ generally represented as a folder in the Xcode application. PBXGroups can
60
+ contain other PBXGroups as well as PBXFileReferences, which are pointers to
61
+ actual files.
62
+
63
+ Each XCTarget contains a list of build phases, represented in this module by
64
+ the abstract base XCBuildPhase. Examples of concrete XCBuildPhase derivations
65
+ are PBXSourcesBuildPhase and PBXFrameworksBuildPhase, which correspond to the
66
+ "Compile Sources" and "Link Binary With Libraries" phases displayed in the
67
+ Xcode application. Files used as input to these phases (for example, source
68
+ files in the former case and libraries and frameworks in the latter) are
69
+ represented by PBXBuildFile objects, referenced by elements of "files" lists
70
+ in XCTarget objects. Each PBXBuildFile object refers to a PBXBuildFile
71
+ object as a "weak" reference: it does not "own" the PBXBuildFile, which is
72
+ owned by the root object's mainGroup or a descendant group. In most cases, the
73
+ layer of indirection between an XCBuildPhase and a PBXFileReference via a
74
+ PBXBuildFile appears extraneous, but there's actually one reason for this:
75
+ file-specific compiler flags are added to the PBXBuildFile object so as to
76
+ allow a single file to be a member of multiple targets while having distinct
77
+ compiler flags for each. These flags can be modified in the Xcode applciation
78
+ in the "Build" tab of a File Info window.
79
+
80
+ When a project is open in the Xcode application, Xcode will rewrite it. As
81
+ such, this module is careful to adhere to the formatting used by Xcode, to
82
+ avoid insignificant changes appearing in the file when it is used in the
83
+ Xcode application. This will keep version control repositories happy, and
84
+ makes it possible to compare a project file used in Xcode to one generated by
85
+ this module to determine if any significant changes were made in the
86
+ application.
87
+
88
+ Xcode has its own way of assigning 24-character identifiers to each object,
89
+ which is not duplicated here. Because the identifier only is only generated
90
+ once, when an object is created, and is then left unchanged, there is no need
91
+ to attempt to duplicate Xcode's behavior in this area. The generator is free
92
+ to select any identifier, even at random, to refer to the objects it creates,
93
+ and Xcode will retain those identifiers and use them when subsequently
94
+ rewriting the project file. However, the generator would choose new random
95
+ identifiers each time the project files are generated, leading to difficulties
96
+ comparing "used" project files to "pristine" ones produced by this module,
97
+ and causing the appearance of changes as every object identifier is changed
98
+ when updated projects are checked in to a version control repository. To
99
+ mitigate this problem, this module chooses identifiers in a more deterministic
100
+ way, by hashing a description of each object as well as its parent and ancestor
101
+ objects. This strategy should result in minimal "shift" in IDs as successive
102
+ generations of project files are produced.
103
+
104
+ THIS MODULE
105
+
106
+ This module introduces several classes, all derived from the XCObject class.
107
+ Nearly all of the "brains" are built into the XCObject class, which understands
108
+ how to create and modify objects, maintain the proper tree structure, compute
109
+ identifiers, and print objects. For the most part, classes derived from
110
+ XCObject need only provide a _schema class object, a dictionary that
111
+ expresses what properties objects of the class may contain.
112
+
113
+ Given this structure, it's possible to build a minimal project file by creating
114
+ objects of the appropriate types and making the proper connections:
115
+
116
+ config_list = XCConfigurationList()
117
+ group = PBXGroup()
118
+ project = PBXProject({'buildConfigurationList': config_list,
119
+ 'mainGroup': group})
120
+
121
+ With the project object set up, it can be added to an XCProjectFile object.
122
+ XCProjectFile is a pseudo-class in the sense that it is a concrete XCObject
123
+ subclass that does not actually correspond to a class type found in a project
124
+ file. Rather, it is used to represent the project file's root dictionary.
125
+ Printing an XCProjectFile will print the entire project file, including the
126
+ full "objects" dictionary.
127
+
128
+ project_file = XCProjectFile({'rootObject': project})
129
+ project_file.ComputeIDs()
130
+ project_file.Print()
131
+
132
+ Xcode project files are always encoded in UTF-8. This module will accept
133
+ strings of either the str class or the unicode class. Strings of class str
134
+ are assumed to already be encoded in UTF-8. Obviously, if you're just using
135
+ ASCII, you won't encounter difficulties because ASCII is a UTF-8 subset.
136
+ Strings of class unicode are handled properly and encoded in UTF-8 when
137
+ a project file is output.
138
+ """
139
+
140
+ import gyp.common
141
+ import posixpath
142
+ import re
143
+ import struct
144
+ import sys
145
+
146
+ # hashlib is supplied as of Python 2.5 as the replacement interface for sha
147
+ # and other secure hashes. In 2.6, sha is deprecated. Import hashlib if
148
+ # available, avoiding a deprecation warning under 2.6. Import sha otherwise,
149
+ # preserving 2.4 compatibility.
150
+ try:
151
+ import hashlib
152
+ _new_sha1 = hashlib.sha1
153
+ except ImportError:
154
+ import sha
155
+ _new_sha1 = sha.new
156
+
157
+
158
+ # See XCObject._EncodeString. This pattern is used to determine when a string
159
+ # can be printed unquoted. Strings that match this pattern may be printed
160
+ # unquoted. Strings that do not match must be quoted and may be further
161
+ # transformed to be properly encoded. Note that this expression matches the
162
+ # characters listed with "+", for 1 or more occurrences: if a string is empty,
163
+ # it must not match this pattern, because it needs to be encoded as "".
164
+ _unquoted = re.compile('^[A-Za-z0-9$./_]+$')
165
+
166
+ # Strings that match this pattern are quoted regardless of what _unquoted says.
167
+ # Oddly, Xcode will quote any string with a run of three or more underscores.
168
+ _quoted = re.compile('___')
169
+
170
+ # This pattern should match any character that needs to be escaped by
171
+ # XCObject._EncodeString. See that function.
172
+ _escaped = re.compile('[\\\\"]|[\x00-\x1f]')
173
+
174
+
175
+ # Used by SourceTreeAndPathFromPath
176
+ _path_leading_variable = re.compile(r'^\$\((.*?)\)(/(.*))?$')
177
+
178
+ def SourceTreeAndPathFromPath(input_path):
179
+ """Given input_path, returns a tuple with sourceTree and path values.
180
+
181
+ Examples:
182
+ input_path (source_tree, output_path)
183
+ '$(VAR)/path' ('VAR', 'path')
184
+ '$(VAR)' ('VAR', None)
185
+ 'path' (None, 'path')
186
+ """
187
+
188
+ source_group_match = _path_leading_variable.match(input_path)
189
+ if source_group_match:
190
+ source_tree = source_group_match.group(1)
191
+ output_path = source_group_match.group(3) # This may be None.
192
+ else:
193
+ source_tree = None
194
+ output_path = input_path
195
+
196
+ return (source_tree, output_path)
197
+
198
+ def ConvertVariablesToShellSyntax(input_string):
199
+ return re.sub(r'\$\((.*?)\)', '${\\1}', input_string)
200
+
201
+ class XCObject(object):
202
+ """The abstract base of all class types used in Xcode project files.
203
+
204
+ Class variables:
205
+ _schema: A dictionary defining the properties of this class. The keys to
206
+ _schema are string property keys as used in project files. Values
207
+ are a list of four or five elements:
208
+ [ is_list, property_type, is_strong, is_required, default ]
209
+ is_list: True if the property described is a list, as opposed
210
+ to a single element.
211
+ property_type: The type to use as the value of the property,
212
+ or if is_list is True, the type to use for each
213
+ element of the value's list. property_type must
214
+ be an XCObject subclass, or one of the built-in
215
+ types str, int, or dict.
216
+ is_strong: If property_type is an XCObject subclass, is_strong
217
+ is True to assert that this class "owns," or serves
218
+ as parent, to the property value (or, if is_list is
219
+ True, values). is_strong must be False if
220
+ property_type is not an XCObject subclass.
221
+ is_required: True if the property is required for the class.
222
+ Note that is_required being True does not preclude
223
+ an empty string ("", in the case of property_type
224
+ str) or list ([], in the case of is_list True) from
225
+ being set for the property.
226
+ default: Optional. If is_requried is True, default may be set
227
+ to provide a default value for objects that do not supply
228
+ their own value. If is_required is True and default
229
+ is not provided, users of the class must supply their own
230
+ value for the property.
231
+ Note that although the values of the array are expressed in
232
+ boolean terms, subclasses provide values as integers to conserve
233
+ horizontal space.
234
+ _should_print_single_line: False in XCObject. Subclasses whose objects
235
+ should be written to the project file in the
236
+ alternate single-line format, such as
237
+ PBXFileReference and PBXBuildFile, should
238
+ set this to True.
239
+ _encode_transforms: Used by _EncodeString to encode unprintable characters.
240
+ The index into this list is the ordinal of the
241
+ character to transform; each value is a string
242
+ used to represent the character in the output. XCObject
243
+ provides an _encode_transforms list suitable for most
244
+ XCObject subclasses.
245
+ _alternate_encode_transforms: Provided for subclasses that wish to use
246
+ the alternate encoding rules. Xcode seems
247
+ to use these rules when printing objects in
248
+ single-line format. Subclasses that desire
249
+ this behavior should set _encode_transforms
250
+ to _alternate_encode_transforms.
251
+ _hashables: A list of XCObject subclasses that can be hashed by ComputeIDs
252
+ to construct this object's ID. Most classes that need custom
253
+ hashing behavior should do it by overriding Hashables,
254
+ but in some cases an object's parent may wish to push a
255
+ hashable value into its child, and it can do so by appending
256
+ to _hashables.
257
+ Attributes:
258
+ id: The object's identifier, a 24-character uppercase hexadecimal string.
259
+ Usually, objects being created should not set id until the entire
260
+ project file structure is built. At that point, UpdateIDs() should
261
+ be called on the root object to assign deterministic values for id to
262
+ each object in the tree.
263
+ parent: The object's parent. This is set by a parent XCObject when a child
264
+ object is added to it.
265
+ _properties: The object's property dictionary. An object's properties are
266
+ described by its class' _schema variable.
267
+ """
268
+
269
+ _schema = {}
270
+ _should_print_single_line = False
271
+
272
+ # See _EncodeString.
273
+ _encode_transforms = []
274
+ i = 0
275
+ while i < ord(' '):
276
+ _encode_transforms.append('\\U%04x' % i)
277
+ i = i + 1
278
+ _encode_transforms[7] = '\\a'
279
+ _encode_transforms[8] = '\\b'
280
+ _encode_transforms[9] = '\\t'
281
+ _encode_transforms[10] = '\\n'
282
+ _encode_transforms[11] = '\\v'
283
+ _encode_transforms[12] = '\\f'
284
+ _encode_transforms[13] = '\\n'
285
+
286
+ _alternate_encode_transforms = list(_encode_transforms)
287
+ _alternate_encode_transforms[9] = chr(9)
288
+ _alternate_encode_transforms[10] = chr(10)
289
+ _alternate_encode_transforms[11] = chr(11)
290
+
291
+ def __init__(self, properties=None, id=None, parent=None):
292
+ self.id = id
293
+ self.parent = parent
294
+ self._properties = {}
295
+ self._hashables = []
296
+ self._SetDefaultsFromSchema()
297
+ self.UpdateProperties(properties)
298
+
299
+ def __repr__(self):
300
+ try:
301
+ name = self.Name()
302
+ except NotImplementedError:
303
+ return '<%s at 0x%x>' % (self.__class__.__name__, id(self))
304
+ return '<%s %r at 0x%x>' % (self.__class__.__name__, name, id(self))
305
+
306
+ def Copy(self):
307
+ """Make a copy of this object.
308
+
309
+ The new object will have its own copy of lists and dicts. Any XCObject
310
+ objects owned by this object (marked "strong") will be copied in the
311
+ new object, even those found in lists. If this object has any weak
312
+ references to other XCObjects, the same references are added to the new
313
+ object without making a copy.
314
+ """
315
+
316
+ that = self.__class__(id=self.id, parent=self.parent)
317
+ for key, value in self._properties.iteritems():
318
+ is_strong = self._schema[key][2]
319
+
320
+ if isinstance(value, XCObject):
321
+ if is_strong:
322
+ new_value = value.Copy()
323
+ new_value.parent = that
324
+ that._properties[key] = new_value
325
+ else:
326
+ that._properties[key] = value
327
+ elif isinstance(value, str) or isinstance(value, unicode) or \
328
+ isinstance(value, int):
329
+ that._properties[key] = value
330
+ elif isinstance(value, list):
331
+ if is_strong:
332
+ # If is_strong is True, each element is an XCObject, so it's safe to
333
+ # call Copy.
334
+ that._properties[key] = []
335
+ for item in value:
336
+ new_item = item.Copy()
337
+ new_item.parent = that
338
+ that._properties[key].append(new_item)
339
+ else:
340
+ that._properties[key] = value[:]
341
+ elif isinstance(value, dict):
342
+ # dicts are never strong.
343
+ if is_strong:
344
+ raise TypeError('Strong dict for key ' + key + ' in ' + \
345
+ self.__class__.__name__)
346
+ else:
347
+ that._properties[key] = value.copy()
348
+ else:
349
+ raise TypeError('Unexpected type ' + value.__class__.__name__ + \
350
+ ' for key ' + key + ' in ' + self.__class__.__name__)
351
+
352
+ return that
353
+
354
+ def Name(self):
355
+ """Return the name corresponding to an object.
356
+
357
+ Not all objects necessarily need to be nameable, and not all that do have
358
+ a "name" property. Override as needed.
359
+ """
360
+
361
+ # If the schema indicates that "name" is required, try to access the
362
+ # property even if it doesn't exist. This will result in a KeyError
363
+ # being raised for the property that should be present, which seems more
364
+ # appropriate than NotImplementedError in this case.
365
+ if 'name' in self._properties or \
366
+ ('name' in self._schema and self._schema['name'][3]):
367
+ return self._properties['name']
368
+
369
+ raise NotImplementedError(self.__class__.__name__ + ' must implement Name')
370
+
371
+ def Comment(self):
372
+ """Return a comment string for the object.
373
+
374
+ Most objects just use their name as the comment, but PBXProject uses
375
+ different values.
376
+
377
+ The returned comment is not escaped and does not have any comment marker
378
+ strings applied to it.
379
+ """
380
+
381
+ return self.Name()
382
+
383
+ def Hashables(self):
384
+ hashables = [self.__class__.__name__]
385
+
386
+ name = self.Name()
387
+ if name != None:
388
+ hashables.append(name)
389
+
390
+ hashables.extend(self._hashables)
391
+
392
+ return hashables
393
+
394
+ def HashablesForChild(self):
395
+ return None
396
+
397
+ def ComputeIDs(self, recursive=True, overwrite=True, seed_hash=None):
398
+ """Set "id" properties deterministically.
399
+
400
+ An object's "id" property is set based on a hash of its class type and
401
+ name, as well as the class type and name of all ancestor objects. As
402
+ such, it is only advisable to call ComputeIDs once an entire project file
403
+ tree is built.
404
+
405
+ If recursive is True, recurse into all descendant objects and update their
406
+ hashes.
407
+
408
+ If overwrite is True, any existing value set in the "id" property will be
409
+ replaced.
410
+ """
411
+
412
+ def _HashUpdate(hash, data):
413
+ """Update hash with data's length and contents.
414
+
415
+ If the hash were updated only with the value of data, it would be
416
+ possible for clowns to induce collisions by manipulating the names of
417
+ their objects. By adding the length, it's exceedingly less likely that
418
+ ID collisions will be encountered, intentionally or not.
419
+ """
420
+
421
+ hash.update(struct.pack('>i', len(data)))
422
+ hash.update(data)
423
+
424
+ if seed_hash is None:
425
+ seed_hash = _new_sha1()
426
+
427
+ hash = seed_hash.copy()
428
+
429
+ hashables = self.Hashables()
430
+ assert len(hashables) > 0
431
+ for hashable in hashables:
432
+ _HashUpdate(hash, hashable)
433
+
434
+ if recursive:
435
+ hashables_for_child = self.HashablesForChild()
436
+ if hashables_for_child is None:
437
+ child_hash = hash
438
+ else:
439
+ assert len(hashables_for_child) > 0
440
+ child_hash = seed_hash.copy()
441
+ for hashable in hashables_for_child:
442
+ _HashUpdate(child_hash, hashable)
443
+
444
+ for child in self.Children():
445
+ child.ComputeIDs(recursive, overwrite, child_hash)
446
+
447
+ if overwrite or self.id is None:
448
+ # Xcode IDs are only 96 bits (24 hex characters), but a SHA-1 digest is
449
+ # is 160 bits. Instead of throwing out 64 bits of the digest, xor them
450
+ # into the portion that gets used.
451
+ assert hash.digest_size % 4 == 0
452
+ digest_int_count = hash.digest_size / 4
453
+ digest_ints = struct.unpack('>' + 'I' * digest_int_count, hash.digest())
454
+ id_ints = [0, 0, 0]
455
+ for index in xrange(0, digest_int_count):
456
+ id_ints[index % 3] ^= digest_ints[index]
457
+ self.id = '%08X%08X%08X' % tuple(id_ints)
458
+
459
+ def EnsureNoIDCollisions(self):
460
+ """Verifies that no two objects have the same ID. Checks all descendants.
461
+ """
462
+
463
+ ids = {}
464
+ descendants = self.Descendants()
465
+ for descendant in descendants:
466
+ if descendant.id in ids:
467
+ other = ids[descendant.id]
468
+ raise KeyError(
469
+ 'Duplicate ID %s, objects "%s" and "%s" in "%s"' % \
470
+ (descendant.id, str(descendant._properties),
471
+ str(other._properties), self._properties['rootObject'].Name()))
472
+ ids[descendant.id] = descendant
473
+
474
+ def Children(self):
475
+ """Returns a list of all of this object's owned (strong) children."""
476
+
477
+ children = []
478
+ for property, attributes in self._schema.iteritems():
479
+ (is_list, property_type, is_strong) = attributes[0:3]
480
+ if is_strong and property in self._properties:
481
+ if not is_list:
482
+ children.append(self._properties[property])
483
+ else:
484
+ children.extend(self._properties[property])
485
+ return children
486
+
487
+ def Descendants(self):
488
+ """Returns a list of all of this object's descendants, including this
489
+ object.
490
+ """
491
+
492
+ children = self.Children()
493
+ descendants = [self]
494
+ for child in children:
495
+ descendants.extend(child.Descendants())
496
+ return descendants
497
+
498
+ def PBXProjectAncestor(self):
499
+ # The base case for recursion is defined at PBXProject.PBXProjectAncestor.
500
+ if self.parent:
501
+ return self.parent.PBXProjectAncestor()
502
+ return None
503
+
504
+ def _EncodeComment(self, comment):
505
+ """Encodes a comment to be placed in the project file output, mimicing
506
+ Xcode behavior.
507
+ """
508
+
509
+ # This mimics Xcode behavior by wrapping the comment in "/*" and "*/". If
510
+ # the string already contains a "*/", it is turned into "(*)/". This keeps
511
+ # the file writer from outputting something that would be treated as the
512
+ # end of a comment in the middle of something intended to be entirely a
513
+ # comment.
514
+
515
+ return '/* ' + comment.replace('*/', '(*)/') + ' */'
516
+
517
+ def _EncodeTransform(self, match):
518
+ # This function works closely with _EncodeString. It will only be called
519
+ # by re.sub with match.group(0) containing a character matched by the
520
+ # the _escaped expression.
521
+ char = match.group(0)
522
+
523
+ # Backslashes (\) and quotation marks (") are always replaced with a
524
+ # backslash-escaped version of the same. Everything else gets its
525
+ # replacement from the class' _encode_transforms array.
526
+ if char == '\\':
527
+ return '\\\\'
528
+ if char == '"':
529
+ return '\\"'
530
+ return self._encode_transforms[ord(char)]
531
+
532
+ def _EncodeString(self, value):
533
+ """Encodes a string to be placed in the project file output, mimicing
534
+ Xcode behavior.
535
+ """
536
+
537
+ # Use quotation marks when any character outside of the range A-Z, a-z, 0-9,
538
+ # $ (dollar sign), . (period), and _ (underscore) is present. Also use
539
+ # quotation marks to represent empty strings.
540
+ #
541
+ # Escape " (double-quote) and \ (backslash) by preceding them with a
542
+ # backslash.
543
+ #
544
+ # Some characters below the printable ASCII range are encoded specially:
545
+ # 7 ^G BEL is encoded as "\a"
546
+ # 8 ^H BS is encoded as "\b"
547
+ # 11 ^K VT is encoded as "\v"
548
+ # 12 ^L NP is encoded as "\f"
549
+ # 127 ^? DEL is passed through as-is without escaping
550
+ # - In PBXFileReference and PBXBuildFile objects:
551
+ # 9 ^I HT is passed through as-is without escaping
552
+ # 10 ^J NL is passed through as-is without escaping
553
+ # 13 ^M CR is passed through as-is without escaping
554
+ # - In other objects:
555
+ # 9 ^I HT is encoded as "\t"
556
+ # 10 ^J NL is encoded as "\n"
557
+ # 13 ^M CR is encoded as "\n" rendering it indistinguishable from
558
+ # 10 ^J NL
559
+ # All other characters within the ASCII control character range (0 through
560
+ # 31 inclusive) are encoded as "\U001f" referring to the Unicode code point
561
+ # in hexadecimal. For example, character 14 (^N SO) is encoded as "\U000e".
562
+ # Characters above the ASCII range are passed through to the output encoded
563
+ # as UTF-8 without any escaping. These mappings are contained in the
564
+ # class' _encode_transforms list.
565
+
566
+ if _unquoted.search(value) and not _quoted.search(value):
567
+ return value
568
+
569
+ return '"' + _escaped.sub(self._EncodeTransform, value) + '"'
570
+
571
+ def _XCPrint(self, file, tabs, line):
572
+ file.write('\t' * tabs + line)
573
+
574
+ def _XCPrintableValue(self, tabs, value, flatten_list=False):
575
+ """Returns a representation of value that may be printed in a project file,
576
+ mimicing Xcode's behavior.
577
+
578
+ _XCPrintableValue can handle str and int values, XCObjects (which are
579
+ made printable by returning their id property), and list and dict objects
580
+ composed of any of the above types. When printing a list or dict, and
581
+ _should_print_single_line is False, the tabs parameter is used to determine
582
+ how much to indent the lines corresponding to the items in the list or
583
+ dict.
584
+
585
+ If flatten_list is True, single-element lists will be transformed into
586
+ strings.
587
+ """
588
+
589
+ printable = ''
590
+ comment = None
591
+
592
+ if self._should_print_single_line:
593
+ sep = ' '
594
+ element_tabs = ''
595
+ end_tabs = ''
596
+ else:
597
+ sep = '\n'
598
+ element_tabs = '\t' * (tabs + 1)
599
+ end_tabs = '\t' * tabs
600
+
601
+ if isinstance(value, XCObject):
602
+ printable += value.id
603
+ comment = value.Comment()
604
+ elif isinstance(value, str):
605
+ printable += self._EncodeString(value)
606
+ elif isinstance(value, unicode):
607
+ printable += self._EncodeString(value.encode('utf-8'))
608
+ elif isinstance(value, int):
609
+ printable += str(value)
610
+ elif isinstance(value, list):
611
+ if flatten_list and len(value) <= 1:
612
+ if len(value) == 0:
613
+ printable += self._EncodeString('')
614
+ else:
615
+ printable += self._EncodeString(value[0])
616
+ else:
617
+ printable = '(' + sep
618
+ for item in value:
619
+ printable += element_tabs + \
620
+ self._XCPrintableValue(tabs + 1, item, flatten_list) + \
621
+ ',' + sep
622
+ printable += end_tabs + ')'
623
+ elif isinstance(value, dict):
624
+ printable = '{' + sep
625
+ for item_key, item_value in sorted(value.iteritems()):
626
+ printable += element_tabs + \
627
+ self._XCPrintableValue(tabs + 1, item_key, flatten_list) + ' = ' + \
628
+ self._XCPrintableValue(tabs + 1, item_value, flatten_list) + ';' + \
629
+ sep
630
+ printable += end_tabs + '}'
631
+ else:
632
+ raise TypeError("Can't make " + value.__class__.__name__ + ' printable')
633
+
634
+ if comment != None:
635
+ printable += ' ' + self._EncodeComment(comment)
636
+
637
+ return printable
638
+
639
+ def _XCKVPrint(self, file, tabs, key, value):
640
+ """Prints a key and value, members of an XCObject's _properties dictionary,
641
+ to file.
642
+
643
+ tabs is an int identifying the indentation level. If the class'
644
+ _should_print_single_line variable is True, tabs is ignored and the
645
+ key-value pair will be followed by a space insead of a newline.
646
+ """
647
+
648
+ if self._should_print_single_line:
649
+ printable = ''
650
+ after_kv = ' '
651
+ else:
652
+ printable = '\t' * tabs
653
+ after_kv = '\n'
654
+
655
+ # Xcode usually prints remoteGlobalIDString values in PBXContainerItemProxy
656
+ # objects without comments. Sometimes it prints them with comments, but
657
+ # the majority of the time, it doesn't. To avoid unnecessary changes to
658
+ # the project file after Xcode opens it, don't write comments for
659
+ # remoteGlobalIDString. This is a sucky hack and it would certainly be
660
+ # cleaner to extend the schema to indicate whether or not a comment should
661
+ # be printed, but since this is the only case where the problem occurs and
662
+ # Xcode itself can't seem to make up its mind, the hack will suffice.
663
+ #
664
+ # Also see PBXContainerItemProxy._schema['remoteGlobalIDString'].
665
+ if key == 'remoteGlobalIDString' and isinstance(self,
666
+ PBXContainerItemProxy):
667
+ value_to_print = value.id
668
+ else:
669
+ value_to_print = value
670
+
671
+ # PBXBuildFile's settings property is represented in the output as a dict,
672
+ # but a hack here has it represented as a string. Arrange to strip off the
673
+ # quotes so that it shows up in the output as expected.
674
+ if key == 'settings' and isinstance(self, PBXBuildFile):
675
+ strip_value_quotes = True
676
+ else:
677
+ strip_value_quotes = False
678
+
679
+ # In another one-off, let's set flatten_list on buildSettings properties
680
+ # of XCBuildConfiguration objects, because that's how Xcode treats them.
681
+ if key == 'buildSettings' and isinstance(self, XCBuildConfiguration):
682
+ flatten_list = True
683
+ else:
684
+ flatten_list = False
685
+
686
+ try:
687
+ printable_key = self._XCPrintableValue(tabs, key, flatten_list)
688
+ printable_value = self._XCPrintableValue(tabs, value_to_print,
689
+ flatten_list)
690
+ if strip_value_quotes and len(printable_value) > 1 and \
691
+ printable_value[0] == '"' and printable_value[-1] == '"':
692
+ printable_value = printable_value[1:-1]
693
+ printable += printable_key + ' = ' + printable_value + ';' + after_kv
694
+ except TypeError, e:
695
+ gyp.common.ExceptionAppend(e,
696
+ 'while printing key "%s"' % key)
697
+ raise
698
+
699
+ self._XCPrint(file, 0, printable)
700
+
701
+ def Print(self, file=sys.stdout):
702
+ """Prints a reprentation of this object to file, adhering to Xcode output
703
+ formatting.
704
+ """
705
+
706
+ self.VerifyHasRequiredProperties()
707
+
708
+ if self._should_print_single_line:
709
+ # When printing an object in a single line, Xcode doesn't put any space
710
+ # between the beginning of a dictionary (or presumably a list) and the
711
+ # first contained item, so you wind up with snippets like
712
+ # ...CDEF = {isa = PBXFileReference; fileRef = 0123...
713
+ # If it were me, I would have put a space in there after the opening
714
+ # curly, but I guess this is just another one of those inconsistencies
715
+ # between how Xcode prints PBXFileReference and PBXBuildFile objects as
716
+ # compared to other objects. Mimic Xcode's behavior here by using an
717
+ # empty string for sep.
718
+ sep = ''
719
+ end_tabs = 0
720
+ else:
721
+ sep = '\n'
722
+ end_tabs = 2
723
+
724
+ # Start the object. For example, '\t\tPBXProject = {\n'.
725
+ self._XCPrint(file, 2, self._XCPrintableValue(2, self) + ' = {' + sep)
726
+
727
+ # "isa" isn't in the _properties dictionary, it's an intrinsic property
728
+ # of the class which the object belongs to. Xcode always outputs "isa"
729
+ # as the first element of an object dictionary.
730
+ self._XCKVPrint(file, 3, 'isa', self.__class__.__name__)
731
+
732
+ # The remaining elements of an object dictionary are sorted alphabetically.
733
+ for property, value in sorted(self._properties.iteritems()):
734
+ self._XCKVPrint(file, 3, property, value)
735
+
736
+ # End the object.
737
+ self._XCPrint(file, end_tabs, '};\n')
738
+
739
+ def UpdateProperties(self, properties, do_copy=False):
740
+ """Merge the supplied properties into the _properties dictionary.
741
+
742
+ The input properties must adhere to the class schema or a KeyError or
743
+ TypeError exception will be raised. If adding an object of an XCObject
744
+ subclass and the schema indicates a strong relationship, the object's
745
+ parent will be set to this object.
746
+
747
+ If do_copy is True, then lists, dicts, strong-owned XCObjects, and
748
+ strong-owned XCObjects in lists will be copied instead of having their
749
+ references added.
750
+ """
751
+
752
+ if properties is None:
753
+ return
754
+
755
+ for property, value in properties.iteritems():
756
+ # Make sure the property is in the schema.
757
+ if not property in self._schema:
758
+ raise KeyError(property + ' not in ' + self.__class__.__name__)
759
+
760
+ # Make sure the property conforms to the schema.
761
+ (is_list, property_type, is_strong) = self._schema[property][0:3]
762
+ if is_list:
763
+ if value.__class__ != list:
764
+ raise TypeError(
765
+ property + ' of ' + self.__class__.__name__ + \
766
+ ' must be list, not ' + value.__class__.__name__)
767
+ for item in value:
768
+ if not isinstance(item, property_type) and \
769
+ not (item.__class__ == unicode and property_type == str):
770
+ # Accept unicode where str is specified. str is treated as
771
+ # UTF-8-encoded.
772
+ raise TypeError(
773
+ 'item of ' + property + ' of ' + self.__class__.__name__ + \
774
+ ' must be ' + property_type.__name__ + ', not ' + \
775
+ item.__class__.__name__)
776
+ elif not isinstance(value, property_type) and \
777
+ not (value.__class__ == unicode and property_type == str):
778
+ # Accept unicode where str is specified. str is treated as
779
+ # UTF-8-encoded.
780
+ raise TypeError(
781
+ property + ' of ' + self.__class__.__name__ + ' must be ' + \
782
+ property_type.__name__ + ', not ' + value.__class__.__name__)
783
+
784
+ # Checks passed, perform the assignment.
785
+ if do_copy:
786
+ if isinstance(value, XCObject):
787
+ if is_strong:
788
+ self._properties[property] = value.Copy()
789
+ else:
790
+ self._properties[property] = value
791
+ elif isinstance(value, str) or isinstance(value, unicode) or \
792
+ isinstance(value, int):
793
+ self._properties[property] = value
794
+ elif isinstance(value, list):
795
+ if is_strong:
796
+ # If is_strong is True, each element is an XCObject, so it's safe
797
+ # to call Copy.
798
+ self._properties[property] = []
799
+ for item in value:
800
+ self._properties[property].append(item.Copy())
801
+ else:
802
+ self._properties[property] = value[:]
803
+ elif isinstance(value, dict):
804
+ self._properties[property] = value.copy()
805
+ else:
806
+ raise TypeError("Don't know how to copy a " + \
807
+ value.__class__.__name__ + ' object for ' + \
808
+ property + ' in ' + self.__class__.__name__)
809
+ else:
810
+ self._properties[property] = value
811
+
812
+ # Set up the child's back-reference to this object. Don't use |value|
813
+ # any more because it may not be right if do_copy is true.
814
+ if is_strong:
815
+ if not is_list:
816
+ self._properties[property].parent = self
817
+ else:
818
+ for item in self._properties[property]:
819
+ item.parent = self
820
+
821
+ def HasProperty(self, key):
822
+ return key in self._properties
823
+
824
+ def GetProperty(self, key):
825
+ return self._properties[key]
826
+
827
+ def SetProperty(self, key, value):
828
+ self.UpdateProperties({key: value})
829
+
830
+ def DelProperty(self, key):
831
+ if key in self._properties:
832
+ del self._properties[key]
833
+
834
+ def AppendProperty(self, key, value):
835
+ # TODO(mark): Support ExtendProperty too (and make this call that)?
836
+
837
+ # Schema validation.
838
+ if not key in self._schema:
839
+ raise KeyError(key + ' not in ' + self.__class__.__name__)
840
+
841
+ (is_list, property_type, is_strong) = self._schema[key][0:3]
842
+ if not is_list:
843
+ raise TypeError(key + ' of ' + self.__class__.__name__ + ' must be list')
844
+ if not isinstance(value, property_type):
845
+ raise TypeError('item of ' + key + ' of ' + self.__class__.__name__ + \
846
+ ' must be ' + property_type.__name__ + ', not ' + \
847
+ value.__class__.__name__)
848
+
849
+ # If the property doesn't exist yet, create a new empty list to receive the
850
+ # item.
851
+ if not key in self._properties:
852
+ self._properties[key] = []
853
+
854
+ # Set up the ownership link.
855
+ if is_strong:
856
+ value.parent = self
857
+
858
+ # Store the item.
859
+ self._properties[key].append(value)
860
+
861
+ def VerifyHasRequiredProperties(self):
862
+ """Ensure that all properties identified as required by the schema are
863
+ set.
864
+ """
865
+
866
+ # TODO(mark): A stronger verification mechanism is needed. Some
867
+ # subclasses need to perform validation beyond what the schema can enforce.
868
+ for property, attributes in self._schema.iteritems():
869
+ (is_list, property_type, is_strong, is_required) = attributes[0:4]
870
+ if is_required and not property in self._properties:
871
+ raise KeyError(self.__class__.__name__ + ' requires ' + property)
872
+
873
+ def _SetDefaultsFromSchema(self):
874
+ """Assign object default values according to the schema. This will not
875
+ overwrite properties that have already been set."""
876
+
877
+ defaults = {}
878
+ for property, attributes in self._schema.iteritems():
879
+ (is_list, property_type, is_strong, is_required) = attributes[0:4]
880
+ if is_required and len(attributes) >= 5 and \
881
+ not property in self._properties:
882
+ default = attributes[4]
883
+
884
+ defaults[property] = default
885
+
886
+ if len(defaults) > 0:
887
+ # Use do_copy=True so that each new object gets its own copy of strong
888
+ # objects, lists, and dicts.
889
+ self.UpdateProperties(defaults, do_copy=True)
890
+
891
+
892
+ class XCHierarchicalElement(XCObject):
893
+ """Abstract base for PBXGroup and PBXFileReference. Not represented in a
894
+ project file."""
895
+
896
+ # TODO(mark): Do name and path belong here? Probably so.
897
+ # If path is set and name is not, name may have a default value. Name will
898
+ # be set to the basename of path, if the basename of path is different from
899
+ # the full value of path. If path is already just a leaf name, name will
900
+ # not be set.
901
+ _schema = XCObject._schema.copy()
902
+ _schema.update({
903
+ 'comments': [0, str, 0, 0],
904
+ 'fileEncoding': [0, str, 0, 0],
905
+ 'includeInIndex': [0, int, 0, 0],
906
+ 'indentWidth': [0, int, 0, 0],
907
+ 'lineEnding': [0, int, 0, 0],
908
+ 'sourceTree': [0, str, 0, 1, '<group>'],
909
+ 'tabWidth': [0, int, 0, 0],
910
+ 'usesTabs': [0, int, 0, 0],
911
+ 'wrapsLines': [0, int, 0, 0],
912
+ })
913
+
914
+ def __init__(self, properties=None, id=None, parent=None):
915
+ # super
916
+ XCObject.__init__(self, properties, id, parent)
917
+ if 'path' in self._properties and not 'name' in self._properties:
918
+ path = self._properties['path']
919
+ name = posixpath.basename(path)
920
+ if name != '' and path != name:
921
+ self.SetProperty('name', name)
922
+
923
+ if 'path' in self._properties and \
924
+ (not 'sourceTree' in self._properties or \
925
+ self._properties['sourceTree'] == '<group>'):
926
+ # If the pathname begins with an Xcode variable like "$(SDKROOT)/", take
927
+ # the variable out and make the path be relative to that variable by
928
+ # assigning the variable name as the sourceTree.
929
+ (source_tree, path) = SourceTreeAndPathFromPath(self._properties['path'])
930
+ if source_tree != None:
931
+ self._properties['sourceTree'] = source_tree
932
+ if path != None:
933
+ self._properties['path'] = path
934
+ if source_tree != None and path is None and \
935
+ not 'name' in self._properties:
936
+ # The path was of the form "$(SDKROOT)" with no path following it.
937
+ # This object is now relative to that variable, so it has no path
938
+ # attribute of its own. It does, however, keep a name.
939
+ del self._properties['path']
940
+ self._properties['name'] = source_tree
941
+
942
+ def Name(self):
943
+ if 'name' in self._properties:
944
+ return self._properties['name']
945
+ elif 'path' in self._properties:
946
+ return self._properties['path']
947
+ else:
948
+ # This happens in the case of the root PBXGroup.
949
+ return None
950
+
951
+ def Hashables(self):
952
+ """Custom hashables for XCHierarchicalElements.
953
+
954
+ XCHierarchicalElements are special. Generally, their hashes shouldn't
955
+ change if the paths don't change. The normal XCObject implementation of
956
+ Hashables adds a hashable for each object, which means that if
957
+ the hierarchical structure changes (possibly due to changes caused when
958
+ TakeOverOnlyChild runs and encounters slight changes in the hierarchy),
959
+ the hashes will change. For example, if a project file initially contains
960
+ a/b/f1 and a/b becomes collapsed into a/b, f1 will have a single parent
961
+ a/b. If someone later adds a/f2 to the project file, a/b can no longer be
962
+ collapsed, and f1 winds up with parent b and grandparent a. That would
963
+ be sufficient to change f1's hash.
964
+
965
+ To counteract this problem, hashables for all XCHierarchicalElements except
966
+ for the main group (which has neither a name nor a path) are taken to be
967
+ just the set of path components. Because hashables are inherited from
968
+ parents, this provides assurance that a/b/f1 has the same set of hashables
969
+ whether its parent is b or a/b.
970
+
971
+ The main group is a special case. As it is permitted to have no name or
972
+ path, it is permitted to use the standard XCObject hash mechanism. This
973
+ is not considered a problem because there can be only one main group.
974
+ """
975
+
976
+ if self == self.PBXProjectAncestor()._properties['mainGroup']:
977
+ # super
978
+ return XCObject.Hashables(self)
979
+
980
+ hashables = []
981
+
982
+ # Put the name in first, ensuring that if TakeOverOnlyChild collapses
983
+ # children into a top-level group like "Source", the name always goes
984
+ # into the list of hashables without interfering with path components.
985
+ if 'name' in self._properties:
986
+ # Make it less likely for people to manipulate hashes by following the
987
+ # pattern of always pushing an object type value onto the list first.
988
+ hashables.append(self.__class__.__name__ + '.name')
989
+ hashables.append(self._properties['name'])
990
+
991
+ # NOTE: This still has the problem that if an absolute path is encountered,
992
+ # including paths with a sourceTree, they'll still inherit their parents'
993
+ # hashables, even though the paths aren't relative to their parents. This
994
+ # is not expected to be much of a problem in practice.
995
+ path = self.PathFromSourceTreeAndPath()
996
+ if path != None:
997
+ components = path.split(posixpath.sep)
998
+ for component in components:
999
+ hashables.append(self.__class__.__name__ + '.path')
1000
+ hashables.append(component)
1001
+
1002
+ hashables.extend(self._hashables)
1003
+
1004
+ return hashables
1005
+
1006
+ def Compare(self, other):
1007
+ # Allow comparison of these types. PBXGroup has the highest sort rank;
1008
+ # PBXVariantGroup is treated as equal to PBXFileReference.
1009
+ valid_class_types = {
1010
+ PBXFileReference: 'file',
1011
+ PBXGroup: 'group',
1012
+ PBXVariantGroup: 'file',
1013
+ }
1014
+ self_type = valid_class_types[self.__class__]
1015
+ other_type = valid_class_types[other.__class__]
1016
+
1017
+ if self_type == other_type:
1018
+ # If the two objects are of the same sort rank, compare their names.
1019
+ return cmp(self.Name(), other.Name())
1020
+
1021
+ # Otherwise, sort groups before everything else.
1022
+ if self_type == 'group':
1023
+ return -1
1024
+ return 1
1025
+
1026
+ def CompareRootGroup(self, other):
1027
+ # This function should be used only to compare direct children of the
1028
+ # containing PBXProject's mainGroup. These groups should appear in the
1029
+ # listed order.
1030
+ # TODO(mark): "Build" is used by gyp.generator.xcode, perhaps the
1031
+ # generator should have a way of influencing this list rather than having
1032
+ # to hardcode for the generator here.
1033
+ order = ['Source', 'Intermediates', 'Projects', 'Frameworks', 'Products',
1034
+ 'Build']
1035
+
1036
+ # If the groups aren't in the listed order, do a name comparison.
1037
+ # Otherwise, groups in the listed order should come before those that
1038
+ # aren't.
1039
+ self_name = self.Name()
1040
+ other_name = other.Name()
1041
+ self_in = isinstance(self, PBXGroup) and self_name in order
1042
+ other_in = isinstance(self, PBXGroup) and other_name in order
1043
+ if not self_in and not other_in:
1044
+ return self.Compare(other)
1045
+ if self_name in order and not other_name in order:
1046
+ return -1
1047
+ if other_name in order and not self_name in order:
1048
+ return 1
1049
+
1050
+ # If both groups are in the listed order, go by the defined order.
1051
+ self_index = order.index(self_name)
1052
+ other_index = order.index(other_name)
1053
+ if self_index < other_index:
1054
+ return -1
1055
+ if self_index > other_index:
1056
+ return 1
1057
+ return 0
1058
+
1059
+ def PathFromSourceTreeAndPath(self):
1060
+ # Turn the object's sourceTree and path properties into a single flat
1061
+ # string of a form comparable to the path parameter. If there's a
1062
+ # sourceTree property other than "<group>", wrap it in $(...) for the
1063
+ # comparison.
1064
+ components = []
1065
+ if self._properties['sourceTree'] != '<group>':
1066
+ components.append('$(' + self._properties['sourceTree'] + ')')
1067
+ if 'path' in self._properties:
1068
+ components.append(self._properties['path'])
1069
+
1070
+ if len(components) > 0:
1071
+ return posixpath.join(*components)
1072
+
1073
+ return None
1074
+
1075
+ def FullPath(self):
1076
+ # Returns a full path to self relative to the project file, or relative
1077
+ # to some other source tree. Start with self, and walk up the chain of
1078
+ # parents prepending their paths, if any, until no more parents are
1079
+ # available (project-relative path) or until a path relative to some
1080
+ # source tree is found.
1081
+ xche = self
1082
+ path = None
1083
+ while isinstance(xche, XCHierarchicalElement) and \
1084
+ (path is None or \
1085
+ (not path.startswith('/') and not path.startswith('$'))):
1086
+ this_path = xche.PathFromSourceTreeAndPath()
1087
+ if this_path != None and path != None:
1088
+ path = posixpath.join(this_path, path)
1089
+ elif this_path != None:
1090
+ path = this_path
1091
+ xche = xche.parent
1092
+
1093
+ return path
1094
+
1095
+
1096
+ class PBXGroup(XCHierarchicalElement):
1097
+ """
1098
+ Attributes:
1099
+ _children_by_path: Maps pathnames of children of this PBXGroup to the
1100
+ actual child XCHierarchicalElement objects.
1101
+ _variant_children_by_name_and_path: Maps (name, path) tuples of
1102
+ PBXVariantGroup children to the actual child PBXVariantGroup objects.
1103
+ """
1104
+
1105
+ _schema = XCHierarchicalElement._schema.copy()
1106
+ _schema.update({
1107
+ 'children': [1, XCHierarchicalElement, 1, 1, []],
1108
+ 'name': [0, str, 0, 0],
1109
+ 'path': [0, str, 0, 0],
1110
+ })
1111
+
1112
+ def __init__(self, properties=None, id=None, parent=None):
1113
+ # super
1114
+ XCHierarchicalElement.__init__(self, properties, id, parent)
1115
+ self._children_by_path = {}
1116
+ self._variant_children_by_name_and_path = {}
1117
+ for child in self._properties.get('children', []):
1118
+ self._AddChildToDicts(child)
1119
+
1120
+ def Hashables(self):
1121
+ # super
1122
+ hashables = XCHierarchicalElement.Hashables(self)
1123
+
1124
+ # It is not sufficient to just rely on name and parent to build a unique
1125
+ # hashable : a node could have two child PBXGroup sharing a common name.
1126
+ # To add entropy the hashable is enhanced with the names of all its
1127
+ # children.
1128
+ for child in self._properties.get('children', []):
1129
+ child_name = child.Name()
1130
+ if child_name != None:
1131
+ hashables.append(child_name)
1132
+
1133
+ return hashables
1134
+
1135
+ def HashablesForChild(self):
1136
+ # To avoid a circular reference the hashables used to compute a child id do
1137
+ # not include the child names.
1138
+ return XCHierarchicalElement.Hashables(self)
1139
+
1140
+ def _AddChildToDicts(self, child):
1141
+ # Sets up this PBXGroup object's dicts to reference the child properly.
1142
+ child_path = child.PathFromSourceTreeAndPath()
1143
+ if child_path:
1144
+ if child_path in self._children_by_path:
1145
+ raise ValueError('Found multiple children with path ' + child_path)
1146
+ self._children_by_path[child_path] = child
1147
+
1148
+ if isinstance(child, PBXVariantGroup):
1149
+ child_name = child._properties.get('name', None)
1150
+ key = (child_name, child_path)
1151
+ if key in self._variant_children_by_name_and_path:
1152
+ raise ValueError('Found multiple PBXVariantGroup children with ' + \
1153
+ 'name ' + str(child_name) + ' and path ' + \
1154
+ str(child_path))
1155
+ self._variant_children_by_name_and_path[key] = child
1156
+
1157
+ def AppendChild(self, child):
1158
+ # Callers should use this instead of calling
1159
+ # AppendProperty('children', child) directly because this function
1160
+ # maintains the group's dicts.
1161
+ self.AppendProperty('children', child)
1162
+ self._AddChildToDicts(child)
1163
+
1164
+ def GetChildByName(self, name):
1165
+ # This is not currently optimized with a dict as GetChildByPath is because
1166
+ # it has few callers. Most callers probably want GetChildByPath. This
1167
+ # function is only useful to get children that have names but no paths,
1168
+ # which is rare. The children of the main group ("Source", "Products",
1169
+ # etc.) is pretty much the only case where this likely to come up.
1170
+ #
1171
+ # TODO(mark): Maybe this should raise an error if more than one child is
1172
+ # present with the same name.
1173
+ if not 'children' in self._properties:
1174
+ return None
1175
+
1176
+ for child in self._properties['children']:
1177
+ if child.Name() == name:
1178
+ return child
1179
+
1180
+ return None
1181
+
1182
+ def GetChildByPath(self, path):
1183
+ if not path:
1184
+ return None
1185
+
1186
+ if path in self._children_by_path:
1187
+ return self._children_by_path[path]
1188
+
1189
+ return None
1190
+
1191
+ def GetChildByRemoteObject(self, remote_object):
1192
+ # This method is a little bit esoteric. Given a remote_object, which
1193
+ # should be a PBXFileReference in another project file, this method will
1194
+ # return this group's PBXReferenceProxy object serving as a local proxy
1195
+ # for the remote PBXFileReference.
1196
+ #
1197
+ # This function might benefit from a dict optimization as GetChildByPath
1198
+ # for some workloads, but profiling shows that it's not currently a
1199
+ # problem.
1200
+ if not 'children' in self._properties:
1201
+ return None
1202
+
1203
+ for child in self._properties['children']:
1204
+ if not isinstance(child, PBXReferenceProxy):
1205
+ continue
1206
+
1207
+ container_proxy = child._properties['remoteRef']
1208
+ if container_proxy._properties['remoteGlobalIDString'] == remote_object:
1209
+ return child
1210
+
1211
+ return None
1212
+
1213
+ def AddOrGetFileByPath(self, path, hierarchical):
1214
+ """Returns an existing or new file reference corresponding to path.
1215
+
1216
+ If hierarchical is True, this method will create or use the necessary
1217
+ hierarchical group structure corresponding to path. Otherwise, it will
1218
+ look in and create an item in the current group only.
1219
+
1220
+ If an existing matching reference is found, it is returned, otherwise, a
1221
+ new one will be created, added to the correct group, and returned.
1222
+
1223
+ If path identifies a directory by virtue of carrying a trailing slash,
1224
+ this method returns a PBXFileReference of "folder" type. If path
1225
+ identifies a variant, by virtue of it identifying a file inside a directory
1226
+ with an ".lproj" extension, this method returns a PBXVariantGroup
1227
+ containing the variant named by path, and possibly other variants. For
1228
+ all other paths, a "normal" PBXFileReference will be returned.
1229
+ """
1230
+
1231
+ # Adding or getting a directory? Directories end with a trailing slash.
1232
+ is_dir = False
1233
+ if path.endswith('/'):
1234
+ is_dir = True
1235
+ path = posixpath.normpath(path)
1236
+ if is_dir:
1237
+ path = path + '/'
1238
+
1239
+ # Adding or getting a variant? Variants are files inside directories
1240
+ # with an ".lproj" extension. Xcode uses variants for localization. For
1241
+ # a variant path/to/Language.lproj/MainMenu.nib, put a variant group named
1242
+ # MainMenu.nib inside path/to, and give it a variant named Language. In
1243
+ # this example, grandparent would be set to path/to and parent_root would
1244
+ # be set to Language.
1245
+ variant_name = None
1246
+ parent = posixpath.dirname(path)
1247
+ grandparent = posixpath.dirname(parent)
1248
+ parent_basename = posixpath.basename(parent)
1249
+ (parent_root, parent_ext) = posixpath.splitext(parent_basename)
1250
+ if parent_ext == '.lproj':
1251
+ variant_name = parent_root
1252
+ if grandparent == '':
1253
+ grandparent = None
1254
+
1255
+ # Putting a directory inside a variant group is not currently supported.
1256
+ assert not is_dir or variant_name is None
1257
+
1258
+ path_split = path.split(posixpath.sep)
1259
+ if len(path_split) == 1 or \
1260
+ ((is_dir or variant_name != None) and len(path_split) == 2) or \
1261
+ not hierarchical:
1262
+ # The PBXFileReference or PBXVariantGroup will be added to or gotten from
1263
+ # this PBXGroup, no recursion necessary.
1264
+ if variant_name is None:
1265
+ # Add or get a PBXFileReference.
1266
+ file_ref = self.GetChildByPath(path)
1267
+ if file_ref != None:
1268
+ assert file_ref.__class__ == PBXFileReference
1269
+ else:
1270
+ file_ref = PBXFileReference({'path': path})
1271
+ self.AppendChild(file_ref)
1272
+ else:
1273
+ # Add or get a PBXVariantGroup. The variant group name is the same
1274
+ # as the basename (MainMenu.nib in the example above). grandparent
1275
+ # specifies the path to the variant group itself, and path_split[-2:]
1276
+ # is the path of the specific variant relative to its group.
1277
+ variant_group_name = posixpath.basename(path)
1278
+ variant_group_ref = self.AddOrGetVariantGroupByNameAndPath(
1279
+ variant_group_name, grandparent)
1280
+ variant_path = posixpath.sep.join(path_split[-2:])
1281
+ variant_ref = variant_group_ref.GetChildByPath(variant_path)
1282
+ if variant_ref != None:
1283
+ assert variant_ref.__class__ == PBXFileReference
1284
+ else:
1285
+ variant_ref = PBXFileReference({'name': variant_name,
1286
+ 'path': variant_path})
1287
+ variant_group_ref.AppendChild(variant_ref)
1288
+ # The caller is interested in the variant group, not the specific
1289
+ # variant file.
1290
+ file_ref = variant_group_ref
1291
+ return file_ref
1292
+ else:
1293
+ # Hierarchical recursion. Add or get a PBXGroup corresponding to the
1294
+ # outermost path component, and then recurse into it, chopping off that
1295
+ # path component.
1296
+ next_dir = path_split[0]
1297
+ group_ref = self.GetChildByPath(next_dir)
1298
+ if group_ref != None:
1299
+ assert group_ref.__class__ == PBXGroup
1300
+ else:
1301
+ group_ref = PBXGroup({'path': next_dir})
1302
+ self.AppendChild(group_ref)
1303
+ return group_ref.AddOrGetFileByPath(posixpath.sep.join(path_split[1:]),
1304
+ hierarchical)
1305
+
1306
+ def AddOrGetVariantGroupByNameAndPath(self, name, path):
1307
+ """Returns an existing or new PBXVariantGroup for name and path.
1308
+
1309
+ If a PBXVariantGroup identified by the name and path arguments is already
1310
+ present as a child of this object, it is returned. Otherwise, a new
1311
+ PBXVariantGroup with the correct properties is created, added as a child,
1312
+ and returned.
1313
+
1314
+ This method will generally be called by AddOrGetFileByPath, which knows
1315
+ when to create a variant group based on the structure of the pathnames
1316
+ passed to it.
1317
+ """
1318
+
1319
+ key = (name, path)
1320
+ if key in self._variant_children_by_name_and_path:
1321
+ variant_group_ref = self._variant_children_by_name_and_path[key]
1322
+ assert variant_group_ref.__class__ == PBXVariantGroup
1323
+ return variant_group_ref
1324
+
1325
+ variant_group_properties = {'name': name}
1326
+ if path != None:
1327
+ variant_group_properties['path'] = path
1328
+ variant_group_ref = PBXVariantGroup(variant_group_properties)
1329
+ self.AppendChild(variant_group_ref)
1330
+
1331
+ return variant_group_ref
1332
+
1333
+ def TakeOverOnlyChild(self, recurse=False):
1334
+ """If this PBXGroup has only one child and it's also a PBXGroup, take
1335
+ it over by making all of its children this object's children.
1336
+
1337
+ This function will continue to take over only children when those children
1338
+ are groups. If there are three PBXGroups representing a, b, and c, with
1339
+ c inside b and b inside a, and a and b have no other children, this will
1340
+ result in a taking over both b and c, forming a PBXGroup for a/b/c.
1341
+
1342
+ If recurse is True, this function will recurse into children and ask them
1343
+ to collapse themselves by taking over only children as well. Assuming
1344
+ an example hierarchy with files at a/b/c/d1, a/b/c/d2, and a/b/c/d3/e/f
1345
+ (d1, d2, and f are files, the rest are groups), recursion will result in
1346
+ a group for a/b/c containing a group for d3/e.
1347
+ """
1348
+
1349
+ # At this stage, check that child class types are PBXGroup exactly,
1350
+ # instead of using isinstance. The only subclass of PBXGroup,
1351
+ # PBXVariantGroup, should not participate in reparenting in the same way:
1352
+ # reparenting by merging different object types would be wrong.
1353
+ while len(self._properties['children']) == 1 and \
1354
+ self._properties['children'][0].__class__ == PBXGroup:
1355
+ # Loop to take over the innermost only-child group possible.
1356
+
1357
+ child = self._properties['children'][0]
1358
+
1359
+ # Assume the child's properties, including its children. Save a copy
1360
+ # of this object's old properties, because they'll still be needed.
1361
+ # This object retains its existing id and parent attributes.
1362
+ old_properties = self._properties
1363
+ self._properties = child._properties
1364
+ self._children_by_path = child._children_by_path
1365
+
1366
+ if not 'sourceTree' in self._properties or \
1367
+ self._properties['sourceTree'] == '<group>':
1368
+ # The child was relative to its parent. Fix up the path. Note that
1369
+ # children with a sourceTree other than "<group>" are not relative to
1370
+ # their parents, so no path fix-up is needed in that case.
1371
+ if 'path' in old_properties:
1372
+ if 'path' in self._properties:
1373
+ # Both the original parent and child have paths set.
1374
+ self._properties['path'] = posixpath.join(old_properties['path'],
1375
+ self._properties['path'])
1376
+ else:
1377
+ # Only the original parent has a path, use it.
1378
+ self._properties['path'] = old_properties['path']
1379
+ if 'sourceTree' in old_properties:
1380
+ # The original parent had a sourceTree set, use it.
1381
+ self._properties['sourceTree'] = old_properties['sourceTree']
1382
+
1383
+ # If the original parent had a name set, keep using it. If the original
1384
+ # parent didn't have a name but the child did, let the child's name
1385
+ # live on. If the name attribute seems unnecessary now, get rid of it.
1386
+ if 'name' in old_properties and old_properties['name'] != None and \
1387
+ old_properties['name'] != self.Name():
1388
+ self._properties['name'] = old_properties['name']
1389
+ if 'name' in self._properties and 'path' in self._properties and \
1390
+ self._properties['name'] == self._properties['path']:
1391
+ del self._properties['name']
1392
+
1393
+ # Notify all children of their new parent.
1394
+ for child in self._properties['children']:
1395
+ child.parent = self
1396
+
1397
+ # If asked to recurse, recurse.
1398
+ if recurse:
1399
+ for child in self._properties['children']:
1400
+ if child.__class__ == PBXGroup:
1401
+ child.TakeOverOnlyChild(recurse)
1402
+
1403
+ def SortGroup(self):
1404
+ self._properties['children'] = \
1405
+ sorted(self._properties['children'], cmp=lambda x,y: x.Compare(y))
1406
+
1407
+ # Recurse.
1408
+ for child in self._properties['children']:
1409
+ if isinstance(child, PBXGroup):
1410
+ child.SortGroup()
1411
+
1412
+
1413
+ class XCFileLikeElement(XCHierarchicalElement):
1414
+ # Abstract base for objects that can be used as the fileRef property of
1415
+ # PBXBuildFile.
1416
+
1417
+ def PathHashables(self):
1418
+ # A PBXBuildFile that refers to this object will call this method to
1419
+ # obtain additional hashables specific to this XCFileLikeElement. Don't
1420
+ # just use this object's hashables, they're not specific and unique enough
1421
+ # on their own (without access to the parent hashables.) Instead, provide
1422
+ # hashables that identify this object by path by getting its hashables as
1423
+ # well as the hashables of ancestor XCHierarchicalElement objects.
1424
+
1425
+ hashables = []
1426
+ xche = self
1427
+ while xche != None and isinstance(xche, XCHierarchicalElement):
1428
+ xche_hashables = xche.Hashables()
1429
+ for index in xrange(0, len(xche_hashables)):
1430
+ hashables.insert(index, xche_hashables[index])
1431
+ xche = xche.parent
1432
+ return hashables
1433
+
1434
+
1435
+ class XCContainerPortal(XCObject):
1436
+ # Abstract base for objects that can be used as the containerPortal property
1437
+ # of PBXContainerItemProxy.
1438
+ pass
1439
+
1440
+
1441
+ class XCRemoteObject(XCObject):
1442
+ # Abstract base for objects that can be used as the remoteGlobalIDString
1443
+ # property of PBXContainerItemProxy.
1444
+ pass
1445
+
1446
+
1447
+ class PBXFileReference(XCFileLikeElement, XCContainerPortal, XCRemoteObject):
1448
+ _schema = XCFileLikeElement._schema.copy()
1449
+ _schema.update({
1450
+ 'explicitFileType': [0, str, 0, 0],
1451
+ 'lastKnownFileType': [0, str, 0, 0],
1452
+ 'name': [0, str, 0, 0],
1453
+ 'path': [0, str, 0, 1],
1454
+ })
1455
+
1456
+ # Weird output rules for PBXFileReference.
1457
+ _should_print_single_line = True
1458
+ # super
1459
+ _encode_transforms = XCFileLikeElement._alternate_encode_transforms
1460
+
1461
+ def __init__(self, properties=None, id=None, parent=None):
1462
+ # super
1463
+ XCFileLikeElement.__init__(self, properties, id, parent)
1464
+ if 'path' in self._properties and self._properties['path'].endswith('/'):
1465
+ self._properties['path'] = self._properties['path'][:-1]
1466
+ is_dir = True
1467
+ else:
1468
+ is_dir = False
1469
+
1470
+ if 'path' in self._properties and \
1471
+ not 'lastKnownFileType' in self._properties and \
1472
+ not 'explicitFileType' in self._properties:
1473
+ # TODO(mark): This is the replacement for a replacement for a quick hack.
1474
+ # It is no longer incredibly sucky, but this list needs to be extended.
1475
+ extension_map = {
1476
+ 'a': 'archive.ar',
1477
+ 'app': 'wrapper.application',
1478
+ 'bdic': 'file',
1479
+ 'bundle': 'wrapper.cfbundle',
1480
+ 'c': 'sourcecode.c.c',
1481
+ 'cc': 'sourcecode.cpp.cpp',
1482
+ 'cpp': 'sourcecode.cpp.cpp',
1483
+ 'css': 'text.css',
1484
+ 'cxx': 'sourcecode.cpp.cpp',
1485
+ 'dart': 'sourcecode',
1486
+ 'dylib': 'compiled.mach-o.dylib',
1487
+ 'framework': 'wrapper.framework',
1488
+ 'gyp': 'sourcecode',
1489
+ 'gypi': 'sourcecode',
1490
+ 'h': 'sourcecode.c.h',
1491
+ 'hxx': 'sourcecode.cpp.h',
1492
+ 'icns': 'image.icns',
1493
+ 'java': 'sourcecode.java',
1494
+ 'js': 'sourcecode.javascript',
1495
+ 'kext': 'wrapper.kext',
1496
+ 'm': 'sourcecode.c.objc',
1497
+ 'mm': 'sourcecode.cpp.objcpp',
1498
+ 'nib': 'wrapper.nib',
1499
+ 'o': 'compiled.mach-o.objfile',
1500
+ 'pdf': 'image.pdf',
1501
+ 'pl': 'text.script.perl',
1502
+ 'plist': 'text.plist.xml',
1503
+ 'pm': 'text.script.perl',
1504
+ 'png': 'image.png',
1505
+ 'py': 'text.script.python',
1506
+ 'r': 'sourcecode.rez',
1507
+ 'rez': 'sourcecode.rez',
1508
+ 's': 'sourcecode.asm',
1509
+ 'storyboard': 'file.storyboard',
1510
+ 'strings': 'text.plist.strings',
1511
+ 'swift': 'sourcecode.swift',
1512
+ 'ttf': 'file',
1513
+ 'xcassets': 'folder.assetcatalog',
1514
+ 'xcconfig': 'text.xcconfig',
1515
+ 'xcdatamodel': 'wrapper.xcdatamodel',
1516
+ 'xcdatamodeld':'wrapper.xcdatamodeld',
1517
+ 'xib': 'file.xib',
1518
+ 'y': 'sourcecode.yacc',
1519
+ }
1520
+
1521
+ prop_map = {
1522
+ 'dart': 'explicitFileType',
1523
+ 'gyp': 'explicitFileType',
1524
+ 'gypi': 'explicitFileType',
1525
+ }
1526
+
1527
+ if is_dir:
1528
+ file_type = 'folder'
1529
+ prop_name = 'lastKnownFileType'
1530
+ else:
1531
+ basename = posixpath.basename(self._properties['path'])
1532
+ (root, ext) = posixpath.splitext(basename)
1533
+ # Check the map using a lowercase extension.
1534
+ # TODO(mark): Maybe it should try with the original case first and fall
1535
+ # back to lowercase, in case there are any instances where case
1536
+ # matters. There currently aren't.
1537
+ if ext != '':
1538
+ ext = ext[1:].lower()
1539
+
1540
+ # TODO(mark): "text" is the default value, but "file" is appropriate
1541
+ # for unrecognized files not containing text. Xcode seems to choose
1542
+ # based on content.
1543
+ file_type = extension_map.get(ext, 'text')
1544
+ prop_name = prop_map.get(ext, 'lastKnownFileType')
1545
+
1546
+ self._properties[prop_name] = file_type
1547
+
1548
+
1549
+ class PBXVariantGroup(PBXGroup, XCFileLikeElement):
1550
+ """PBXVariantGroup is used by Xcode to represent localizations."""
1551
+ # No additions to the schema relative to PBXGroup.
1552
+ pass
1553
+
1554
+
1555
+ # PBXReferenceProxy is also an XCFileLikeElement subclass. It is defined below
1556
+ # because it uses PBXContainerItemProxy, defined below.
1557
+
1558
+
1559
+ class XCBuildConfiguration(XCObject):
1560
+ _schema = XCObject._schema.copy()
1561
+ _schema.update({
1562
+ 'baseConfigurationReference': [0, PBXFileReference, 0, 0],
1563
+ 'buildSettings': [0, dict, 0, 1, {}],
1564
+ 'name': [0, str, 0, 1],
1565
+ })
1566
+
1567
+ def HasBuildSetting(self, key):
1568
+ return key in self._properties['buildSettings']
1569
+
1570
+ def GetBuildSetting(self, key):
1571
+ return self._properties['buildSettings'][key]
1572
+
1573
+ def SetBuildSetting(self, key, value):
1574
+ # TODO(mark): If a list, copy?
1575
+ self._properties['buildSettings'][key] = value
1576
+
1577
+ def AppendBuildSetting(self, key, value):
1578
+ if not key in self._properties['buildSettings']:
1579
+ self._properties['buildSettings'][key] = []
1580
+ self._properties['buildSettings'][key].append(value)
1581
+
1582
+ def DelBuildSetting(self, key):
1583
+ if key in self._properties['buildSettings']:
1584
+ del self._properties['buildSettings'][key]
1585
+
1586
+ def SetBaseConfiguration(self, value):
1587
+ self._properties['baseConfigurationReference'] = value
1588
+
1589
+ class XCConfigurationList(XCObject):
1590
+ # _configs is the default list of configurations.
1591
+ _configs = [ XCBuildConfiguration({'name': 'Debug'}),
1592
+ XCBuildConfiguration({'name': 'Release'}) ]
1593
+
1594
+ _schema = XCObject._schema.copy()
1595
+ _schema.update({
1596
+ 'buildConfigurations': [1, XCBuildConfiguration, 1, 1, _configs],
1597
+ 'defaultConfigurationIsVisible': [0, int, 0, 1, 1],
1598
+ 'defaultConfigurationName': [0, str, 0, 1, 'Release'],
1599
+ })
1600
+
1601
+ def Name(self):
1602
+ return 'Build configuration list for ' + \
1603
+ self.parent.__class__.__name__ + ' "' + self.parent.Name() + '"'
1604
+
1605
+ def ConfigurationNamed(self, name):
1606
+ """Convenience accessor to obtain an XCBuildConfiguration by name."""
1607
+ for configuration in self._properties['buildConfigurations']:
1608
+ if configuration._properties['name'] == name:
1609
+ return configuration
1610
+
1611
+ raise KeyError(name)
1612
+
1613
+ def DefaultConfiguration(self):
1614
+ """Convenience accessor to obtain the default XCBuildConfiguration."""
1615
+ return self.ConfigurationNamed(self._properties['defaultConfigurationName'])
1616
+
1617
+ def HasBuildSetting(self, key):
1618
+ """Determines the state of a build setting in all XCBuildConfiguration
1619
+ child objects.
1620
+
1621
+ If all child objects have key in their build settings, and the value is the
1622
+ same in all child objects, returns 1.
1623
+
1624
+ If no child objects have the key in their build settings, returns 0.
1625
+
1626
+ If some, but not all, child objects have the key in their build settings,
1627
+ or if any children have different values for the key, returns -1.
1628
+ """
1629
+
1630
+ has = None
1631
+ value = None
1632
+ for configuration in self._properties['buildConfigurations']:
1633
+ configuration_has = configuration.HasBuildSetting(key)
1634
+ if has is None:
1635
+ has = configuration_has
1636
+ elif has != configuration_has:
1637
+ return -1
1638
+
1639
+ if configuration_has:
1640
+ configuration_value = configuration.GetBuildSetting(key)
1641
+ if value is None:
1642
+ value = configuration_value
1643
+ elif value != configuration_value:
1644
+ return -1
1645
+
1646
+ if not has:
1647
+ return 0
1648
+
1649
+ return 1
1650
+
1651
+ def GetBuildSetting(self, key):
1652
+ """Gets the build setting for key.
1653
+
1654
+ All child XCConfiguration objects must have the same value set for the
1655
+ setting, or a ValueError will be raised.
1656
+ """
1657
+
1658
+ # TODO(mark): This is wrong for build settings that are lists. The list
1659
+ # contents should be compared (and a list copy returned?)
1660
+
1661
+ value = None
1662
+ for configuration in self._properties['buildConfigurations']:
1663
+ configuration_value = configuration.GetBuildSetting(key)
1664
+ if value is None:
1665
+ value = configuration_value
1666
+ else:
1667
+ if value != configuration_value:
1668
+ raise ValueError('Variant values for ' + key)
1669
+
1670
+ return value
1671
+
1672
+ def SetBuildSetting(self, key, value):
1673
+ """Sets the build setting for key to value in all child
1674
+ XCBuildConfiguration objects.
1675
+ """
1676
+
1677
+ for configuration in self._properties['buildConfigurations']:
1678
+ configuration.SetBuildSetting(key, value)
1679
+
1680
+ def AppendBuildSetting(self, key, value):
1681
+ """Appends value to the build setting for key, which is treated as a list,
1682
+ in all child XCBuildConfiguration objects.
1683
+ """
1684
+
1685
+ for configuration in self._properties['buildConfigurations']:
1686
+ configuration.AppendBuildSetting(key, value)
1687
+
1688
+ def DelBuildSetting(self, key):
1689
+ """Deletes the build setting key from all child XCBuildConfiguration
1690
+ objects.
1691
+ """
1692
+
1693
+ for configuration in self._properties['buildConfigurations']:
1694
+ configuration.DelBuildSetting(key)
1695
+
1696
+ def SetBaseConfiguration(self, value):
1697
+ """Sets the build configuration in all child XCBuildConfiguration objects.
1698
+ """
1699
+
1700
+ for configuration in self._properties['buildConfigurations']:
1701
+ configuration.SetBaseConfiguration(value)
1702
+
1703
+
1704
+ class PBXBuildFile(XCObject):
1705
+ _schema = XCObject._schema.copy()
1706
+ _schema.update({
1707
+ 'fileRef': [0, XCFileLikeElement, 0, 1],
1708
+ 'settings': [0, str, 0, 0], # hack, it's a dict
1709
+ })
1710
+
1711
+ # Weird output rules for PBXBuildFile.
1712
+ _should_print_single_line = True
1713
+ _encode_transforms = XCObject._alternate_encode_transforms
1714
+
1715
+ def Name(self):
1716
+ # Example: "main.cc in Sources"
1717
+ return self._properties['fileRef'].Name() + ' in ' + self.parent.Name()
1718
+
1719
+ def Hashables(self):
1720
+ # super
1721
+ hashables = XCObject.Hashables(self)
1722
+
1723
+ # It is not sufficient to just rely on Name() to get the
1724
+ # XCFileLikeElement's name, because that is not a complete pathname.
1725
+ # PathHashables returns hashables unique enough that no two
1726
+ # PBXBuildFiles should wind up with the same set of hashables, unless
1727
+ # someone adds the same file multiple times to the same target. That
1728
+ # would be considered invalid anyway.
1729
+ hashables.extend(self._properties['fileRef'].PathHashables())
1730
+
1731
+ return hashables
1732
+
1733
+
1734
+ class XCBuildPhase(XCObject):
1735
+ """Abstract base for build phase classes. Not represented in a project
1736
+ file.
1737
+
1738
+ Attributes:
1739
+ _files_by_path: A dict mapping each path of a child in the files list by
1740
+ path (keys) to the corresponding PBXBuildFile children (values).
1741
+ _files_by_xcfilelikeelement: A dict mapping each XCFileLikeElement (keys)
1742
+ to the corresponding PBXBuildFile children (values).
1743
+ """
1744
+
1745
+ # TODO(mark): Some build phase types, like PBXShellScriptBuildPhase, don't
1746
+ # actually have a "files" list. XCBuildPhase should not have "files" but
1747
+ # another abstract subclass of it should provide this, and concrete build
1748
+ # phase types that do have "files" lists should be derived from that new
1749
+ # abstract subclass. XCBuildPhase should only provide buildActionMask and
1750
+ # runOnlyForDeploymentPostprocessing, and not files or the various
1751
+ # file-related methods and attributes.
1752
+
1753
+ _schema = XCObject._schema.copy()
1754
+ _schema.update({
1755
+ 'buildActionMask': [0, int, 0, 1, 0x7fffffff],
1756
+ 'files': [1, PBXBuildFile, 1, 1, []],
1757
+ 'runOnlyForDeploymentPostprocessing': [0, int, 0, 1, 0],
1758
+ })
1759
+
1760
+ def __init__(self, properties=None, id=None, parent=None):
1761
+ # super
1762
+ XCObject.__init__(self, properties, id, parent)
1763
+
1764
+ self._files_by_path = {}
1765
+ self._files_by_xcfilelikeelement = {}
1766
+ for pbxbuildfile in self._properties.get('files', []):
1767
+ self._AddBuildFileToDicts(pbxbuildfile)
1768
+
1769
+ def FileGroup(self, path):
1770
+ # Subclasses must override this by returning a two-element tuple. The
1771
+ # first item in the tuple should be the PBXGroup to which "path" should be
1772
+ # added, either as a child or deeper descendant. The second item should
1773
+ # be a boolean indicating whether files should be added into hierarchical
1774
+ # groups or one single flat group.
1775
+ raise NotImplementedError(
1776
+ self.__class__.__name__ + ' must implement FileGroup')
1777
+
1778
+ def _AddPathToDict(self, pbxbuildfile, path):
1779
+ """Adds path to the dict tracking paths belonging to this build phase.
1780
+
1781
+ If the path is already a member of this build phase, raises an exception.
1782
+ """
1783
+
1784
+ if path in self._files_by_path:
1785
+ raise ValueError('Found multiple build files with path ' + path)
1786
+ self._files_by_path[path] = pbxbuildfile
1787
+
1788
+ def _AddBuildFileToDicts(self, pbxbuildfile, path=None):
1789
+ """Maintains the _files_by_path and _files_by_xcfilelikeelement dicts.
1790
+
1791
+ If path is specified, then it is the path that is being added to the
1792
+ phase, and pbxbuildfile must contain either a PBXFileReference directly
1793
+ referencing that path, or it must contain a PBXVariantGroup that itself
1794
+ contains a PBXFileReference referencing the path.
1795
+
1796
+ If path is not specified, either the PBXFileReference's path or the paths
1797
+ of all children of the PBXVariantGroup are taken as being added to the
1798
+ phase.
1799
+
1800
+ If the path is already present in the phase, raises an exception.
1801
+
1802
+ If the PBXFileReference or PBXVariantGroup referenced by pbxbuildfile
1803
+ are already present in the phase, referenced by a different PBXBuildFile
1804
+ object, raises an exception. This does not raise an exception when
1805
+ a PBXFileReference or PBXVariantGroup reappear and are referenced by the
1806
+ same PBXBuildFile that has already introduced them, because in the case
1807
+ of PBXVariantGroup objects, they may correspond to multiple paths that are
1808
+ not all added simultaneously. When this situation occurs, the path needs
1809
+ to be added to _files_by_path, but nothing needs to change in
1810
+ _files_by_xcfilelikeelement, and the caller should have avoided adding
1811
+ the PBXBuildFile if it is already present in the list of children.
1812
+ """
1813
+
1814
+ xcfilelikeelement = pbxbuildfile._properties['fileRef']
1815
+
1816
+ paths = []
1817
+ if path != None:
1818
+ # It's best when the caller provides the path.
1819
+ if isinstance(xcfilelikeelement, PBXVariantGroup):
1820
+ paths.append(path)
1821
+ else:
1822
+ # If the caller didn't provide a path, there can be either multiple
1823
+ # paths (PBXVariantGroup) or one.
1824
+ if isinstance(xcfilelikeelement, PBXVariantGroup):
1825
+ for variant in xcfilelikeelement._properties['children']:
1826
+ paths.append(variant.FullPath())
1827
+ else:
1828
+ paths.append(xcfilelikeelement.FullPath())
1829
+
1830
+ # Add the paths first, because if something's going to raise, the
1831
+ # messages provided by _AddPathToDict are more useful owing to its
1832
+ # having access to a real pathname and not just an object's Name().
1833
+ for a_path in paths:
1834
+ self._AddPathToDict(pbxbuildfile, a_path)
1835
+
1836
+ # If another PBXBuildFile references this XCFileLikeElement, there's a
1837
+ # problem.
1838
+ if xcfilelikeelement in self._files_by_xcfilelikeelement and \
1839
+ self._files_by_xcfilelikeelement[xcfilelikeelement] != pbxbuildfile:
1840
+ raise ValueError('Found multiple build files for ' + \
1841
+ xcfilelikeelement.Name())
1842
+ self._files_by_xcfilelikeelement[xcfilelikeelement] = pbxbuildfile
1843
+
1844
+ def AppendBuildFile(self, pbxbuildfile, path=None):
1845
+ # Callers should use this instead of calling
1846
+ # AppendProperty('files', pbxbuildfile) directly because this function
1847
+ # maintains the object's dicts. Better yet, callers can just call AddFile
1848
+ # with a pathname and not worry about building their own PBXBuildFile
1849
+ # objects.
1850
+ self.AppendProperty('files', pbxbuildfile)
1851
+ self._AddBuildFileToDicts(pbxbuildfile, path)
1852
+
1853
+ def AddFile(self, path, settings=None):
1854
+ (file_group, hierarchical) = self.FileGroup(path)
1855
+ file_ref = file_group.AddOrGetFileByPath(path, hierarchical)
1856
+
1857
+ if file_ref in self._files_by_xcfilelikeelement and \
1858
+ isinstance(file_ref, PBXVariantGroup):
1859
+ # There's already a PBXBuildFile in this phase corresponding to the
1860
+ # PBXVariantGroup. path just provides a new variant that belongs to
1861
+ # the group. Add the path to the dict.
1862
+ pbxbuildfile = self._files_by_xcfilelikeelement[file_ref]
1863
+ self._AddBuildFileToDicts(pbxbuildfile, path)
1864
+ else:
1865
+ # Add a new PBXBuildFile to get file_ref into the phase.
1866
+ if settings is None:
1867
+ pbxbuildfile = PBXBuildFile({'fileRef': file_ref})
1868
+ else:
1869
+ pbxbuildfile = PBXBuildFile({'fileRef': file_ref, 'settings': settings})
1870
+ self.AppendBuildFile(pbxbuildfile, path)
1871
+
1872
+
1873
+ class PBXHeadersBuildPhase(XCBuildPhase):
1874
+ # No additions to the schema relative to XCBuildPhase.
1875
+
1876
+ def Name(self):
1877
+ return 'Headers'
1878
+
1879
+ def FileGroup(self, path):
1880
+ return self.PBXProjectAncestor().RootGroupForPath(path)
1881
+
1882
+
1883
+ class PBXResourcesBuildPhase(XCBuildPhase):
1884
+ # No additions to the schema relative to XCBuildPhase.
1885
+
1886
+ def Name(self):
1887
+ return 'Resources'
1888
+
1889
+ def FileGroup(self, path):
1890
+ return self.PBXProjectAncestor().RootGroupForPath(path)
1891
+
1892
+
1893
+ class PBXSourcesBuildPhase(XCBuildPhase):
1894
+ # No additions to the schema relative to XCBuildPhase.
1895
+
1896
+ def Name(self):
1897
+ return 'Sources'
1898
+
1899
+ def FileGroup(self, path):
1900
+ return self.PBXProjectAncestor().RootGroupForPath(path)
1901
+
1902
+
1903
+ class PBXFrameworksBuildPhase(XCBuildPhase):
1904
+ # No additions to the schema relative to XCBuildPhase.
1905
+
1906
+ def Name(self):
1907
+ return 'Frameworks'
1908
+
1909
+ def FileGroup(self, path):
1910
+ (root, ext) = posixpath.splitext(path)
1911
+ if ext != '':
1912
+ ext = ext[1:].lower()
1913
+ if ext == 'o':
1914
+ # .o files are added to Xcode Frameworks phases, but conceptually aren't
1915
+ # frameworks, they're more like sources or intermediates. Redirect them
1916
+ # to show up in one of those other groups.
1917
+ return self.PBXProjectAncestor().RootGroupForPath(path)
1918
+ else:
1919
+ return (self.PBXProjectAncestor().FrameworksGroup(), False)
1920
+
1921
+
1922
+ class PBXShellScriptBuildPhase(XCBuildPhase):
1923
+ _schema = XCBuildPhase._schema.copy()
1924
+ _schema.update({
1925
+ 'inputPaths': [1, str, 0, 1, []],
1926
+ 'name': [0, str, 0, 0],
1927
+ 'outputPaths': [1, str, 0, 1, []],
1928
+ 'shellPath': [0, str, 0, 1, '/bin/sh'],
1929
+ 'shellScript': [0, str, 0, 1],
1930
+ 'showEnvVarsInLog': [0, int, 0, 0],
1931
+ })
1932
+
1933
+ def Name(self):
1934
+ if 'name' in self._properties:
1935
+ return self._properties['name']
1936
+
1937
+ return 'ShellScript'
1938
+
1939
+
1940
+ class PBXCopyFilesBuildPhase(XCBuildPhase):
1941
+ _schema = XCBuildPhase._schema.copy()
1942
+ _schema.update({
1943
+ 'dstPath': [0, str, 0, 1],
1944
+ 'dstSubfolderSpec': [0, int, 0, 1],
1945
+ 'name': [0, str, 0, 0],
1946
+ })
1947
+
1948
+ # path_tree_re matches "$(DIR)/path" or just "$(DIR)". Match group 1 is
1949
+ # "DIR", match group 3 is "path" or None.
1950
+ path_tree_re = re.compile('^\\$\\((.*)\\)(/(.*)|)$')
1951
+
1952
+ # path_tree_to_subfolder maps names of Xcode variables to the associated
1953
+ # dstSubfolderSpec property value used in a PBXCopyFilesBuildPhase object.
1954
+ path_tree_to_subfolder = {
1955
+ 'BUILT_FRAMEWORKS_DIR': 10, # Frameworks Directory
1956
+ 'BUILT_PRODUCTS_DIR': 16, # Products Directory
1957
+ # Other types that can be chosen via the Xcode UI.
1958
+ # TODO(mark): Map Xcode variable names to these.
1959
+ # : 1, # Wrapper
1960
+ # : 6, # Executables: 6
1961
+ # : 7, # Resources
1962
+ # : 15, # Java Resources
1963
+ # : 11, # Shared Frameworks
1964
+ # : 12, # Shared Support
1965
+ # : 13, # PlugIns
1966
+ }
1967
+
1968
+ def Name(self):
1969
+ if 'name' in self._properties:
1970
+ return self._properties['name']
1971
+
1972
+ return 'CopyFiles'
1973
+
1974
+ def FileGroup(self, path):
1975
+ return self.PBXProjectAncestor().RootGroupForPath(path)
1976
+
1977
+ def SetDestination(self, path):
1978
+ """Set the dstSubfolderSpec and dstPath properties from path.
1979
+
1980
+ path may be specified in the same notation used for XCHierarchicalElements,
1981
+ specifically, "$(DIR)/path".
1982
+ """
1983
+
1984
+ path_tree_match = self.path_tree_re.search(path)
1985
+ if path_tree_match:
1986
+ # Everything else needs to be relative to an Xcode variable.
1987
+ path_tree = path_tree_match.group(1)
1988
+ relative_path = path_tree_match.group(3)
1989
+
1990
+ if path_tree in self.path_tree_to_subfolder:
1991
+ subfolder = self.path_tree_to_subfolder[path_tree]
1992
+ if relative_path is None:
1993
+ relative_path = ''
1994
+ else:
1995
+ # The path starts with an unrecognized Xcode variable
1996
+ # name like $(SRCROOT). Xcode will still handle this
1997
+ # as an "absolute path" that starts with the variable.
1998
+ subfolder = 0
1999
+ relative_path = path
2000
+ elif path.startswith('/'):
2001
+ # Special case. Absolute paths are in dstSubfolderSpec 0.
2002
+ subfolder = 0
2003
+ relative_path = path[1:]
2004
+ else:
2005
+ raise ValueError('Can\'t use path %s in a %s' % \
2006
+ (path, self.__class__.__name__))
2007
+
2008
+ self._properties['dstPath'] = relative_path
2009
+ self._properties['dstSubfolderSpec'] = subfolder
2010
+
2011
+
2012
+ class PBXBuildRule(XCObject):
2013
+ _schema = XCObject._schema.copy()
2014
+ _schema.update({
2015
+ 'compilerSpec': [0, str, 0, 1],
2016
+ 'filePatterns': [0, str, 0, 0],
2017
+ 'fileType': [0, str, 0, 1],
2018
+ 'isEditable': [0, int, 0, 1, 1],
2019
+ 'outputFiles': [1, str, 0, 1, []],
2020
+ 'script': [0, str, 0, 0],
2021
+ })
2022
+
2023
+ def Name(self):
2024
+ # Not very inspired, but it's what Xcode uses.
2025
+ return self.__class__.__name__
2026
+
2027
+ def Hashables(self):
2028
+ # super
2029
+ hashables = XCObject.Hashables(self)
2030
+
2031
+ # Use the hashables of the weak objects that this object refers to.
2032
+ hashables.append(self._properties['fileType'])
2033
+ if 'filePatterns' in self._properties:
2034
+ hashables.append(self._properties['filePatterns'])
2035
+ return hashables
2036
+
2037
+
2038
+ class PBXContainerItemProxy(XCObject):
2039
+ # When referencing an item in this project file, containerPortal is the
2040
+ # PBXProject root object of this project file. When referencing an item in
2041
+ # another project file, containerPortal is a PBXFileReference identifying
2042
+ # the other project file.
2043
+ #
2044
+ # When serving as a proxy to an XCTarget (in this project file or another),
2045
+ # proxyType is 1. When serving as a proxy to a PBXFileReference (in another
2046
+ # project file), proxyType is 2. Type 2 is used for references to the
2047
+ # producs of the other project file's targets.
2048
+ #
2049
+ # Xcode is weird about remoteGlobalIDString. Usually, it's printed without
2050
+ # a comment, indicating that it's tracked internally simply as a string, but
2051
+ # sometimes it's printed with a comment (usually when the object is initially
2052
+ # created), indicating that it's tracked as a project file object at least
2053
+ # sometimes. This module always tracks it as an object, but contains a hack
2054
+ # to prevent it from printing the comment in the project file output. See
2055
+ # _XCKVPrint.
2056
+ _schema = XCObject._schema.copy()
2057
+ _schema.update({
2058
+ 'containerPortal': [0, XCContainerPortal, 0, 1],
2059
+ 'proxyType': [0, int, 0, 1],
2060
+ 'remoteGlobalIDString': [0, XCRemoteObject, 0, 1],
2061
+ 'remoteInfo': [0, str, 0, 1],
2062
+ })
2063
+
2064
+ def __repr__(self):
2065
+ props = self._properties
2066
+ name = '%s.gyp:%s' % (props['containerPortal'].Name(), props['remoteInfo'])
2067
+ return '<%s %r at 0x%x>' % (self.__class__.__name__, name, id(self))
2068
+
2069
+ def Name(self):
2070
+ # Admittedly not the best name, but it's what Xcode uses.
2071
+ return self.__class__.__name__
2072
+
2073
+ def Hashables(self):
2074
+ # super
2075
+ hashables = XCObject.Hashables(self)
2076
+
2077
+ # Use the hashables of the weak objects that this object refers to.
2078
+ hashables.extend(self._properties['containerPortal'].Hashables())
2079
+ hashables.extend(self._properties['remoteGlobalIDString'].Hashables())
2080
+ return hashables
2081
+
2082
+
2083
+ class PBXTargetDependency(XCObject):
2084
+ # The "target" property accepts an XCTarget object, and obviously not
2085
+ # NoneType. But XCTarget is defined below, so it can't be put into the
2086
+ # schema yet. The definition of PBXTargetDependency can't be moved below
2087
+ # XCTarget because XCTarget's own schema references PBXTargetDependency.
2088
+ # Python doesn't deal well with this circular relationship, and doesn't have
2089
+ # a real way to do forward declarations. To work around, the type of
2090
+ # the "target" property is reset below, after XCTarget is defined.
2091
+ #
2092
+ # At least one of "name" and "target" is required.
2093
+ _schema = XCObject._schema.copy()
2094
+ _schema.update({
2095
+ 'name': [0, str, 0, 0],
2096
+ 'target': [0, None.__class__, 0, 0],
2097
+ 'targetProxy': [0, PBXContainerItemProxy, 1, 1],
2098
+ })
2099
+
2100
+ def __repr__(self):
2101
+ name = self._properties.get('name') or self._properties['target'].Name()
2102
+ return '<%s %r at 0x%x>' % (self.__class__.__name__, name, id(self))
2103
+
2104
+ def Name(self):
2105
+ # Admittedly not the best name, but it's what Xcode uses.
2106
+ return self.__class__.__name__
2107
+
2108
+ def Hashables(self):
2109
+ # super
2110
+ hashables = XCObject.Hashables(self)
2111
+
2112
+ # Use the hashables of the weak objects that this object refers to.
2113
+ hashables.extend(self._properties['targetProxy'].Hashables())
2114
+ return hashables
2115
+
2116
+
2117
+ class PBXReferenceProxy(XCFileLikeElement):
2118
+ _schema = XCFileLikeElement._schema.copy()
2119
+ _schema.update({
2120
+ 'fileType': [0, str, 0, 1],
2121
+ 'path': [0, str, 0, 1],
2122
+ 'remoteRef': [0, PBXContainerItemProxy, 1, 1],
2123
+ })
2124
+
2125
+
2126
+ class XCTarget(XCRemoteObject):
2127
+ # An XCTarget is really just an XCObject, the XCRemoteObject thing is just
2128
+ # to allow PBXProject to be used in the remoteGlobalIDString property of
2129
+ # PBXContainerItemProxy.
2130
+ #
2131
+ # Setting a "name" property at instantiation may also affect "productName",
2132
+ # which may in turn affect the "PRODUCT_NAME" build setting in children of
2133
+ # "buildConfigurationList". See __init__ below.
2134
+ _schema = XCRemoteObject._schema.copy()
2135
+ _schema.update({
2136
+ 'buildConfigurationList': [0, XCConfigurationList, 1, 1,
2137
+ XCConfigurationList()],
2138
+ 'buildPhases': [1, XCBuildPhase, 1, 1, []],
2139
+ 'dependencies': [1, PBXTargetDependency, 1, 1, []],
2140
+ 'name': [0, str, 0, 1],
2141
+ 'productName': [0, str, 0, 1],
2142
+ })
2143
+
2144
+ def __init__(self, properties=None, id=None, parent=None,
2145
+ force_outdir=None, force_prefix=None, force_extension=None):
2146
+ # super
2147
+ XCRemoteObject.__init__(self, properties, id, parent)
2148
+
2149
+ # Set up additional defaults not expressed in the schema. If a "name"
2150
+ # property was supplied, set "productName" if it is not present. Also set
2151
+ # the "PRODUCT_NAME" build setting in each configuration, but only if
2152
+ # the setting is not present in any build configuration.
2153
+ if 'name' in self._properties:
2154
+ if not 'productName' in self._properties:
2155
+ self.SetProperty('productName', self._properties['name'])
2156
+
2157
+ if 'productName' in self._properties:
2158
+ if 'buildConfigurationList' in self._properties:
2159
+ configs = self._properties['buildConfigurationList']
2160
+ if configs.HasBuildSetting('PRODUCT_NAME') == 0:
2161
+ configs.SetBuildSetting('PRODUCT_NAME',
2162
+ self._properties['productName'])
2163
+
2164
+ def AddDependency(self, other):
2165
+ pbxproject = self.PBXProjectAncestor()
2166
+ other_pbxproject = other.PBXProjectAncestor()
2167
+ if pbxproject == other_pbxproject:
2168
+ # Add a dependency to another target in the same project file.
2169
+ container = PBXContainerItemProxy({'containerPortal': pbxproject,
2170
+ 'proxyType': 1,
2171
+ 'remoteGlobalIDString': other,
2172
+ 'remoteInfo': other.Name()})
2173
+ dependency = PBXTargetDependency({'target': other,
2174
+ 'targetProxy': container})
2175
+ self.AppendProperty('dependencies', dependency)
2176
+ else:
2177
+ # Add a dependency to a target in a different project file.
2178
+ other_project_ref = \
2179
+ pbxproject.AddOrGetProjectReference(other_pbxproject)[1]
2180
+ container = PBXContainerItemProxy({
2181
+ 'containerPortal': other_project_ref,
2182
+ 'proxyType': 1,
2183
+ 'remoteGlobalIDString': other,
2184
+ 'remoteInfo': other.Name(),
2185
+ })
2186
+ dependency = PBXTargetDependency({'name': other.Name(),
2187
+ 'targetProxy': container})
2188
+ self.AppendProperty('dependencies', dependency)
2189
+
2190
+ # Proxy all of these through to the build configuration list.
2191
+
2192
+ def ConfigurationNamed(self, name):
2193
+ return self._properties['buildConfigurationList'].ConfigurationNamed(name)
2194
+
2195
+ def DefaultConfiguration(self):
2196
+ return self._properties['buildConfigurationList'].DefaultConfiguration()
2197
+
2198
+ def HasBuildSetting(self, key):
2199
+ return self._properties['buildConfigurationList'].HasBuildSetting(key)
2200
+
2201
+ def GetBuildSetting(self, key):
2202
+ return self._properties['buildConfigurationList'].GetBuildSetting(key)
2203
+
2204
+ def SetBuildSetting(self, key, value):
2205
+ return self._properties['buildConfigurationList'].SetBuildSetting(key, \
2206
+ value)
2207
+
2208
+ def AppendBuildSetting(self, key, value):
2209
+ return self._properties['buildConfigurationList'].AppendBuildSetting(key, \
2210
+ value)
2211
+
2212
+ def DelBuildSetting(self, key):
2213
+ return self._properties['buildConfigurationList'].DelBuildSetting(key)
2214
+
2215
+
2216
+ # Redefine the type of the "target" property. See PBXTargetDependency._schema
2217
+ # above.
2218
+ PBXTargetDependency._schema['target'][1] = XCTarget
2219
+
2220
+
2221
+ class PBXNativeTarget(XCTarget):
2222
+ # buildPhases is overridden in the schema to be able to set defaults.
2223
+ #
2224
+ # NOTE: Contrary to most objects, it is advisable to set parent when
2225
+ # constructing PBXNativeTarget. A parent of an XCTarget must be a PBXProject
2226
+ # object. A parent reference is required for a PBXNativeTarget during
2227
+ # construction to be able to set up the target defaults for productReference,
2228
+ # because a PBXBuildFile object must be created for the target and it must
2229
+ # be added to the PBXProject's mainGroup hierarchy.
2230
+ _schema = XCTarget._schema.copy()
2231
+ _schema.update({
2232
+ 'buildPhases': [1, XCBuildPhase, 1, 1,
2233
+ [PBXSourcesBuildPhase(), PBXFrameworksBuildPhase()]],
2234
+ 'buildRules': [1, PBXBuildRule, 1, 1, []],
2235
+ 'productReference': [0, PBXFileReference, 0, 1],
2236
+ 'productType': [0, str, 0, 1],
2237
+ })
2238
+
2239
+ # Mapping from Xcode product-types to settings. The settings are:
2240
+ # filetype : used for explicitFileType in the project file
2241
+ # prefix : the prefix for the file name
2242
+ # suffix : the suffix for the file name
2243
+ _product_filetypes = {
2244
+ 'com.apple.product-type.application': ['wrapper.application',
2245
+ '', '.app'],
2246
+ 'com.apple.product-type.application.watchapp': ['wrapper.application',
2247
+ '', '.app'],
2248
+ 'com.apple.product-type.watchkit-extension': ['wrapper.app-extension',
2249
+ '', '.appex'],
2250
+ 'com.apple.product-type.app-extension': ['wrapper.app-extension',
2251
+ '', '.appex'],
2252
+ 'com.apple.product-type.bundle': ['wrapper.cfbundle',
2253
+ '', '.bundle'],
2254
+ 'com.apple.product-type.framework': ['wrapper.framework',
2255
+ '', '.framework'],
2256
+ 'com.apple.product-type.library.dynamic': ['compiled.mach-o.dylib',
2257
+ 'lib', '.dylib'],
2258
+ 'com.apple.product-type.library.static': ['archive.ar',
2259
+ 'lib', '.a'],
2260
+ 'com.apple.product-type.tool': ['compiled.mach-o.executable',
2261
+ '', ''],
2262
+ 'com.apple.product-type.bundle.unit-test': ['wrapper.cfbundle',
2263
+ '', '.xctest'],
2264
+ 'com.googlecode.gyp.xcode.bundle': ['compiled.mach-o.dylib',
2265
+ '', '.so'],
2266
+ 'com.apple.product-type.kernel-extension': ['wrapper.kext',
2267
+ '', '.kext'],
2268
+ }
2269
+
2270
+ def __init__(self, properties=None, id=None, parent=None,
2271
+ force_outdir=None, force_prefix=None, force_extension=None):
2272
+ # super
2273
+ XCTarget.__init__(self, properties, id, parent)
2274
+
2275
+ if 'productName' in self._properties and \
2276
+ 'productType' in self._properties and \
2277
+ not 'productReference' in self._properties and \
2278
+ self._properties['productType'] in self._product_filetypes:
2279
+ products_group = None
2280
+ pbxproject = self.PBXProjectAncestor()
2281
+ if pbxproject != None:
2282
+ products_group = pbxproject.ProductsGroup()
2283
+
2284
+ if products_group != None:
2285
+ (filetype, prefix, suffix) = \
2286
+ self._product_filetypes[self._properties['productType']]
2287
+ # Xcode does not have a distinct type for loadable modules that are
2288
+ # pure BSD targets (not in a bundle wrapper). GYP allows such modules
2289
+ # to be specified by setting a target type to loadable_module without
2290
+ # having mac_bundle set. These are mapped to the pseudo-product type
2291
+ # com.googlecode.gyp.xcode.bundle.
2292
+ #
2293
+ # By picking up this special type and converting it to a dynamic
2294
+ # library (com.apple.product-type.library.dynamic) with fix-ups,
2295
+ # single-file loadable modules can be produced.
2296
+ #
2297
+ # MACH_O_TYPE is changed to mh_bundle to produce the proper file type
2298
+ # (as opposed to mh_dylib). In order for linking to succeed,
2299
+ # DYLIB_CURRENT_VERSION and DYLIB_COMPATIBILITY_VERSION must be
2300
+ # cleared. They are meaningless for type mh_bundle.
2301
+ #
2302
+ # Finally, the .so extension is forcibly applied over the default
2303
+ # (.dylib), unless another forced extension is already selected.
2304
+ # .dylib is plainly wrong, and .bundle is used by loadable_modules in
2305
+ # bundle wrappers (com.apple.product-type.bundle). .so seems an odd
2306
+ # choice because it's used as the extension on many other systems that
2307
+ # don't distinguish between linkable shared libraries and non-linkable
2308
+ # loadable modules, but there's precedent: Python loadable modules on
2309
+ # Mac OS X use an .so extension.
2310
+ if self._properties['productType'] == 'com.googlecode.gyp.xcode.bundle':
2311
+ self._properties['productType'] = \
2312
+ 'com.apple.product-type.library.dynamic'
2313
+ self.SetBuildSetting('MACH_O_TYPE', 'mh_bundle')
2314
+ self.SetBuildSetting('DYLIB_CURRENT_VERSION', '')
2315
+ self.SetBuildSetting('DYLIB_COMPATIBILITY_VERSION', '')
2316
+ if force_extension is None:
2317
+ force_extension = suffix[1:]
2318
+
2319
+ if self._properties['productType'] == \
2320
+ 'com.apple.product-type-bundle.unit.test':
2321
+ if force_extension is None:
2322
+ force_extension = suffix[1:]
2323
+
2324
+ if force_extension is not None:
2325
+ # If it's a wrapper (bundle), set WRAPPER_EXTENSION.
2326
+ # Extension override.
2327
+ suffix = '.' + force_extension
2328
+ if filetype.startswith('wrapper.'):
2329
+ self.SetBuildSetting('WRAPPER_EXTENSION', force_extension)
2330
+ else:
2331
+ self.SetBuildSetting('EXECUTABLE_EXTENSION', force_extension)
2332
+
2333
+ if filetype.startswith('compiled.mach-o.executable'):
2334
+ product_name = self._properties['productName']
2335
+ product_name += suffix
2336
+ suffix = ''
2337
+ self.SetProperty('productName', product_name)
2338
+ self.SetBuildSetting('PRODUCT_NAME', product_name)
2339
+
2340
+ # Xcode handles most prefixes based on the target type, however there
2341
+ # are exceptions. If a "BSD Dynamic Library" target is added in the
2342
+ # Xcode UI, Xcode sets EXECUTABLE_PREFIX. This check duplicates that
2343
+ # behavior.
2344
+ if force_prefix is not None:
2345
+ prefix = force_prefix
2346
+ if filetype.startswith('wrapper.'):
2347
+ self.SetBuildSetting('WRAPPER_PREFIX', prefix)
2348
+ else:
2349
+ self.SetBuildSetting('EXECUTABLE_PREFIX', prefix)
2350
+
2351
+ if force_outdir is not None:
2352
+ self.SetBuildSetting('TARGET_BUILD_DIR', force_outdir)
2353
+
2354
+ # TODO(tvl): Remove the below hack.
2355
+ # http://code.google.com/p/gyp/issues/detail?id=122
2356
+
2357
+ # Some targets include the prefix in the target_name. These targets
2358
+ # really should just add a product_name setting that doesn't include
2359
+ # the prefix. For example:
2360
+ # target_name = 'libevent', product_name = 'event'
2361
+ # This check cleans up for them.
2362
+ product_name = self._properties['productName']
2363
+ prefix_len = len(prefix)
2364
+ if prefix_len and (product_name[:prefix_len] == prefix):
2365
+ product_name = product_name[prefix_len:]
2366
+ self.SetProperty('productName', product_name)
2367
+ self.SetBuildSetting('PRODUCT_NAME', product_name)
2368
+
2369
+ ref_props = {
2370
+ 'explicitFileType': filetype,
2371
+ 'includeInIndex': 0,
2372
+ 'path': prefix + product_name + suffix,
2373
+ 'sourceTree': 'BUILT_PRODUCTS_DIR',
2374
+ }
2375
+ file_ref = PBXFileReference(ref_props)
2376
+ products_group.AppendChild(file_ref)
2377
+ self.SetProperty('productReference', file_ref)
2378
+
2379
+ def GetBuildPhaseByType(self, type):
2380
+ if not 'buildPhases' in self._properties:
2381
+ return None
2382
+
2383
+ the_phase = None
2384
+ for phase in self._properties['buildPhases']:
2385
+ if isinstance(phase, type):
2386
+ # Some phases may be present in multiples in a well-formed project file,
2387
+ # but phases like PBXSourcesBuildPhase may only be present singly, and
2388
+ # this function is intended as an aid to GetBuildPhaseByType. Loop
2389
+ # over the entire list of phases and assert if more than one of the
2390
+ # desired type is found.
2391
+ assert the_phase is None
2392
+ the_phase = phase
2393
+
2394
+ return the_phase
2395
+
2396
+ def HeadersPhase(self):
2397
+ headers_phase = self.GetBuildPhaseByType(PBXHeadersBuildPhase)
2398
+ if headers_phase is None:
2399
+ headers_phase = PBXHeadersBuildPhase()
2400
+
2401
+ # The headers phase should come before the resources, sources, and
2402
+ # frameworks phases, if any.
2403
+ insert_at = len(self._properties['buildPhases'])
2404
+ for index in xrange(0, len(self._properties['buildPhases'])):
2405
+ phase = self._properties['buildPhases'][index]
2406
+ if isinstance(phase, PBXResourcesBuildPhase) or \
2407
+ isinstance(phase, PBXSourcesBuildPhase) or \
2408
+ isinstance(phase, PBXFrameworksBuildPhase):
2409
+ insert_at = index
2410
+ break
2411
+
2412
+ self._properties['buildPhases'].insert(insert_at, headers_phase)
2413
+ headers_phase.parent = self
2414
+
2415
+ return headers_phase
2416
+
2417
+ def ResourcesPhase(self):
2418
+ resources_phase = self.GetBuildPhaseByType(PBXResourcesBuildPhase)
2419
+ if resources_phase is None:
2420
+ resources_phase = PBXResourcesBuildPhase()
2421
+
2422
+ # The resources phase should come before the sources and frameworks
2423
+ # phases, if any.
2424
+ insert_at = len(self._properties['buildPhases'])
2425
+ for index in xrange(0, len(self._properties['buildPhases'])):
2426
+ phase = self._properties['buildPhases'][index]
2427
+ if isinstance(phase, PBXSourcesBuildPhase) or \
2428
+ isinstance(phase, PBXFrameworksBuildPhase):
2429
+ insert_at = index
2430
+ break
2431
+
2432
+ self._properties['buildPhases'].insert(insert_at, resources_phase)
2433
+ resources_phase.parent = self
2434
+
2435
+ return resources_phase
2436
+
2437
+ def SourcesPhase(self):
2438
+ sources_phase = self.GetBuildPhaseByType(PBXSourcesBuildPhase)
2439
+ if sources_phase is None:
2440
+ sources_phase = PBXSourcesBuildPhase()
2441
+ self.AppendProperty('buildPhases', sources_phase)
2442
+
2443
+ return sources_phase
2444
+
2445
+ def FrameworksPhase(self):
2446
+ frameworks_phase = self.GetBuildPhaseByType(PBXFrameworksBuildPhase)
2447
+ if frameworks_phase is None:
2448
+ frameworks_phase = PBXFrameworksBuildPhase()
2449
+ self.AppendProperty('buildPhases', frameworks_phase)
2450
+
2451
+ return frameworks_phase
2452
+
2453
+ def AddDependency(self, other):
2454
+ # super
2455
+ XCTarget.AddDependency(self, other)
2456
+
2457
+ static_library_type = 'com.apple.product-type.library.static'
2458
+ shared_library_type = 'com.apple.product-type.library.dynamic'
2459
+ framework_type = 'com.apple.product-type.framework'
2460
+ if isinstance(other, PBXNativeTarget) and \
2461
+ 'productType' in self._properties and \
2462
+ self._properties['productType'] != static_library_type and \
2463
+ 'productType' in other._properties and \
2464
+ (other._properties['productType'] == static_library_type or \
2465
+ ((other._properties['productType'] == shared_library_type or \
2466
+ other._properties['productType'] == framework_type) and \
2467
+ ((not other.HasBuildSetting('MACH_O_TYPE')) or
2468
+ other.GetBuildSetting('MACH_O_TYPE') != 'mh_bundle'))):
2469
+
2470
+ file_ref = other.GetProperty('productReference')
2471
+
2472
+ pbxproject = self.PBXProjectAncestor()
2473
+ other_pbxproject = other.PBXProjectAncestor()
2474
+ if pbxproject != other_pbxproject:
2475
+ other_project_product_group = \
2476
+ pbxproject.AddOrGetProjectReference(other_pbxproject)[0]
2477
+ file_ref = other_project_product_group.GetChildByRemoteObject(file_ref)
2478
+
2479
+ self.FrameworksPhase().AppendProperty('files',
2480
+ PBXBuildFile({'fileRef': file_ref}))
2481
+
2482
+
2483
+ class PBXAggregateTarget(XCTarget):
2484
+ pass
2485
+
2486
+
2487
+ class PBXProject(XCContainerPortal):
2488
+ # A PBXProject is really just an XCObject, the XCContainerPortal thing is
2489
+ # just to allow PBXProject to be used in the containerPortal property of
2490
+ # PBXContainerItemProxy.
2491
+ """
2492
+
2493
+ Attributes:
2494
+ path: "sample.xcodeproj". TODO(mark) Document me!
2495
+ _other_pbxprojects: A dictionary, keyed by other PBXProject objects. Each
2496
+ value is a reference to the dict in the
2497
+ projectReferences list associated with the keyed
2498
+ PBXProject.
2499
+ """
2500
+
2501
+ _schema = XCContainerPortal._schema.copy()
2502
+ _schema.update({
2503
+ 'attributes': [0, dict, 0, 0],
2504
+ 'buildConfigurationList': [0, XCConfigurationList, 1, 1,
2505
+ XCConfigurationList()],
2506
+ 'compatibilityVersion': [0, str, 0, 1, 'Xcode 3.2'],
2507
+ 'hasScannedForEncodings': [0, int, 0, 1, 1],
2508
+ 'mainGroup': [0, PBXGroup, 1, 1, PBXGroup()],
2509
+ 'projectDirPath': [0, str, 0, 1, ''],
2510
+ 'projectReferences': [1, dict, 0, 0],
2511
+ 'projectRoot': [0, str, 0, 1, ''],
2512
+ 'targets': [1, XCTarget, 1, 1, []],
2513
+ })
2514
+
2515
+ def __init__(self, properties=None, id=None, parent=None, path=None):
2516
+ self.path = path
2517
+ self._other_pbxprojects = {}
2518
+ # super
2519
+ return XCContainerPortal.__init__(self, properties, id, parent)
2520
+
2521
+ def Name(self):
2522
+ name = self.path
2523
+ if name[-10:] == '.xcodeproj':
2524
+ name = name[:-10]
2525
+ return posixpath.basename(name)
2526
+
2527
+ def Path(self):
2528
+ return self.path
2529
+
2530
+ def Comment(self):
2531
+ return 'Project object'
2532
+
2533
+ def Children(self):
2534
+ # super
2535
+ children = XCContainerPortal.Children(self)
2536
+
2537
+ # Add children that the schema doesn't know about. Maybe there's a more
2538
+ # elegant way around this, but this is the only case where we need to own
2539
+ # objects in a dictionary (that is itself in a list), and three lines for
2540
+ # a one-off isn't that big a deal.
2541
+ if 'projectReferences' in self._properties:
2542
+ for reference in self._properties['projectReferences']:
2543
+ children.append(reference['ProductGroup'])
2544
+
2545
+ return children
2546
+
2547
+ def PBXProjectAncestor(self):
2548
+ return self
2549
+
2550
+ def _GroupByName(self, name):
2551
+ if not 'mainGroup' in self._properties:
2552
+ self.SetProperty('mainGroup', PBXGroup())
2553
+
2554
+ main_group = self._properties['mainGroup']
2555
+ group = main_group.GetChildByName(name)
2556
+ if group is None:
2557
+ group = PBXGroup({'name': name})
2558
+ main_group.AppendChild(group)
2559
+
2560
+ return group
2561
+
2562
+ # SourceGroup and ProductsGroup are created by default in Xcode's own
2563
+ # templates.
2564
+ def SourceGroup(self):
2565
+ return self._GroupByName('Source')
2566
+
2567
+ def ProductsGroup(self):
2568
+ return self._GroupByName('Products')
2569
+
2570
+ # IntermediatesGroup is used to collect source-like files that are generated
2571
+ # by rules or script phases and are placed in intermediate directories such
2572
+ # as DerivedSources.
2573
+ def IntermediatesGroup(self):
2574
+ return self._GroupByName('Intermediates')
2575
+
2576
+ # FrameworksGroup and ProjectsGroup are top-level groups used to collect
2577
+ # frameworks and projects.
2578
+ def FrameworksGroup(self):
2579
+ return self._GroupByName('Frameworks')
2580
+
2581
+ def ProjectsGroup(self):
2582
+ return self._GroupByName('Projects')
2583
+
2584
+ def RootGroupForPath(self, path):
2585
+ """Returns a PBXGroup child of this object to which path should be added.
2586
+
2587
+ This method is intended to choose between SourceGroup and
2588
+ IntermediatesGroup on the basis of whether path is present in a source
2589
+ directory or an intermediates directory. For the purposes of this
2590
+ determination, any path located within a derived file directory such as
2591
+ PROJECT_DERIVED_FILE_DIR is treated as being in an intermediates
2592
+ directory.
2593
+
2594
+ The returned value is a two-element tuple. The first element is the
2595
+ PBXGroup, and the second element specifies whether that group should be
2596
+ organized hierarchically (True) or as a single flat list (False).
2597
+ """
2598
+
2599
+ # TODO(mark): make this a class variable and bind to self on call?
2600
+ # Also, this list is nowhere near exhaustive.
2601
+ # INTERMEDIATE_DIR and SHARED_INTERMEDIATE_DIR are used by
2602
+ # gyp.generator.xcode. There should probably be some way for that module
2603
+ # to push the names in, rather than having to hard-code them here.
2604
+ source_tree_groups = {
2605
+ 'DERIVED_FILE_DIR': (self.IntermediatesGroup, True),
2606
+ 'INTERMEDIATE_DIR': (self.IntermediatesGroup, True),
2607
+ 'PROJECT_DERIVED_FILE_DIR': (self.IntermediatesGroup, True),
2608
+ 'SHARED_INTERMEDIATE_DIR': (self.IntermediatesGroup, True),
2609
+ }
2610
+
2611
+ (source_tree, path) = SourceTreeAndPathFromPath(path)
2612
+ if source_tree != None and source_tree in source_tree_groups:
2613
+ (group_func, hierarchical) = source_tree_groups[source_tree]
2614
+ group = group_func()
2615
+ return (group, hierarchical)
2616
+
2617
+ # TODO(mark): make additional choices based on file extension.
2618
+
2619
+ return (self.SourceGroup(), True)
2620
+
2621
+ def AddOrGetFileInRootGroup(self, path):
2622
+ """Returns a PBXFileReference corresponding to path in the correct group
2623
+ according to RootGroupForPath's heuristics.
2624
+
2625
+ If an existing PBXFileReference for path exists, it will be returned.
2626
+ Otherwise, one will be created and returned.
2627
+ """
2628
+
2629
+ (group, hierarchical) = self.RootGroupForPath(path)
2630
+ return group.AddOrGetFileByPath(path, hierarchical)
2631
+
2632
+ def RootGroupsTakeOverOnlyChildren(self, recurse=False):
2633
+ """Calls TakeOverOnlyChild for all groups in the main group."""
2634
+
2635
+ for group in self._properties['mainGroup']._properties['children']:
2636
+ if isinstance(group, PBXGroup):
2637
+ group.TakeOverOnlyChild(recurse)
2638
+
2639
+ def SortGroups(self):
2640
+ # Sort the children of the mainGroup (like "Source" and "Products")
2641
+ # according to their defined order.
2642
+ self._properties['mainGroup']._properties['children'] = \
2643
+ sorted(self._properties['mainGroup']._properties['children'],
2644
+ cmp=lambda x,y: x.CompareRootGroup(y))
2645
+
2646
+ # Sort everything else by putting group before files, and going
2647
+ # alphabetically by name within sections of groups and files. SortGroup
2648
+ # is recursive.
2649
+ for group in self._properties['mainGroup']._properties['children']:
2650
+ if not isinstance(group, PBXGroup):
2651
+ continue
2652
+
2653
+ if group.Name() == 'Products':
2654
+ # The Products group is a special case. Instead of sorting
2655
+ # alphabetically, sort things in the order of the targets that
2656
+ # produce the products. To do this, just build up a new list of
2657
+ # products based on the targets.
2658
+ products = []
2659
+ for target in self._properties['targets']:
2660
+ if not isinstance(target, PBXNativeTarget):
2661
+ continue
2662
+ product = target._properties['productReference']
2663
+ # Make sure that the product is already in the products group.
2664
+ assert product in group._properties['children']
2665
+ products.append(product)
2666
+
2667
+ # Make sure that this process doesn't miss anything that was already
2668
+ # in the products group.
2669
+ assert len(products) == len(group._properties['children'])
2670
+ group._properties['children'] = products
2671
+ else:
2672
+ group.SortGroup()
2673
+
2674
+ def AddOrGetProjectReference(self, other_pbxproject):
2675
+ """Add a reference to another project file (via PBXProject object) to this
2676
+ one.
2677
+
2678
+ Returns [ProductGroup, ProjectRef]. ProductGroup is a PBXGroup object in
2679
+ this project file that contains a PBXReferenceProxy object for each
2680
+ product of each PBXNativeTarget in the other project file. ProjectRef is
2681
+ a PBXFileReference to the other project file.
2682
+
2683
+ If this project file already references the other project file, the
2684
+ existing ProductGroup and ProjectRef are returned. The ProductGroup will
2685
+ still be updated if necessary.
2686
+ """
2687
+
2688
+ if not 'projectReferences' in self._properties:
2689
+ self._properties['projectReferences'] = []
2690
+
2691
+ product_group = None
2692
+ project_ref = None
2693
+
2694
+ if not other_pbxproject in self._other_pbxprojects:
2695
+ # This project file isn't yet linked to the other one. Establish the
2696
+ # link.
2697
+ product_group = PBXGroup({'name': 'Products'})
2698
+
2699
+ # ProductGroup is strong.
2700
+ product_group.parent = self
2701
+
2702
+ # There's nothing unique about this PBXGroup, and if left alone, it will
2703
+ # wind up with the same set of hashables as all other PBXGroup objects
2704
+ # owned by the projectReferences list. Add the hashables of the
2705
+ # remote PBXProject that it's related to.
2706
+ product_group._hashables.extend(other_pbxproject.Hashables())
2707
+
2708
+ # The other project reports its path as relative to the same directory
2709
+ # that this project's path is relative to. The other project's path
2710
+ # is not necessarily already relative to this project. Figure out the
2711
+ # pathname that this project needs to use to refer to the other one.
2712
+ this_path = posixpath.dirname(self.Path())
2713
+ projectDirPath = self.GetProperty('projectDirPath')
2714
+ if projectDirPath:
2715
+ if posixpath.isabs(projectDirPath[0]):
2716
+ this_path = projectDirPath
2717
+ else:
2718
+ this_path = posixpath.join(this_path, projectDirPath)
2719
+ other_path = gyp.common.RelativePath(other_pbxproject.Path(), this_path)
2720
+
2721
+ # ProjectRef is weak (it's owned by the mainGroup hierarchy).
2722
+ project_ref = PBXFileReference({
2723
+ 'lastKnownFileType': 'wrapper.pb-project',
2724
+ 'path': other_path,
2725
+ 'sourceTree': 'SOURCE_ROOT',
2726
+ })
2727
+ self.ProjectsGroup().AppendChild(project_ref)
2728
+
2729
+ ref_dict = {'ProductGroup': product_group, 'ProjectRef': project_ref}
2730
+ self._other_pbxprojects[other_pbxproject] = ref_dict
2731
+ self.AppendProperty('projectReferences', ref_dict)
2732
+
2733
+ # Xcode seems to sort this list case-insensitively
2734
+ self._properties['projectReferences'] = \
2735
+ sorted(self._properties['projectReferences'], cmp=lambda x,y:
2736
+ cmp(x['ProjectRef'].Name().lower(),
2737
+ y['ProjectRef'].Name().lower()))
2738
+ else:
2739
+ # The link already exists. Pull out the relevnt data.
2740
+ project_ref_dict = self._other_pbxprojects[other_pbxproject]
2741
+ product_group = project_ref_dict['ProductGroup']
2742
+ project_ref = project_ref_dict['ProjectRef']
2743
+
2744
+ self._SetUpProductReferences(other_pbxproject, product_group, project_ref)
2745
+
2746
+ inherit_unique_symroot = self._AllSymrootsUnique(other_pbxproject, False)
2747
+ targets = other_pbxproject.GetProperty('targets')
2748
+ if all(self._AllSymrootsUnique(t, inherit_unique_symroot) for t in targets):
2749
+ dir_path = project_ref._properties['path']
2750
+ product_group._hashables.extend(dir_path)
2751
+
2752
+ return [product_group, project_ref]
2753
+
2754
+ def _AllSymrootsUnique(self, target, inherit_unique_symroot):
2755
+ # Returns True if all configurations have a unique 'SYMROOT' attribute.
2756
+ # The value of inherit_unique_symroot decides, if a configuration is assumed
2757
+ # to inherit a unique 'SYMROOT' attribute from its parent, if it doesn't
2758
+ # define an explicit value for 'SYMROOT'.
2759
+ symroots = self._DefinedSymroots(target)
2760
+ for s in self._DefinedSymroots(target):
2761
+ if (s is not None and not self._IsUniqueSymrootForTarget(s) or
2762
+ s is None and not inherit_unique_symroot):
2763
+ return False
2764
+ return True if symroots else inherit_unique_symroot
2765
+
2766
+ def _DefinedSymroots(self, target):
2767
+ # Returns all values for the 'SYMROOT' attribute defined in all
2768
+ # configurations for this target. If any configuration doesn't define the
2769
+ # 'SYMROOT' attribute, None is added to the returned set. If all
2770
+ # configurations don't define the 'SYMROOT' attribute, an empty set is
2771
+ # returned.
2772
+ config_list = target.GetProperty('buildConfigurationList')
2773
+ symroots = set()
2774
+ for config in config_list.GetProperty('buildConfigurations'):
2775
+ setting = config.GetProperty('buildSettings')
2776
+ if 'SYMROOT' in setting:
2777
+ symroots.add(setting['SYMROOT'])
2778
+ else:
2779
+ symroots.add(None)
2780
+ if len(symroots) == 1 and None in symroots:
2781
+ return set()
2782
+ return symroots
2783
+
2784
+ def _IsUniqueSymrootForTarget(self, symroot):
2785
+ # This method returns True if all configurations in target contain a
2786
+ # 'SYMROOT' attribute that is unique for the given target. A value is
2787
+ # unique, if the Xcode macro '$SRCROOT' appears in it in any form.
2788
+ uniquifier = ['$SRCROOT', '$(SRCROOT)']
2789
+ if any(x in symroot for x in uniquifier):
2790
+ return True
2791
+ return False
2792
+
2793
+ def _SetUpProductReferences(self, other_pbxproject, product_group,
2794
+ project_ref):
2795
+ # TODO(mark): This only adds references to products in other_pbxproject
2796
+ # when they don't exist in this pbxproject. Perhaps it should also
2797
+ # remove references from this pbxproject that are no longer present in
2798
+ # other_pbxproject. Perhaps it should update various properties if they
2799
+ # change.
2800
+ for target in other_pbxproject._properties['targets']:
2801
+ if not isinstance(target, PBXNativeTarget):
2802
+ continue
2803
+
2804
+ other_fileref = target._properties['productReference']
2805
+ if product_group.GetChildByRemoteObject(other_fileref) is None:
2806
+ # Xcode sets remoteInfo to the name of the target and not the name
2807
+ # of its product, despite this proxy being a reference to the product.
2808
+ container_item = PBXContainerItemProxy({
2809
+ 'containerPortal': project_ref,
2810
+ 'proxyType': 2,
2811
+ 'remoteGlobalIDString': other_fileref,
2812
+ 'remoteInfo': target.Name()
2813
+ })
2814
+ # TODO(mark): Does sourceTree get copied straight over from the other
2815
+ # project? Can the other project ever have lastKnownFileType here
2816
+ # instead of explicitFileType? (Use it if so?) Can path ever be
2817
+ # unset? (I don't think so.) Can other_fileref have name set, and
2818
+ # does it impact the PBXReferenceProxy if so? These are the questions
2819
+ # that perhaps will be answered one day.
2820
+ reference_proxy = PBXReferenceProxy({
2821
+ 'fileType': other_fileref._properties['explicitFileType'],
2822
+ 'path': other_fileref._properties['path'],
2823
+ 'sourceTree': other_fileref._properties['sourceTree'],
2824
+ 'remoteRef': container_item,
2825
+ })
2826
+
2827
+ product_group.AppendChild(reference_proxy)
2828
+
2829
+ def SortRemoteProductReferences(self):
2830
+ # For each remote project file, sort the associated ProductGroup in the
2831
+ # same order that the targets are sorted in the remote project file. This
2832
+ # is the sort order used by Xcode.
2833
+
2834
+ def CompareProducts(x, y, remote_products):
2835
+ # x and y are PBXReferenceProxy objects. Go through their associated
2836
+ # PBXContainerItem to get the remote PBXFileReference, which will be
2837
+ # present in the remote_products list.
2838
+ x_remote = x._properties['remoteRef']._properties['remoteGlobalIDString']
2839
+ y_remote = y._properties['remoteRef']._properties['remoteGlobalIDString']
2840
+ x_index = remote_products.index(x_remote)
2841
+ y_index = remote_products.index(y_remote)
2842
+
2843
+ # Use the order of each remote PBXFileReference in remote_products to
2844
+ # determine the sort order.
2845
+ return cmp(x_index, y_index)
2846
+
2847
+ for other_pbxproject, ref_dict in self._other_pbxprojects.iteritems():
2848
+ # Build up a list of products in the remote project file, ordered the
2849
+ # same as the targets that produce them.
2850
+ remote_products = []
2851
+ for target in other_pbxproject._properties['targets']:
2852
+ if not isinstance(target, PBXNativeTarget):
2853
+ continue
2854
+ remote_products.append(target._properties['productReference'])
2855
+
2856
+ # Sort the PBXReferenceProxy children according to the list of remote
2857
+ # products.
2858
+ product_group = ref_dict['ProductGroup']
2859
+ product_group._properties['children'] = sorted(
2860
+ product_group._properties['children'],
2861
+ cmp=lambda x, y, rp=remote_products: CompareProducts(x, y, rp))
2862
+
2863
+
2864
+ class XCProjectFile(XCObject):
2865
+ _schema = XCObject._schema.copy()
2866
+ _schema.update({
2867
+ 'archiveVersion': [0, int, 0, 1, 1],
2868
+ 'classes': [0, dict, 0, 1, {}],
2869
+ 'objectVersion': [0, int, 0, 1, 46],
2870
+ 'rootObject': [0, PBXProject, 1, 1],
2871
+ })
2872
+
2873
+ def ComputeIDs(self, recursive=True, overwrite=True, hash=None):
2874
+ # Although XCProjectFile is implemented here as an XCObject, it's not a
2875
+ # proper object in the Xcode sense, and it certainly doesn't have its own
2876
+ # ID. Pass through an attempt to update IDs to the real root object.
2877
+ if recursive:
2878
+ self._properties['rootObject'].ComputeIDs(recursive, overwrite, hash)
2879
+
2880
+ def Print(self, file=sys.stdout):
2881
+ self.VerifyHasRequiredProperties()
2882
+
2883
+ # Add the special "objects" property, which will be caught and handled
2884
+ # separately during printing. This structure allows a fairly standard
2885
+ # loop do the normal printing.
2886
+ self._properties['objects'] = {}
2887
+ self._XCPrint(file, 0, '// !$*UTF8*$!\n')
2888
+ if self._should_print_single_line:
2889
+ self._XCPrint(file, 0, '{ ')
2890
+ else:
2891
+ self._XCPrint(file, 0, '{\n')
2892
+ for property, value in sorted(self._properties.iteritems(),
2893
+ cmp=lambda x, y: cmp(x, y)):
2894
+ if property == 'objects':
2895
+ self._PrintObjects(file)
2896
+ else:
2897
+ self._XCKVPrint(file, 1, property, value)
2898
+ self._XCPrint(file, 0, '}\n')
2899
+ del self._properties['objects']
2900
+
2901
+ def _PrintObjects(self, file):
2902
+ if self._should_print_single_line:
2903
+ self._XCPrint(file, 0, 'objects = {')
2904
+ else:
2905
+ self._XCPrint(file, 1, 'objects = {\n')
2906
+
2907
+ objects_by_class = {}
2908
+ for object in self.Descendants():
2909
+ if object == self:
2910
+ continue
2911
+ class_name = object.__class__.__name__
2912
+ if not class_name in objects_by_class:
2913
+ objects_by_class[class_name] = []
2914
+ objects_by_class[class_name].append(object)
2915
+
2916
+ for class_name in sorted(objects_by_class):
2917
+ self._XCPrint(file, 0, '\n')
2918
+ self._XCPrint(file, 0, '/* Begin ' + class_name + ' section */\n')
2919
+ for object in sorted(objects_by_class[class_name],
2920
+ cmp=lambda x, y: cmp(x.id, y.id)):
2921
+ object.Print(file)
2922
+ self._XCPrint(file, 0, '/* End ' + class_name + ' section */\n')
2923
+
2924
+ if self._should_print_single_line:
2925
+ self._XCPrint(file, 0, '}; ')
2926
+ else:
2927
+ self._XCPrint(file, 1, '};\n')