PBXProjectExtensions.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. using System.Collections.Generic;
  2. using System.Text;
  3. using System.Text.RegularExpressions;
  4. using System.IO;
  5. using System;
  6. using ChillyRoom.UnityEditor.iOS.Xcode.PBX;
  7. namespace ChillyRoom.UnityEditor.iOS.Xcode.Extensions
  8. {
  9. /* This class implements a number of static methods for performing common tasks
  10. on xcode projects.
  11. TODO: Make sure enough stuff is exposed so that it's possible to perform the tasks
  12. without using internal APIs
  13. */
  14. public static class PBXProjectExtensions
  15. {
  16. // Create a wrapper class so that collection initializers work and we can have a
  17. // compact notation. Note that we can't use Dictionary because the keys may be duplicate
  18. internal class FlagList : List<KeyValuePair<string, string>>
  19. {
  20. public void Add(string flag, string value)
  21. {
  22. Add(new KeyValuePair<string, string>(flag, value));
  23. }
  24. }
  25. internal static FlagList appExtensionReleaseBuildFlags = new FlagList
  26. {
  27. // { "INFOPLIST_FILE", <path/to/info.plist> },
  28. { "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
  29. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
  30. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
  31. // { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  32. { "PRODUCT_NAME", "$(TARGET_NAME)" },
  33. { "SKIP_INSTALL", "YES" },
  34. };
  35. internal static FlagList appExtensionDebugBuildFlags = new FlagList
  36. {
  37. // { "INFOPLIST_FILE", <path/to/info.plist> },
  38. { "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
  39. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
  40. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
  41. // { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  42. { "PRODUCT_NAME", "$(TARGET_NAME)" },
  43. { "SKIP_INSTALL", "YES" },
  44. };
  45. internal static FlagList watchExtensionReleaseBuildFlags = new FlagList
  46. {
  47. { "ASSETCATALOG_COMPILER_COMPLICATION_NAME", "Complication" },
  48. { "CLANG_ANALYZER_NONNULL", "YES" },
  49. { "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
  50. { "CLANG_WARN_INFINITE_RECURSION", "YES" },
  51. { "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
  52. { "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" },
  53. { "GCC_NO_COMMON_BLOCKS", "YES" },
  54. //{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
  55. { "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
  56. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
  57. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
  58. // { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  59. { "PRODUCT_NAME", "${TARGET_NAME}" },
  60. { "SDKROOT", "watchos" },
  61. { "SKIP_INSTALL", "YES" },
  62. { "TARGETED_DEVICE_FAMILY", "4" },
  63. { "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
  64. // the following are needed to override project settings in Unity Xcode project
  65. { "ARCHS", "$(ARCHS_STANDARD)" },
  66. { "SUPPORTED_PLATFORMS", "watchos" },
  67. { "SUPPORTED_PLATFORMS", "watchsimulator" },
  68. };
  69. internal static FlagList watchExtensionDebugBuildFlags = new FlagList
  70. {
  71. { "ASSETCATALOG_COMPILER_COMPLICATION_NAME", "Complication" },
  72. { "CLANG_ANALYZER_NONNULL", "YES" },
  73. { "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
  74. { "CLANG_WARN_INFINITE_RECURSION", "YES" },
  75. { "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
  76. { "DEBUG_INFORMATION_FORMAT", "dwarf" },
  77. { "ENABLE_TESTABILITY", "YES" },
  78. { "GCC_NO_COMMON_BLOCKS", "YES" },
  79. // { "INFOPLIST_FILE", "<path/to/Info.plist>" },
  80. { "LD_RUNPATH_SEARCH_PATHS", "$(inherited)" },
  81. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks" },
  82. { "LD_RUNPATH_SEARCH_PATHS", "@executable_path/../../Frameworks" },
  83. // { "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  84. { "PRODUCT_NAME", "${TARGET_NAME}" },
  85. { "SDKROOT", "watchos" },
  86. { "SKIP_INSTALL", "YES" },
  87. { "TARGETED_DEVICE_FAMILY", "4" },
  88. { "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
  89. // the following are needed to override project settings in Unity Xcode project
  90. { "ARCHS", "$(ARCHS_STANDARD)" },
  91. { "SUPPORTED_PLATFORMS", "watchos" },
  92. { "SUPPORTED_PLATFORMS", "watchsimulator" },
  93. };
  94. internal static FlagList watchAppReleaseBuildFlags = new FlagList
  95. {
  96. { "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon" },
  97. { "CLANG_ANALYZER_NONNULL", "YES" },
  98. { "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
  99. { "CLANG_WARN_INFINITE_RECURSION", "YES" },
  100. { "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
  101. { "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" },
  102. { "GCC_NO_COMMON_BLOCKS", "YES" },
  103. //{ "IBSC_MODULE", "the extension target name with ' ' replaced with '_'" },
  104. //{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
  105. //{ "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  106. { "PRODUCT_NAME", "$(TARGET_NAME)" },
  107. { "SDKROOT", "watchos" },
  108. { "SKIP_INSTALL", "YES" },
  109. { "TARGETED_DEVICE_FAMILY", "4" },
  110. { "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
  111. // the following are needed to override project settings in Unity Xcode project
  112. { "ARCHS", "$(ARCHS_STANDARD)" },
  113. { "SUPPORTED_PLATFORMS", "watchos" },
  114. { "SUPPORTED_PLATFORMS", "watchsimulator" },
  115. };
  116. internal static FlagList watchAppDebugBuildFlags = new FlagList
  117. {
  118. { "ASSETCATALOG_COMPILER_APPICON_NAME", "AppIcon" },
  119. { "CLANG_ANALYZER_NONNULL", "YES" },
  120. { "CLANG_WARN_DOCUMENTATION_COMMENTS", "YES" },
  121. { "CLANG_WARN_INFINITE_RECURSION", "YES" },
  122. { "CLANG_WARN_SUSPICIOUS_MOVE", "YES" },
  123. { "DEBUG_INFORMATION_FORMAT", "dwarf" },
  124. { "ENABLE_TESTABILITY", "YES" },
  125. { "GCC_NO_COMMON_BLOCKS", "YES" },
  126. //{ "IBSC_MODULE", "the extension target name with ' ' replaced with '_'" },
  127. //{ "INFOPLIST_FILE", "<path/to/Info.plist>" },
  128. //{ "PRODUCT_BUNDLE_IDENTIFIER", "<bundle id>" },
  129. { "PRODUCT_NAME", "$(TARGET_NAME)" },
  130. { "SDKROOT", "watchos" },
  131. { "SKIP_INSTALL", "YES" },
  132. { "TARGETED_DEVICE_FAMILY", "4" },
  133. { "WATCHOS_DEPLOYMENT_TARGET", "3.1" },
  134. // the following are needed to override project settings in Unity Xcode project
  135. { "ARCHS", "$(ARCHS_STANDARD)" },
  136. { "SUPPORTED_PLATFORMS", "watchos" },
  137. { "SUPPORTED_PLATFORMS", "watchsimulator" },
  138. };
  139. static void SetBuildFlagsFromDict(this PBXProject proj, string configGuid, IEnumerable<KeyValuePair<string, string>> data)
  140. {
  141. foreach (var kv in data)
  142. proj.AddBuildPropertyForConfig(configGuid, kv.Key, kv.Value);
  143. }
  144. internal static void SetDefaultAppExtensionReleaseBuildFlags(this PBXProject proj, string configGuid)
  145. {
  146. SetBuildFlagsFromDict(proj, configGuid, appExtensionReleaseBuildFlags);
  147. }
  148. internal static void SetDefaultAppExtensionDebugBuildFlags(this PBXProject proj, string configGuid)
  149. {
  150. SetBuildFlagsFromDict(proj, configGuid, appExtensionDebugBuildFlags);
  151. }
  152. internal static void SetDefaultWatchExtensionReleaseBuildFlags(this PBXProject proj, string configGuid)
  153. {
  154. SetBuildFlagsFromDict(proj, configGuid, watchExtensionReleaseBuildFlags);
  155. }
  156. internal static void SetDefaultWatchExtensionDebugBuildFlags(this PBXProject proj, string configGuid)
  157. {
  158. SetBuildFlagsFromDict(proj, configGuid, watchExtensionDebugBuildFlags);
  159. }
  160. internal static void SetDefaultWatchAppReleaseBuildFlags(this PBXProject proj, string configGuid)
  161. {
  162. SetBuildFlagsFromDict(proj, configGuid, watchAppReleaseBuildFlags);
  163. }
  164. internal static void SetDefaultWatchAppDebugBuildFlags(this PBXProject proj, string configGuid)
  165. {
  166. SetBuildFlagsFromDict(proj, configGuid, watchAppDebugBuildFlags);
  167. }
  168. /// <summary>
  169. /// Creates an app extension.
  170. /// </summary>
  171. /// <returns>The GUID of the new target.</returns>
  172. /// <param name="proj">A project passed as this argument.</param>
  173. /// <param name="mainTargetGuid">The GUID of the main target to link the app to.</param>
  174. /// <param name="name">The name of the app extension.</param>
  175. /// <param name="bundleId">The bundle ID of the app extension. The bundle ID must be
  176. /// prefixed with the parent app bundle ID.</param>
  177. /// <param name="infoPlistPath">Path to the app extension Info.plist document.</param>
  178. public static string AddAppExtension(this PBXProject proj, string mainTargetGuid,
  179. string name, string bundleId, string infoPlistPath)
  180. {
  181. string ext = ".appex";
  182. var newTargetGuid = proj.AddTarget(name, ext, "com.apple.product-type.app-extension");
  183. foreach (var configName in proj.BuildConfigNames())
  184. {
  185. var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
  186. if (configName.Contains("Debug"))
  187. SetDefaultAppExtensionDebugBuildFlags(proj, configGuid);
  188. else
  189. SetDefaultAppExtensionReleaseBuildFlags(proj, configGuid);
  190. proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
  191. proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
  192. }
  193. proj.AddSourcesBuildPhase(newTargetGuid);
  194. proj.AddResourcesBuildPhase(newTargetGuid);
  195. proj.AddFrameworksBuildPhase(newTargetGuid);
  196. string copyFilesPhaseGuid = proj.AddCopyFilesBuildPhase(mainTargetGuid, "Embed App Extensions", "", "13");
  197. proj.AddFileToBuildSection(mainTargetGuid, copyFilesPhaseGuid, proj.GetTargetProductFileRef(newTargetGuid));
  198. proj.AddTargetDependency(mainTargetGuid, newTargetGuid);
  199. return newTargetGuid;
  200. }
  201. /// <summary>
  202. /// Creates a watch application.
  203. /// </summary>
  204. /// <returns>The GUID of the new target.</returns>
  205. /// <param name="proj">A project passed as this argument.</param>
  206. /// <param name="mainTargetGuid">The GUID of the main target to link the watch app to.</param>
  207. /// <param name="watchExtensionTargetGuid">The GUID of watch extension as returned by [[AddWatchExtension()]].</param>
  208. /// <param name="name">The name of the watch app. It must the same as the name of the watch extension.</param>
  209. /// <param name="bundleId">The bundle ID of the watch app.</param>
  210. /// <param name="infoPlistPath">Path to the watch app Info.plist document.</param>
  211. public static string AddWatchApp(this PBXProject proj, string mainTargetGuid, string watchExtensionTargetGuid,
  212. string name, string bundleId, string infoPlistPath)
  213. {
  214. var newTargetGuid = proj.AddTarget(name, ".app", "com.apple.product-type.application.watchapp2");
  215. var isbcModuleName = proj.nativeTargets[watchExtensionTargetGuid].name.Replace(" ", "_");
  216. foreach (var configName in proj.BuildConfigNames())
  217. {
  218. var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
  219. if (configName.Contains("Debug"))
  220. SetDefaultWatchAppDebugBuildFlags(proj, configGuid);
  221. else
  222. SetDefaultWatchAppReleaseBuildFlags(proj, configGuid);
  223. proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
  224. proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
  225. proj.SetBuildPropertyForConfig(configGuid, "IBSC_MODULE", isbcModuleName);
  226. }
  227. proj.AddResourcesBuildPhase(newTargetGuid);
  228. string copyFilesGuid = proj.AddCopyFilesBuildPhase(newTargetGuid, "Embed App Extensions", "", "13");
  229. proj.AddFileToBuildSection(newTargetGuid, copyFilesGuid, proj.GetTargetProductFileRef(watchExtensionTargetGuid));
  230. string copyWatchFilesGuid = proj.AddCopyFilesBuildPhase(mainTargetGuid, "Embed Watch Content", "$(CONTENTS_FOLDER_PATH)/Watch", "16");
  231. proj.AddFileToBuildSection(mainTargetGuid, copyWatchFilesGuid, proj.GetTargetProductFileRef(newTargetGuid));
  232. proj.AddTargetDependency(newTargetGuid, watchExtensionTargetGuid);
  233. proj.AddTargetDependency(mainTargetGuid, newTargetGuid);
  234. return newTargetGuid;
  235. }
  236. /// <summary>
  237. /// Creates a watch extension.
  238. /// </summary>
  239. /// <returns>The GUID of the new target.</returns>
  240. /// <param name="proj">A project passed as this argument.</param>
  241. /// <param name="mainTarget">The GUID of the main target to link the watch extension to.</param>
  242. /// <param name="name">The name of the watch extension.</param>
  243. /// <param name="bundleId">The bundle ID of the watch extension. The bundle ID must be
  244. /// prefixed with the parent watch app bundle ID.</param>
  245. /// <param name="infoPlistPath">Path to the watch extension Info.plist document.</param>
  246. public static string AddWatchExtension(this PBXProject proj, string mainTarget,
  247. string name, string bundleId, string infoPlistPath)
  248. {
  249. var newTargetGuid = proj.AddTarget(name, ".appex", "com.apple.product-type.watchkit2-extension");
  250. foreach (var configName in proj.BuildConfigNames())
  251. {
  252. var configGuid = proj.BuildConfigByName(newTargetGuid, configName);
  253. if (configName.Contains("Debug"))
  254. SetDefaultWatchExtensionDebugBuildFlags(proj, configGuid);
  255. else
  256. SetDefaultWatchExtensionReleaseBuildFlags(proj, configGuid);
  257. proj.SetBuildPropertyForConfig(configGuid, "PRODUCT_BUNDLE_IDENTIFIER", bundleId);
  258. proj.SetBuildPropertyForConfig(configGuid, "INFOPLIST_FILE", infoPlistPath);
  259. }
  260. proj.AddSourcesBuildPhase(newTargetGuid);
  261. proj.AddResourcesBuildPhase(newTargetGuid);
  262. proj.AddFrameworksBuildPhase(newTargetGuid);
  263. return newTargetGuid;
  264. }
  265. }
  266. } // namespace UnityEditor.iOS.Xcode