xcres 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +33 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +85 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +181 -0
  8. data/Rakefile +58 -0
  9. data/bin/xcres +12 -0
  10. data/lib/xcres/analyzer/aggregate_analyzer.rb +48 -0
  11. data/lib/xcres/analyzer/analyzer.rb +141 -0
  12. data/lib/xcres/analyzer/resources_aggregate_analyzer.rb +27 -0
  13. data/lib/xcres/analyzer/resources_analyzer/base_resources_analyzer.rb +133 -0
  14. data/lib/xcres/analyzer/resources_analyzer/bundle_resources_analyzer.rb +68 -0
  15. data/lib/xcres/analyzer/resources_analyzer/loose_resources_analyzer.rb +39 -0
  16. data/lib/xcres/analyzer/resources_analyzer/xcassets_analyzer.rb +62 -0
  17. data/lib/xcres/analyzer/strings_analyzer.rb +255 -0
  18. data/lib/xcres/builder/file_builder.rb +61 -0
  19. data/lib/xcres/builder/resources_builder.rb +184 -0
  20. data/lib/xcres/builder/string_builder.rb +31 -0
  21. data/lib/xcres/command/build_command.rb +83 -0
  22. data/lib/xcres/command/command.rb +58 -0
  23. data/lib/xcres/command/install_command.rb +173 -0
  24. data/lib/xcres/command/main_command.rb +14 -0
  25. data/lib/xcres/command/project_command.rb +111 -0
  26. data/lib/xcres/command/version_command.rb +12 -0
  27. data/lib/xcres/helper/file_helper.rb +16 -0
  28. data/lib/xcres/logger.rb +148 -0
  29. data/lib/xcres/model/section.rb +43 -0
  30. data/lib/xcres/model/xcassets/bundle.rb +55 -0
  31. data/lib/xcres/model/xcassets/resource.rb +141 -0
  32. data/lib/xcres/model/xcassets/resource_image.rb +134 -0
  33. data/lib/xcres/version.rb +9 -0
  34. data/lib/xcres.rb +9 -0
  35. data/spec/fixtures/Example/Example/AppDelegate.h +15 -0
  36. data/spec/fixtures/Example/Example/AppDelegate.m +49 -0
  37. data/spec/fixtures/Example/Example/Example-Info.plist +38 -0
  38. data/spec/fixtures/Example/Example/Example-Prefix.pch +16 -0
  39. data/spec/fixtures/Example/Example/Images/doge.jpeg +0 -0
  40. data/spec/fixtures/Example/Example/Images/nyanCat.png +0 -0
  41. data/spec/fixtures/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  42. data/spec/fixtures/Example/Example/Images.xcassets/Doge.imageset/Contents.json +18 -0
  43. data/spec/fixtures/Example/Example/Images.xcassets/Doge.imageset/doge.png +0 -0
  44. data/spec/fixtures/Example/Example/Images.xcassets/Doge.imageset/doge@2x.png +0 -0
  45. data/spec/fixtures/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  46. data/spec/fixtures/Example/Example/de.lproj/Localizable.strings +12 -0
  47. data/spec/fixtures/Example/Example/en.lproj/InfoPlist.strings +2 -0
  48. data/spec/fixtures/Example/Example/en.lproj/Localizable.strings +15 -0
  49. data/spec/fixtures/Example/Example/main.m +18 -0
  50. data/spec/fixtures/Example/Example.xcodeproj/project.pbxproj +449 -0
  51. data/spec/fixtures/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  52. data/spec/fixtures/ExampleOSX/ExampleOSX/AppDelegate.h +15 -0
  53. data/spec/fixtures/ExampleOSX/ExampleOSX/AppDelegate.m +18 -0
  54. data/spec/fixtures/ExampleOSX/ExampleOSX/Base.lproj/MainMenu.xib +680 -0
  55. data/spec/fixtures/ExampleOSX/ExampleOSX/ExampleOSX-Info.plist +34 -0
  56. data/spec/fixtures/ExampleOSX/ExampleOSX/ExampleOSX-Prefix.pch +9 -0
  57. data/spec/fixtures/ExampleOSX/ExampleOSX/Images.xcassets/AppIcon.appiconset/Contents.json +58 -0
  58. data/spec/fixtures/ExampleOSX/ExampleOSX/en.lproj/Credits.rtf +29 -0
  59. data/spec/fixtures/ExampleOSX/ExampleOSX/en.lproj/InfoPlist.strings +2 -0
  60. data/spec/fixtures/ExampleOSX/ExampleOSX/main.m +14 -0
  61. data/spec/fixtures/ExampleOSX/ExampleOSX.xcodeproj/project.pbxproj +481 -0
  62. data/spec/fixtures/ExampleOSX/ExampleOSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  63. data/spec/fixtures/ExampleOSX/ExampleOSXTests/ExampleOSXTests-Info.plist +22 -0
  64. data/spec/fixtures/ExampleOSX/ExampleOSXTests/ExampleOSXTests.m +34 -0
  65. data/spec/fixtures/ExampleOSX/ExampleOSXTests/en.lproj/InfoPlist.strings +2 -0
  66. data/spec/integration/build/after/Example/Example/AppDelegate.h +15 -0
  67. data/spec/integration/build/after/Example/Example/AppDelegate.m +49 -0
  68. data/spec/integration/build/after/Example/Example/Example-Info.plist +38 -0
  69. data/spec/integration/build/after/Example/Example/Example-Prefix.pch +16 -0
  70. data/spec/integration/build/after/Example/Example/Icons.bundle/tab_bar/tabbar_list.png +0 -0
  71. data/spec/integration/build/after/Example/Example/Icons.bundle/tab_bar/tabbar_list@2x.png +0 -0
  72. data/spec/integration/build/after/Example/Example/Icons.bundle/tab_bar/tabbar_map.png +0 -0
  73. data/spec/integration/build/after/Example/Example/Icons.bundle/tab_bar/tabbar_map@2x.png +0 -0
  74. data/spec/integration/build/after/Example/Example/Images/doge.jpeg +0 -0
  75. data/spec/integration/build/after/Example/Example/Images/nyanCat.png +0 -0
  76. data/spec/integration/build/after/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  77. data/spec/integration/build/after/Example/Example/Images.xcassets/Doge.imageset/Contents.json +18 -0
  78. data/spec/integration/build/after/Example/Example/Images.xcassets/Doge.imageset/doge.png +0 -0
  79. data/spec/integration/build/after/Example/Example/Images.xcassets/Doge.imageset/doge@2x.png +0 -0
  80. data/spec/integration/build/after/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  81. data/spec/integration/build/after/Example/Example/de.lproj/Localizable.strings +12 -0
  82. data/spec/integration/build/after/Example/Example/en.lproj/InfoPlist.strings +2 -0
  83. data/spec/integration/build/after/Example/Example/en.lproj/Localizable.strings +15 -0
  84. data/spec/integration/build/after/Example/Example/main.m +18 -0
  85. data/spec/integration/build/after/Example/Example.xcodeproj.yaml +168 -0
  86. data/spec/integration/build/after/R.h +39 -0
  87. data/spec/integration/build/after/R.m +29 -0
  88. data/spec/integration/build/after/execution_output.txt +19 -0
  89. data/spec/integration/build/before/Example/Example/AppDelegate.h +15 -0
  90. data/spec/integration/build/before/Example/Example/AppDelegate.m +49 -0
  91. data/spec/integration/build/before/Example/Example/Example-Info.plist +38 -0
  92. data/spec/integration/build/before/Example/Example/Example-Prefix.pch +16 -0
  93. data/spec/integration/build/before/Example/Example/Icons.bundle/tab_bar/tabbar_list.png +0 -0
  94. data/spec/integration/build/before/Example/Example/Icons.bundle/tab_bar/tabbar_list@2x.png +0 -0
  95. data/spec/integration/build/before/Example/Example/Icons.bundle/tab_bar/tabbar_map.png +0 -0
  96. data/spec/integration/build/before/Example/Example/Icons.bundle/tab_bar/tabbar_map@2x.png +0 -0
  97. data/spec/integration/build/before/Example/Example/Images/doge.jpeg +0 -0
  98. data/spec/integration/build/before/Example/Example/Images/nyanCat.png +0 -0
  99. data/spec/integration/build/before/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  100. data/spec/integration/build/before/Example/Example/Images.xcassets/Doge.imageset/Contents.json +18 -0
  101. data/spec/integration/build/before/Example/Example/Images.xcassets/Doge.imageset/doge.png +0 -0
  102. data/spec/integration/build/before/Example/Example/Images.xcassets/Doge.imageset/doge@2x.png +0 -0
  103. data/spec/integration/build/before/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  104. data/spec/integration/build/before/Example/Example/de.lproj/Localizable.strings +12 -0
  105. data/spec/integration/build/before/Example/Example/en.lproj/InfoPlist.strings +2 -0
  106. data/spec/integration/build/before/Example/Example/en.lproj/Localizable.strings +15 -0
  107. data/spec/integration/build/before/Example/Example/main.m +18 -0
  108. data/spec/integration/build/before/Example/Example.xcodeproj/project.pbxproj +453 -0
  109. data/spec/integration/build/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  110. data/spec/integration/build-keyword-clash/after/Example/Example/AppDelegate.h +15 -0
  111. data/spec/integration/build-keyword-clash/after/Example/Example/AppDelegate.m +49 -0
  112. data/spec/integration/build-keyword-clash/after/Example/Example/Default-568h@2x.png +0 -0
  113. data/spec/integration/build-keyword-clash/after/Example/Example/Default.png +0 -0
  114. data/spec/integration/build-keyword-clash/after/Example/Example/Default@2x.png +0 -0
  115. data/spec/integration/build-keyword-clash/after/Example/Example/Example-Info.plist +38 -0
  116. data/spec/integration/build-keyword-clash/after/Example/Example/Example-Prefix.pch +16 -0
  117. data/spec/integration/build-keyword-clash/after/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  118. data/spec/integration/build-keyword-clash/after/Example/Example/en.lproj/InfoPlist.strings +2 -0
  119. data/spec/integration/build-keyword-clash/after/Example/Example/main.m +18 -0
  120. data/spec/integration/build-keyword-clash/after/Example/Example.xcodeproj.yaml +160 -0
  121. data/spec/integration/build-keyword-clash/after/R.h +15 -0
  122. data/spec/integration/build-keyword-clash/after/R.m +15 -0
  123. data/spec/integration/build-keyword-clash/after/execution_output.txt +14 -0
  124. data/spec/integration/build-keyword-clash/before/Example/Example/AppDelegate.h +15 -0
  125. data/spec/integration/build-keyword-clash/before/Example/Example/AppDelegate.m +49 -0
  126. data/spec/integration/build-keyword-clash/before/Example/Example/Default-568h@2x.png +0 -0
  127. data/spec/integration/build-keyword-clash/before/Example/Example/Default.png +0 -0
  128. data/spec/integration/build-keyword-clash/before/Example/Example/Default@2x.png +0 -0
  129. data/spec/integration/build-keyword-clash/before/Example/Example/Example-Info.plist +38 -0
  130. data/spec/integration/build-keyword-clash/before/Example/Example/Example-Prefix.pch +16 -0
  131. data/spec/integration/build-keyword-clash/before/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  132. data/spec/integration/build-keyword-clash/before/Example/Example/en.lproj/InfoPlist.strings +2 -0
  133. data/spec/integration/build-keyword-clash/before/Example/Example/main.m +18 -0
  134. data/spec/integration/build-keyword-clash/before/Example/Example.xcodeproj/project.pbxproj +427 -0
  135. data/spec/integration/build-keyword-clash/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  136. data/spec/integration/build-var-infoplist/after/Example/Example/AppDelegate.h +15 -0
  137. data/spec/integration/build-var-infoplist/after/Example/Example/AppDelegate.m +49 -0
  138. data/spec/integration/build-var-infoplist/after/Example/Example/Example-Info.plist +38 -0
  139. data/spec/integration/build-var-infoplist/after/Example/Example/Example-Prefix.pch +16 -0
  140. data/spec/integration/build-var-infoplist/after/Example/Example/Icons.bundle/tab_bar/tabbar_list.png +0 -0
  141. data/spec/integration/build-var-infoplist/after/Example/Example/Icons.bundle/tab_bar/tabbar_list@2x.png +0 -0
  142. data/spec/integration/build-var-infoplist/after/Example/Example/Icons.bundle/tab_bar/tabbar_map.png +0 -0
  143. data/spec/integration/build-var-infoplist/after/Example/Example/Icons.bundle/tab_bar/tabbar_map@2x.png +0 -0
  144. data/spec/integration/build-var-infoplist/after/Example/Example/Images/doge.jpeg +0 -0
  145. data/spec/integration/build-var-infoplist/after/Example/Example/Images/nyanCat.png +0 -0
  146. data/spec/integration/build-var-infoplist/after/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  147. data/spec/integration/build-var-infoplist/after/Example/Example/Images.xcassets/Doge.imageset/Contents.json +18 -0
  148. data/spec/integration/build-var-infoplist/after/Example/Example/Images.xcassets/Doge.imageset/doge.png +0 -0
  149. data/spec/integration/build-var-infoplist/after/Example/Example/Images.xcassets/Doge.imageset/doge@2x.png +0 -0
  150. data/spec/integration/build-var-infoplist/after/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  151. data/spec/integration/build-var-infoplist/after/Example/Example/de.lproj/Localizable.strings +12 -0
  152. data/spec/integration/build-var-infoplist/after/Example/Example/en.lproj/InfoPlist.strings +2 -0
  153. data/spec/integration/build-var-infoplist/after/Example/Example/en.lproj/Localizable.strings +15 -0
  154. data/spec/integration/build-var-infoplist/after/Example/Example/main.m +18 -0
  155. data/spec/integration/build-var-infoplist/after/Example/Example.xcodeproj.yaml +168 -0
  156. data/spec/integration/build-var-infoplist/after/R.h +39 -0
  157. data/spec/integration/build-var-infoplist/after/R.m +29 -0
  158. data/spec/integration/build-var-infoplist/after/execution_output.txt +19 -0
  159. data/spec/integration/build-var-infoplist/before/Example/Example/AppDelegate.h +15 -0
  160. data/spec/integration/build-var-infoplist/before/Example/Example/AppDelegate.m +49 -0
  161. data/spec/integration/build-var-infoplist/before/Example/Example/Example-Info.plist +38 -0
  162. data/spec/integration/build-var-infoplist/before/Example/Example/Example-Prefix.pch +16 -0
  163. data/spec/integration/build-var-infoplist/before/Example/Example/Icons.bundle/tab_bar/tabbar_list.png +0 -0
  164. data/spec/integration/build-var-infoplist/before/Example/Example/Icons.bundle/tab_bar/tabbar_list@2x.png +0 -0
  165. data/spec/integration/build-var-infoplist/before/Example/Example/Icons.bundle/tab_bar/tabbar_map.png +0 -0
  166. data/spec/integration/build-var-infoplist/before/Example/Example/Icons.bundle/tab_bar/tabbar_map@2x.png +0 -0
  167. data/spec/integration/build-var-infoplist/before/Example/Example/Images/doge.jpeg +0 -0
  168. data/spec/integration/build-var-infoplist/before/Example/Example/Images/nyanCat.png +0 -0
  169. data/spec/integration/build-var-infoplist/before/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  170. data/spec/integration/build-var-infoplist/before/Example/Example/Images.xcassets/Doge.imageset/Contents.json +18 -0
  171. data/spec/integration/build-var-infoplist/before/Example/Example/Images.xcassets/Doge.imageset/doge.png +0 -0
  172. data/spec/integration/build-var-infoplist/before/Example/Example/Images.xcassets/Doge.imageset/doge@2x.png +0 -0
  173. data/spec/integration/build-var-infoplist/before/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  174. data/spec/integration/build-var-infoplist/before/Example/Example/de.lproj/Localizable.strings +12 -0
  175. data/spec/integration/build-var-infoplist/before/Example/Example/en.lproj/InfoPlist.strings +2 -0
  176. data/spec/integration/build-var-infoplist/before/Example/Example/en.lproj/Localizable.strings +15 -0
  177. data/spec/integration/build-var-infoplist/before/Example/Example/main.m +18 -0
  178. data/spec/integration/build-var-infoplist/before/Example/Example.xcodeproj/project.pbxproj +453 -0
  179. data/spec/integration/build-var-infoplist/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  180. data/spec/integration/help/after/execution_output.txt +18 -0
  181. data/spec/integration/help/before/.gitkeep +0 -0
  182. data/spec/integration/install/after/Example/Example/AppDelegate.h +15 -0
  183. data/spec/integration/install/after/Example/Example/AppDelegate.m +49 -0
  184. data/spec/integration/install/after/Example/Example/Example-Info.plist +38 -0
  185. data/spec/integration/install/after/Example/Example/Example-Prefix.pch +18 -0
  186. data/spec/integration/install/after/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  187. data/spec/integration/install/after/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  188. data/spec/integration/install/after/Example/Example/Resources/R.h +19 -0
  189. data/spec/integration/install/after/Example/Example/Resources/R.m +17 -0
  190. data/spec/integration/install/after/Example/Example/en.lproj/InfoPlist.strings +2 -0
  191. data/spec/integration/install/after/Example/Example/main.m +18 -0
  192. data/spec/integration/install/after/Example/Example.xcodeproj/project.pbxproj +898 -0
  193. data/spec/integration/install/after/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  194. data/spec/integration/install/after/Example/Example.xcodeproj.yaml +172 -0
  195. data/spec/integration/install/after/execution_output.txt +22 -0
  196. data/spec/integration/install/before/Example/Example/AppDelegate.h +15 -0
  197. data/spec/integration/install/before/Example/Example/AppDelegate.m +49 -0
  198. data/spec/integration/install/before/Example/Example/Example-Info.plist +38 -0
  199. data/spec/integration/install/before/Example/Example/Example-Prefix.pch +16 -0
  200. data/spec/integration/install/before/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  201. data/spec/integration/install/before/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  202. data/spec/integration/install/before/Example/Example/en.lproj/InfoPlist.strings +2 -0
  203. data/spec/integration/install/before/Example/Example/main.m +18 -0
  204. data/spec/integration/install/before/Example/Example.xcodeproj/project.pbxproj +421 -0
  205. data/spec/integration/install/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  206. data/spec/integration/install-again/after/Example/Example/AppDelegate.h +15 -0
  207. data/spec/integration/install-again/after/Example/Example/AppDelegate.m +49 -0
  208. data/spec/integration/install-again/after/Example/Example/Example-Info.plist +38 -0
  209. data/spec/integration/install-again/after/Example/Example/Example-Prefix.pch +18 -0
  210. data/spec/integration/install-again/after/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  211. data/spec/integration/install-again/after/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  212. data/spec/integration/install-again/after/Example/Example/Resources/R.h +19 -0
  213. data/spec/integration/install-again/after/Example/Example/Resources/R.m +17 -0
  214. data/spec/integration/install-again/after/Example/Example/en.lproj/InfoPlist.strings +2 -0
  215. data/spec/integration/install-again/after/Example/Example/main.m +18 -0
  216. data/spec/integration/install-again/after/Example/Example.xcodeproj.yaml +172 -0
  217. data/spec/integration/install-again/after/execution_output.txt +23 -0
  218. data/spec/integration/install-again/before/Example/Example/AppDelegate.h +15 -0
  219. data/spec/integration/install-again/before/Example/Example/AppDelegate.m +49 -0
  220. data/spec/integration/install-again/before/Example/Example/Example-Info.plist +38 -0
  221. data/spec/integration/install-again/before/Example/Example/Example-Prefix.pch +18 -0
  222. data/spec/integration/install-again/before/Example/Example/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  223. data/spec/integration/install-again/before/Example/Example/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  224. data/spec/integration/install-again/before/Example/Example/Resources/R.h +19 -0
  225. data/spec/integration/install-again/before/Example/Example/Resources/R.m +17 -0
  226. data/spec/integration/install-again/before/Example/Example/en.lproj/InfoPlist.strings +2 -0
  227. data/spec/integration/install-again/before/Example/Example/main.m +18 -0
  228. data/spec/integration/install-again/before/Example/Example.xcodeproj/project.pbxproj +898 -0
  229. data/spec/integration/install-again/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  230. data/spec/integration/install-moved-supporting-files/after/Example/Example.xcodeproj.yaml +173 -0
  231. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/Example-Info.plist +38 -0
  232. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/Example-Prefix.pch +18 -0
  233. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/Resources/R.h +19 -0
  234. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/Resources/R.m +17 -0
  235. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/en.lproj/InfoPlist.strings +2 -0
  236. data/spec/integration/install-moved-supporting-files/after/Example/Supporting_Files/main.m +18 -0
  237. data/spec/integration/install-moved-supporting-files/after/Example/res/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  238. data/spec/integration/install-moved-supporting-files/after/Example/res/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  239. data/spec/integration/install-moved-supporting-files/after/Example/src/AppDelegate.h +15 -0
  240. data/spec/integration/install-moved-supporting-files/after/Example/src/AppDelegate.m +49 -0
  241. data/spec/integration/install-moved-supporting-files/after/execution_output.txt +22 -0
  242. data/spec/integration/install-moved-supporting-files/before/Example/Example.xcodeproj/project.pbxproj +431 -0
  243. data/spec/integration/install-moved-supporting-files/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  244. data/spec/integration/install-moved-supporting-files/before/Example/Supporting_Files/Example-Info.plist +38 -0
  245. data/spec/integration/install-moved-supporting-files/before/Example/Supporting_Files/Example-Prefix.pch +16 -0
  246. data/spec/integration/install-moved-supporting-files/before/Example/Supporting_Files/en.lproj/InfoPlist.strings +2 -0
  247. data/spec/integration/install-moved-supporting-files/before/Example/Supporting_Files/main.m +18 -0
  248. data/spec/integration/install-moved-supporting-files/before/Example/res/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  249. data/spec/integration/install-moved-supporting-files/before/Example/res/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  250. data/spec/integration/install-moved-supporting-files/before/Example/src/AppDelegate.h +15 -0
  251. data/spec/integration/install-moved-supporting-files/before/Example/src/AppDelegate.m +49 -0
  252. data/spec/integration/install-no-supporting-files/after/Example/Example.xcodeproj.yaml +173 -0
  253. data/spec/integration/install-no-supporting-files/after/Example/Resources/R.h +19 -0
  254. data/spec/integration/install-no-supporting-files/after/Example/Resources/R.m +17 -0
  255. data/spec/integration/install-no-supporting-files/after/Example/Supporting_Files/Example-Info.plist +38 -0
  256. data/spec/integration/install-no-supporting-files/after/Example/Supporting_Files/Example-Prefix.pch +18 -0
  257. data/spec/integration/install-no-supporting-files/after/Example/Supporting_Files/en.lproj/InfoPlist.strings +2 -0
  258. data/spec/integration/install-no-supporting-files/after/Example/Supporting_Files/main.m +18 -0
  259. data/spec/integration/install-no-supporting-files/after/Example/res/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  260. data/spec/integration/install-no-supporting-files/after/Example/res/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  261. data/spec/integration/install-no-supporting-files/after/Example/src/AppDelegate.h +15 -0
  262. data/spec/integration/install-no-supporting-files/after/Example/src/AppDelegate.m +49 -0
  263. data/spec/integration/install-no-supporting-files/after/execution_output.txt +23 -0
  264. data/spec/integration/install-no-supporting-files/before/Example/Example.xcodeproj/project.pbxproj +430 -0
  265. data/spec/integration/install-no-supporting-files/before/Example/Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  266. data/spec/integration/install-no-supporting-files/before/Example/Supporting_Files/Example-Info.plist +38 -0
  267. data/spec/integration/install-no-supporting-files/before/Example/Supporting_Files/Example-Prefix.pch +16 -0
  268. data/spec/integration/install-no-supporting-files/before/Example/Supporting_Files/en.lproj/InfoPlist.strings +2 -0
  269. data/spec/integration/install-no-supporting-files/before/Example/Supporting_Files/main.m +18 -0
  270. data/spec/integration/install-no-supporting-files/before/Example/res/Images.xcassets/AppIcon.appiconset/Contents.json +23 -0
  271. data/spec/integration/install-no-supporting-files/before/Example/res/Images.xcassets/LaunchImage.launchimage/Contents.json +23 -0
  272. data/spec/integration/install-no-supporting-files/before/Example/src/AppDelegate.h +15 -0
  273. data/spec/integration/install-no-supporting-files/before/Example/src/AppDelegate.m +49 -0
  274. data/spec/integration/version/after/execution_output.txt +3 -0
  275. data/spec/integration/version/before/.gitkeep +0 -0
  276. data/spec/integration.rb +86 -0
  277. data/spec/spec_helper/xcodeproj_project_yaml.rb +26 -0
  278. data/spec/unit/analyzer/aggregate_analyzer_spec.rb +61 -0
  279. data/spec/unit/analyzer/analyzer_spec.rb +123 -0
  280. data/spec/unit/analyzer/resources_aggregate_analyzer_spec.rb +46 -0
  281. data/spec/unit/analyzer/resources_analyzer/base_resources_analyzer_spec.rb +126 -0
  282. data/spec/unit/analyzer/resources_analyzer/bundle_resources_analyzer_spec.rb +57 -0
  283. data/spec/unit/analyzer/resources_analyzer/loose_resources_analyzer_spec.rb +22 -0
  284. data/spec/unit/analyzer/strings_analyzer_spec.rb +99 -0
  285. data/spec/unit/builder/file_builder_spec.rb +46 -0
  286. data/spec/unit/builder/resources_builder_spec.rb +50 -0
  287. data/spec/unit/builder/string_builder_spec.rb +76 -0
  288. data/spec/unit/command/main_command_spec.rb +5 -0
  289. data/spec/unit/command/project_command_spec.rb +23 -0
  290. data/spec/unit/helper/file_helper_spec.rb +24 -0
  291. data/spec/unit/logger_spec.rb +142 -0
  292. data/spec/unit/model/section_spec.rb +47 -0
  293. data/spec/unit/model/xcassets/bundle_spec.rb +154 -0
  294. data/spec/unit/model/xcassets/resource_image_spec.rb +141 -0
  295. data/spec/unit/model/xcassets/resource_spec.rb +174 -0
  296. data/spec/unit/model/xcassets/spec_helper.rb +31 -0
  297. data/spec/unit/spec_helper.rb +17 -0
  298. data/xcres.gemspec +42 -0
  299. metadata +811 -0
@@ -0,0 +1,133 @@
1
+ require 'xcres/analyzer/analyzer'
2
+ require 'set'
3
+
4
+ module XCRes
5
+ module ResourcesAnalyzer
6
+
7
+ # A +BaseResourcesAnalyzer+ scans the project for resources,
8
+ # which should be included in the output file.
9
+ #
10
+ class BaseResourcesAnalyzer < Analyzer
11
+
12
+ FILTER_WORDS = ['icon', 'image']
13
+
14
+ # Get a list of all files in a directory
15
+ #
16
+ # @param [Pathname] dir
17
+ # the directory
18
+ #
19
+ # @return [Array<Pathname>]
20
+ # the file paths relative to the given dir
21
+ #
22
+ def find_files_in_dir dir
23
+ unless dir.exist?
24
+ warn "Can't find files in dir %s as it doesn't exist!",
25
+ dir.relative_path_from(project.path + '..').to_s
26
+ return []
27
+ end
28
+ Dir.chdir dir do
29
+ Dir['**/*'].map { |path| Pathname(path) }
30
+ end
31
+ end
32
+
33
+ # Build a section for image resources
34
+ #
35
+ # @param [Array<String>] image_files
36
+ #
37
+ # @param [Hash] options
38
+ # see #build_section_data
39
+ #
40
+ # @return [Hash{String => Pathname}]
41
+ #
42
+ def build_images_section_data image_file_paths, options={}
43
+ image_file_paths = filter_exclusions(image_file_paths)
44
+ image_file_paths = filter_device_specific_image_paths(image_file_paths)
45
+ build_section_data(image_file_paths, options)
46
+ end
47
+
48
+ # Filter out device scale and idiom specific images (retina, ipad),
49
+ # but ensure the base exist once
50
+ #
51
+ # @param [Array<Pathname>] file_paths
52
+ # the file paths to filter
53
+ #
54
+ # @return [Array<String>]
55
+ # the filtered file paths
56
+ #
57
+ def filter_device_specific_image_paths file_paths
58
+ file_paths.map do |path|
59
+ path.to_s.gsub /(@2x)?(~(iphone|ipad))?(?=\.\w+$)/, ''
60
+ end.to_set.to_a
61
+ end
62
+
63
+ # Find image files in a given list of file paths
64
+ #
65
+ # @param [Array<Pathname>] file_paths
66
+ # the list of files
67
+ #
68
+ # @return [Array<Pathname>]
69
+ # the filtered list
70
+ #
71
+ def find_image_files file_paths
72
+ file_paths.select { |path| path.to_s.match /\.(png|jpe?g|gif)$/ }
73
+ end
74
+
75
+ # Build a keys to paths mapping
76
+ #
77
+ # @param [Array<Pathname>] file_paths
78
+ # the file paths, which will be the values of the mapping
79
+ #
80
+ # @param [Hash] options
81
+ # valid options are:
82
+ # * [Bool] :use_basename?
83
+ # if set, it will only use the basename of the path for the key
84
+ #
85
+ # @return [Hash{String => Pathname}]
86
+ #
87
+ def build_section_data file_paths, options={}
88
+ options = { use_basename?: false }.merge options
89
+
90
+ # Transform image file paths to keys
91
+ keys_to_paths = {}
92
+ for path in file_paths
93
+ path = options[:use_basename?] ? File.basename(path) : path.to_s
94
+ key = key_from_path(path)
95
+ keys_to_paths[key] = path
96
+ end
97
+
98
+ keys_to_paths
99
+ end
100
+
101
+ # Derive a key from a resource path
102
+ #
103
+ # @param [String] path
104
+ # the path to the resource
105
+ #
106
+ # @return [String]
107
+ #
108
+ def key_from_path path
109
+ key = path.to_s
110
+
111
+ # Get rid of the file extension
112
+ key = key.sub /#{File.extname(path)}$/, ''
113
+
114
+ # Graphical assets tend to contain words, which you want to strip.
115
+ # Because we want to list the words to ignore only in one variant,
116
+ # we have to ensure that the icon name is prepared for that, without
117
+ # loosing word separation if camel case was used.
118
+ key = key.underscore.downcase
119
+
120
+ for filter_word in FILTER_WORDS do
121
+ key.gsub! filter_word, ''
122
+ end
123
+
124
+ # Remove unnecessary underscores
125
+ key = key.gsub(/^_*|_*$|(_)_+/, '\1')
126
+
127
+ return key
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,68 @@
1
+ require 'xcres/analyzer/resources_analyzer/base_resources_analyzer'
2
+
3
+ module XCRes
4
+ module ResourcesAnalyzer
5
+
6
+ # A +BundleResourcesAnalyzer+ scans the project for bundles, whose resources
7
+ # should be included in the output file.
8
+ #
9
+ class BundleResourcesAnalyzer < BaseResourcesAnalyzer
10
+
11
+ def analyze
12
+ @sections = build_sections_for_bundles.compact
13
+ end
14
+
15
+ # Build a section for each bundle if it contains any resources
16
+ #
17
+ # @return [Array<Section>]
18
+ # the built sections
19
+ #
20
+ def build_sections_for_bundles
21
+ bundle_file_refs = find_bundle_file_refs
22
+
23
+ log "Found #%s resource bundles in project.", bundle_file_refs.count
24
+
25
+ bundle_file_refs.map do |file_ref|
26
+ section = build_section_for_bundle(file_ref)
27
+ log 'Add section for %s with %s elements', section.name, section.items.count unless section.nil?
28
+ section
29
+ end.compact
30
+ end
31
+
32
+ # Discover all references to resources bundles in project
33
+ #
34
+ # @return [Array<PBXFileReference>]
35
+ #
36
+ def find_bundle_file_refs
37
+ find_file_refs_by_extname '.bundle'
38
+ end
39
+
40
+ # Build a section for a resources bundle
41
+ #
42
+ # @param [PBXFileReference] bundle_file_ref
43
+ # the file reference to the resources bundle file
44
+ #
45
+ # @return [Section?]
46
+ # a section or nil
47
+ #
48
+ def build_section_for_bundle bundle_file_ref
49
+ bundle_files = find_files_in_dir(bundle_file_ref.real_path)
50
+ image_files = find_image_files(bundle_files)
51
+
52
+ log "Found bundle %s with #%s image files of #%s total files.", bundle_file_ref.path, image_files.count, bundle_files.count
53
+
54
+ return nil if image_files.empty?
55
+
56
+ section_data = build_images_section_data(image_files)
57
+
58
+ return nil if section_data.empty?
59
+
60
+ section_name = basename_without_ext(bundle_file_ref.path)
61
+
62
+ new_section(section_name, section_data)
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ require 'xcres/analyzer/resources_analyzer/base_resources_analyzer'
2
+
3
+ module XCRes
4
+ module ResourcesAnalyzer
5
+
6
+ # A +LooseResourcesAnalyzer+ scans the project for resources, which are
7
+ # loosely placed in the project or in a group and should be included in the
8
+ # output file.
9
+ #
10
+ class LooseResourcesAnalyzer < BaseResourcesAnalyzer
11
+
12
+ def analyze
13
+ @sections = [build_section_for_loose_images].compact
14
+ end
15
+
16
+ def exclude_file_patterns
17
+ super + ['Default.*', 'Default@2x.*', 'Default-568h@2x.*']
18
+ end
19
+
20
+ # Build a section for loose image resources in the project
21
+ #
22
+ # @return [Section?]
23
+ #
24
+ def build_section_for_loose_images
25
+ image_files = find_image_files(resources_files.map(&:path))
26
+
27
+ log "Found #%s image files in project.", image_files.count
28
+
29
+ return nil if image_files.empty?
30
+
31
+ data = build_images_section_data(image_files, use_basename?: true)
32
+
33
+ new_section('Images', data)
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,62 @@
1
+ require 'xcres/analyzer/resources_analyzer/base_resources_analyzer'
2
+ require 'xcres/model/xcassets/bundle'
3
+
4
+ module XCRes
5
+ module ResourcesAnalyzer
6
+
7
+ # A +XCAssetsAnalyzer+ scans the project for asset catalogs,
8
+ # which should be included in the output file.
9
+ #
10
+ class XCAssetsAnalyzer < BaseResourcesAnalyzer
11
+
12
+ def analyze
13
+ sections = []
14
+ sections += build_sections_for_xcassets
15
+ @sections = sections.compact
16
+ end
17
+
18
+ # Build a section for each asset catalog if it contains any resources
19
+ #
20
+ # @return [Array<Section>]
21
+ # the built sections
22
+ #
23
+ def build_sections_for_xcassets
24
+ file_refs = find_file_refs_by_extname '.xcassets'
25
+
26
+ log "Found #%s asset catalogs in project.", file_refs.count
27
+
28
+ file_refs.map do |file_ref|
29
+ bundle = XCAssets::Bundle.open(file_ref.real_path)
30
+ section = build_section_for_xcassets(bundle)
31
+ log 'Add section for %s with %s elements', section.name, section.items.count unless section.nil?
32
+ section
33
+ end.compact
34
+ end
35
+
36
+ # Build a section for a asset catalog
37
+ #
38
+ # @param [XCAssets::Bundle] xcassets_bundle
39
+ # the file reference to the resources bundle file
40
+ #
41
+ # @return [Section?]
42
+ # a section or nil
43
+ #
44
+ def build_section_for_xcassets bundle
45
+ log "Found asset catalog %s with #%s image files.",
46
+ bundle.path.basename, bundle.resources.count
47
+
48
+ return nil if bundle.resources.empty?
49
+
50
+ section_data = build_images_section_data(bundle.resources.map(&:name))
51
+
52
+ return nil if section_data.empty?
53
+
54
+ section_name = "#{basename_without_ext(bundle.path)}Assets"
55
+
56
+ new_section(section_name, section_data)
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,255 @@
1
+ require 'xcres/analyzer/analyzer'
2
+ require 'json'
3
+
4
+ module XCRes
5
+
6
+ # A +StringsAnalyzer+ scans the project for resources,
7
+ # which should be included in the output file.
8
+ #
9
+ class StringsAnalyzer < Analyzer
10
+
11
+ # @return [String]
12
+ # optional two-letter language code conforming ISO 639-1
13
+ attr_accessor :default_language
14
+
15
+ # Initialize a new analyzer
16
+ #
17
+ # @param [Xcodeproj::Project] project
18
+ # see #project.
19
+ #
20
+ # @param [Hash] options
21
+ # Possible options:
22
+ # * :default_language => see #default_language.
23
+ #
24
+ def initialize(project=nil, options={})
25
+ super
26
+ self.default_language = options[:default_language]
27
+ end
28
+
29
+ def analyze
30
+ log 'Strings files in project: %s', strings_file_refs.map(&:path)
31
+ log 'Native development languages: %s', native_dev_languages.to_a
32
+ log 'Used languages for .strings files: %s', used_languages.to_a
33
+ log 'Preferred languages: %s', languages.to_a
34
+ log 'Strings files after language selection: %s', selected_strings_file_refs.map(&:path)
35
+
36
+ @sections = [build_section]
37
+ end
38
+
39
+ # Build the section
40
+ #
41
+ # @return [Section]
42
+ #
43
+ def build_section
44
+ selected_file_refs = selected_strings_file_refs
45
+
46
+ # Apply ignore list
47
+ file_paths = filter_exclusions(selected_file_refs.map(&:path))
48
+ filtered_file_refs = selected_file_refs.select { |file_ref| file_paths.include? file_ref.path }
49
+ rel_file_paths = filtered_file_refs.map { |p| p.real_path.relative_path_from(Pathname.pwd) }
50
+
51
+ log 'Non-ignored .strings files: %s', rel_file_paths.map(&:to_s)
52
+
53
+ keys_by_file = {}
54
+ for path in rel_file_paths
55
+ keys_by_file[path] = keys_by_file(path)
56
+ end
57
+ items = keys_by_file.values.reduce({}, :merge)
58
+
59
+ new_section('Strings', items)
60
+ end
61
+
62
+ # Discover all references to .strings files in project (e.g. Localizable.strings)
63
+ #
64
+ # @return [Array<PBXFileReference>]
65
+ #
66
+ def strings_file_refs
67
+ @strings_file_refs ||= find_file_refs_by_extname '.strings'
68
+ end
69
+
70
+ # Select strings files by language
71
+ #
72
+ # @return [Array<PBXFileReference>]
73
+ #
74
+ def selected_strings_file_refs
75
+ @selected_strings_file_refs ||= strings_file_refs.select { |file_ref| languages.include? file_ref.name }
76
+ end
77
+
78
+ # Derive the used languages from given strings files
79
+ #
80
+ # @param [Array<PBXFileReference>] strings_file_refs
81
+ #
82
+ # @return [Set<String>]
83
+ #
84
+ def derive_used_languages(strings_file_refs)
85
+ strings_file_refs.map(&:name).to_set
86
+ end
87
+
88
+ # All used languages in the project
89
+ #
90
+ # @return [Set<String>]
91
+ #
92
+ def used_languages
93
+ @used_languages ||= derive_used_languages(strings_file_refs)
94
+ end
95
+
96
+ # Find preferred languages, which is:
97
+ # - either only the default_language, if specified
98
+ # - or the intersection of native development and used languages
99
+ # - or all used languages
100
+ #
101
+ # @return [Set<String>]
102
+ #
103
+ def languages
104
+ if default_language != nil
105
+ # Use specified default language as primary language
106
+ [default_language]
107
+ else
108
+ # Calculate the intersection of native development and used languages,
109
+ # fallback to the latter only, if it is empty
110
+ languages = native_dev_languages & used_languages
111
+ if languages.empty?
112
+ used_languages
113
+ else
114
+ languages
115
+ end
116
+ end
117
+ end
118
+
119
+ # Discover Info.plist files by build settings of the application target
120
+ #
121
+ # @return [Set<Pathname>]
122
+ # the relative paths to the .plist-files
123
+ #
124
+ def info_plist_paths
125
+ @info_plist_paths ||= target.build_configurations.map do |config|
126
+ config.build_settings['INFOPLIST_FILE']
127
+ end.compact.map { |file| Pathname(file) }.flatten.to_set
128
+ end
129
+
130
+ # Absolute file paths to Info.plist files by build settings.
131
+ # See #info_plist_paths.
132
+ #
133
+ # @return [Set<Pathname>]
134
+ # the absolute paths to the .plist-files
135
+ #
136
+ def absolute_info_plist_paths
137
+ info_plist_paths.map do |path|
138
+ absolute_project_file_path(path)
139
+ end.select do |path|
140
+ if path.to_s.include?('$')
141
+ warn "Couldn't resolve all placeholders in INFOPLIST_FILE %s.", path.to_s
142
+ false
143
+ else
144
+ true
145
+ end
146
+ end
147
+ end
148
+
149
+ # Find the native development languages by trying to use the
150
+ # "Localization native development region" from Info.plist
151
+ #
152
+ # @return [Set<String>]
153
+ #
154
+ def native_dev_languages
155
+ @native_dev_languages ||= absolute_info_plist_paths.map do |path|
156
+ read_plist_key(path, :CFBundleDevelopmentRegion)
157
+ end.to_set
158
+ end
159
+
160
+ # Extracts a given key from a plist file given as a path
161
+ #
162
+ # @param [Pathname] path
163
+ # the path of the plist file
164
+ #
165
+ # @param [String] key
166
+ # the key, whose value should been extracted
167
+ #
168
+ # @return [String]
169
+ #
170
+ def read_plist_key(path, key)
171
+ raise ArgumentError, 'Path is required, but nil' if path.nil?
172
+ raise ArgumentError, 'Key is required, but nil' if key.nil?
173
+ out = `/usr/libexec/PlistBuddy -c "Print :#{key}" #{path}`.chomp
174
+ raise ArgumentError, out unless $?.success?
175
+ out
176
+ end
177
+
178
+ # Read a .strings file given as a path
179
+ #
180
+ # @param [Pathname] path
181
+ # the path of the strings file
182
+ #
183
+ # @return [Hash]
184
+ #
185
+ def read_strings_file(path)
186
+ raise ArgumentError, "File '#{path}' doesn't exist" unless path.exist?
187
+ raise ArgumentError, "File '#{path}' is not a file" unless path.file?
188
+ error = `plutil -lint -s "#{path}"`
189
+ return warn "File %s is malformed:\n#{error}", path.to_s unless $?.success?
190
+ json_or_error = `plutil -convert json "#{path}" -o -`.chomp
191
+ return warn "File %s couldn't be converted to JSON.\n#{json_or_error}", path.to_s unless $?.success?
192
+ JSON.parse(json_or_error.force_encoding('UTF-8'))
193
+ rescue EncodingError => e
194
+ raise StandardError, "Encoding error in #{path}: #{e}"
195
+ end
196
+
197
+ # Calculate the absolute path for a file path given relative to the
198
+ # project / its `$SRCROOT`.
199
+ #
200
+ # We need either absolute paths or relative paths to our current location.
201
+ # Xcodeproj provides this for +PBXFileReference+, but this doesn't work
202
+ # for file references in build settings.
203
+ #
204
+ # @param [String|Pathname] file_path
205
+ # the path relative to the project.
206
+ #
207
+ # @return [Pathname]
208
+ #
209
+ def absolute_project_file_path(file_path)
210
+ source_root = (project.path + '..').realpath
211
+ if file_path.to_s.include?('$')
212
+ Pathname(file_path.to_s.gsub(/\$[({]?SRCROOT[)}]?/, source_root.to_s))
213
+ else
214
+ source_root + file_path
215
+ end
216
+ end
217
+
218
+ # Get relative file paths
219
+ #
220
+ # @return [Array<Pathname>]
221
+ #
222
+ def strings_file_paths
223
+ project_dir = project.path + '..'
224
+ project_dir_realpath = project_dir.realpath
225
+ strings_file_refs.map(&:real_path).map do |path|
226
+ project_dir + path.relative_path_from(project_dir_realpath) rescue path
227
+ end
228
+ end
229
+
230
+ # Read a file and collect all its keys
231
+ #
232
+ # @param [Pathname] path
233
+ # the path to the .strings file to read
234
+ #
235
+ # @return [Hash{String => Hash}]
236
+ #
237
+ def keys_by_file(path)
238
+ begin
239
+ # Load strings file contents
240
+ strings = read_strings_file(path)
241
+
242
+ keys = Hash[strings.map do |key, value|
243
+ [key, { value: key, comment: value.gsub(/[\r\n]/, ' ') }]
244
+ end]
245
+
246
+ log 'Found %s keys in file %s', keys.count, path
247
+
248
+ keys
249
+ rescue ArgumentError => error
250
+ raise ArgumentError, 'Error while reading %s: %s' % [path, error]
251
+ end
252
+ end
253
+
254
+ end
255
+ end
@@ -0,0 +1,61 @@
1
+ require 'colored'
2
+ require 'xcres/builder/string_builder'
3
+
4
+ class XCRes::FileBuilder
5
+
6
+ attr_accessor :output_path
7
+ attr_accessor :logger
8
+
9
+ def prepare_output_path!
10
+ # Ensure that the given directory exists
11
+ output_dir = File.dirname output_path
12
+ unless Dir.exist? output_dir
13
+ logger.success 'Directory did not exist. Will been created.'
14
+ Dir.mkdir output_dir
15
+ end
16
+
17
+ # Replace an already existing file, by deleting it and rebuilding from scratch
18
+ if File.exist? output_path
19
+ raise ArgumentError.new 'Output path is a directory!' if Dir.exist? output_path
20
+ logger.warn "Output path already exists. Will be replaced."
21
+ end
22
+ end
23
+
24
+ def build
25
+ prepare_output_path!
26
+ end
27
+
28
+ def build_contents &block
29
+ # Pass a new string builder to given block
30
+ builder = XCRes::StringBuilder.new
31
+ block.call builder
32
+ builder.result
33
+ end
34
+
35
+ def write_file_eventually file_path, contents
36
+ # Check if the file already exists and touch it only if the contents changed
37
+ if File.exist? file_path
38
+ tmp_dir_path = `/usr/bin/mktemp -d -t xcres`
39
+ tmp_file_path = tmp_dir_path + File.basename(file_path)
40
+
41
+ # Write temp file
42
+ write_file tmp_file_path, contents
43
+
44
+ # Diff current version and temporary file
45
+ if FileUtils.compare_file(tmp_file_path, file_path)
46
+ logger.success "Existing file is up-to-date. Don't touch."
47
+ return
48
+ end
49
+ end
50
+
51
+ write_file file_path, contents
52
+ end
53
+
54
+ def write_file file_path, contents
55
+ # Write file
56
+ File.open file_path, 'w' do |file|
57
+ file.write contents
58
+ end
59
+ end
60
+
61
+ end