PostProcessBuild_macOS.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. #if UNITY_EDITOR && UNITY_2017_1_OR_NEWER
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Reflection;
  5. using UnityEngine;
  6. using UnityEditor;
  7. using UnityEditor.Callbacks;
  8. //-----------------------------------------------------------------------------
  9. // Copyright 2012-2022 RenderHeads Ltd. All rights reserved.
  10. //-----------------------------------------------------------------------------
  11. namespace RenderHeads.Media.AVProMovieCapture.Editor
  12. {
  13. public class PBXProjectHandlerException : System.Exception
  14. {
  15. public PBXProjectHandlerException(string message)
  16. : base(message)
  17. {
  18. }
  19. }
  20. public class PBXProjectHandler
  21. {
  22. private static System.Type _PBXProjectType;
  23. private static System.Type PBXProjectType
  24. {
  25. get
  26. {
  27. if (_PBXProjectType == null)
  28. {
  29. _PBXProjectType = System.Type.GetType("UnityEditor.iOS.Xcode.PBXProject, UnityEditor.iOS.Extensions.Xcode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  30. if (_PBXProjectType == null)
  31. {
  32. throw new PBXProjectHandlerException("Failed to get type \"PBXProject\"");
  33. }
  34. }
  35. return _PBXProjectType;
  36. }
  37. }
  38. private static Dictionary<string, MethodInfo> _PBXProjectTypeMethods;
  39. private static Dictionary<string, MethodInfo> PBXProjectTypeMethods
  40. {
  41. get
  42. {
  43. if (_PBXProjectTypeMethods == null)
  44. {
  45. _PBXProjectTypeMethods = new Dictionary<string, MethodInfo>();
  46. }
  47. return _PBXProjectTypeMethods;
  48. }
  49. }
  50. private static MethodInfo GetMethod(string name, System.Type[] types)
  51. {
  52. string lookup = name + types.ToString();
  53. MethodInfo method;
  54. if (!PBXProjectTypeMethods.TryGetValue(lookup, out method))
  55. {
  56. method = _PBXProjectType.GetMethod(name, types);
  57. if (method != null)
  58. {
  59. _PBXProjectTypeMethods[lookup] = method;
  60. }
  61. else
  62. {
  63. throw new PBXProjectHandlerException(string.Format("Unknown method \"{0}\"", name));
  64. }
  65. }
  66. return method;
  67. }
  68. private object _project;
  69. public PBXProjectHandler()
  70. {
  71. _project = System.Activator.CreateInstance(PBXProjectType);
  72. }
  73. public void ReadFromFile(string path)
  74. {
  75. MethodInfo method = GetMethod("ReadFromFile", new System.Type[] { typeof(string) });
  76. Debug.LogFormat("[AVProMovieCapture] Reading Xcode project at: {0}", path);
  77. method.Invoke(_project, new object[] { path });
  78. }
  79. public void WriteToFile(string path)
  80. {
  81. MethodInfo method = GetMethod("WriteToFile", new System.Type[] { typeof(string) });
  82. Debug.LogFormat("[AVProMovieCapture] Writing Xcode project to: {0}", path);
  83. method.Invoke(_project, new object[] { path });
  84. }
  85. public string TargetGuidByName(string name)
  86. {
  87. MethodInfo method = GetMethod("TargetGuidByName", new System.Type[] { typeof(string) });
  88. string guid = (string)method.Invoke(_project, new object[] { name });
  89. Debug.LogFormat("[AVProMovieCapture] Target GUID for '{0}' is '{1}'", name, guid);
  90. return guid;
  91. }
  92. public void SetBuildProperty(string guid, string property, string value)
  93. {
  94. MethodInfo method = GetMethod("SetBuildProperty", new System.Type[] { typeof(string), typeof(string), typeof(string) });
  95. Debug.LogFormat("[AVProMovieCapture] Setting build property '{0}' to '{1}' for target with guid '{2}'", property, value, guid);
  96. method.Invoke(_project, new object[] { guid, property, value });
  97. }
  98. }
  99. public class UnknownTypeException : System.Exception
  100. {
  101. public UnknownTypeException(string message) : base(message) { }
  102. }
  103. public class PlistDocumentProxy
  104. {
  105. private static System.Type _PlistDocumentType;
  106. private static System.Type PlistDocumentType
  107. {
  108. get
  109. {
  110. if (_PlistDocumentType == null)
  111. {
  112. _PlistDocumentType = System.Type.GetType("UnityEditor.iOS.Xcode.PlistDocument, UnityEditor.iOS.Extensions.Xcode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  113. if (_PlistDocumentType == null)
  114. {
  115. throw new UnknownTypeException("Unknown type \"PlistDocument\"");
  116. }
  117. }
  118. return _PlistDocumentType;
  119. }
  120. }
  121. private object _plist;
  122. public PlistDocumentProxy()
  123. {
  124. _plist = System.Activator.CreateInstance(PlistDocumentType);
  125. }
  126. public void ReadFromFile(string path)
  127. {
  128. PlistDocumentType.GetMethod("ReadFromFile").Invoke(_plist, new object[] { path });
  129. }
  130. public string WriteToString()
  131. {
  132. return (string)PlistDocumentType.GetMethod("WriteToString").Invoke(_plist, null);
  133. }
  134. public PlistElementDictProxy root
  135. {
  136. get { return new PlistElementDictProxy(_PlistDocumentType.InvokeMember("root", BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetField, null, _plist, null)); }
  137. }
  138. }
  139. public class PlistElementDictProxy
  140. {
  141. private static System.Type _PlistElementDictType;
  142. public static System.Type PlistElementDictType
  143. {
  144. get
  145. {
  146. if (_PlistElementDictType == null)
  147. {
  148. _PlistElementDictType = System.Type.GetType("UnityEditor.iOS.Xcode.PlistElementDict, UnityEditor.iOS.Extensions.Xcode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  149. if (_PlistElementDictType == null)
  150. {
  151. throw new UnknownTypeException("Unknown type \"PlistElementDict\"");
  152. }
  153. }
  154. return _PlistElementDictType;
  155. }
  156. }
  157. private object _element;
  158. public PlistElementDictProxy(object element)
  159. {
  160. _element = element;
  161. }
  162. public void SetString(string key, string value)
  163. {
  164. PlistElementDictType.GetMethod("SetString").Invoke(_element, new object[] { key, value });
  165. }
  166. }
  167. public class PostProcessBuild_macOS
  168. {
  169. private static bool ActualModifyProjectAtPath(string path)
  170. {
  171. if (!Directory.Exists(path))
  172. {
  173. Debug.LogWarningFormat("[AVProMovieCapture] Failed to find Xcode project with path: {0}", path);
  174. return false;
  175. }
  176. Debug.LogFormat("[AVProMovieCapture] Modifying Xcode project at: {0}", path);
  177. string projectPath = Path.Combine(path, "project.pbxproj");
  178. try
  179. {
  180. PBXProjectHandler handler = new PBXProjectHandler();
  181. handler.ReadFromFile(projectPath);
  182. // string guid = handler.TargetGuidByName(Application.productName);
  183. // Modify project here
  184. handler.WriteToFile(projectPath);
  185. return true;
  186. }
  187. catch (PBXProjectHandlerException ex)
  188. {
  189. Debug.LogErrorFormat("[AVProMovieCapture] {0}", ex);
  190. }
  191. return false;
  192. }
  193. [PostProcessBuild]
  194. public static void ModifyProject(BuildTarget target, string path)
  195. {
  196. if (target != BuildTarget.StandaloneOSX)
  197. return;
  198. string projectPath = Path.Combine(path, Path.GetFileName(path) + ".xcodeproj");
  199. if (ActualModifyProjectAtPath(projectPath))
  200. {
  201. }
  202. }
  203. [PostProcessBuild(100)]
  204. public static void ModfifyPlist(BuildTarget buildTarget, string path)
  205. {
  206. if (buildTarget != BuildTarget.StandaloneOSX)
  207. return;
  208. // Check if we need to update Info.plist
  209. SerializedObject settings = Settings.GetSerializedSettings();
  210. SerializedProperty propPhotoLibraryUsageDescription = settings.FindProperty("_photoLibraryUsageDescription");
  211. string photoLibraryUsageDescription = propPhotoLibraryUsageDescription.stringValue;
  212. SerializedProperty propPhotoLibraryAddUsageDescription = settings.FindProperty("_photoLibraryAddUsageDescription");
  213. string photoLibraryAddUsageDescription = propPhotoLibraryAddUsageDescription.stringValue;
  214. if ((photoLibraryUsageDescription == null || photoLibraryUsageDescription.Length == 0)
  215. && (photoLibraryAddUsageDescription == null || photoLibraryAddUsageDescription.Length == 0))
  216. {
  217. // No, nothing to see here
  218. return;
  219. }
  220. bool buildingApp = false;
  221. // Locate the Info.plist file
  222. string plistPath = null;
  223. if (path.EndsWith(".app"))
  224. {
  225. plistPath = Path.Combine(path, "Contents");
  226. buildingApp = true;
  227. }
  228. else
  229. {
  230. plistPath = Path.Combine(path, PlayerSettings.productName);
  231. }
  232. plistPath = Path.Combine(plistPath, "Info.plist");
  233. if (!File.Exists(plistPath))
  234. {
  235. Debug.LogWarning("Unable to locate Info.plist, you may need to add the following keys yourself:\n\tNSPhotoLibraryUsageDescription,\n\tNSPhotoLibraryAddUsageDescription");
  236. return;
  237. }
  238. Debug.Log("Modifying the Info.plist file at: " + plistPath);
  239. PlistDocumentProxy plist = new PlistDocumentProxy();
  240. plist.ReadFromFile(plistPath);
  241. if (photoLibraryUsageDescription != null && photoLibraryUsageDescription.Length > 0)
  242. {
  243. Debug.Log(" Adding 'NSPhotoLibraryUsageDescription' to Info.plist");
  244. plist.root.SetString("NSPhotoLibraryUsageDescription", photoLibraryUsageDescription);
  245. }
  246. if (photoLibraryAddUsageDescription != null && photoLibraryAddUsageDescription.Length > 0)
  247. {
  248. Debug.Log(" Adding 'NSPhotoLibraryAddUsageDescription' to Info.plist");
  249. plist.root.SetString("NSPhotoLibraryAddUsageDescription", photoLibraryAddUsageDescription);
  250. }
  251. File.WriteAllText(plistPath, plist.WriteToString());
  252. Debug.Log(" Finished modifying the Info.plist");
  253. if (buildingApp)
  254. {
  255. Debug.Log("Codesigning...");
  256. CodeSignAppBundle(path);
  257. }
  258. }
  259. // Code signs the app bundle.
  260. private static void CodeSignAppBundle(string path)
  261. {
  262. #if UNITY_EDITOR_OSX
  263. #if UNITY_2020_3_OR_NEWER
  264. UnityEditor.OSXStandalone.MacOSCodeSigning.CodeSignAppBundle(path);
  265. #else
  266. string cmd = string.Format("codesign -f -s - --deep {0}", path);
  267. string args = string.Format("-c \"{0}\"", cmd.Replace("\"", "\\\""));
  268. System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("/bin/sh");
  269. startInfo.Arguments = args;
  270. startInfo.RedirectStandardOutput = true;
  271. startInfo.RedirectStandardError = true;
  272. startInfo.UseShellExecute = false;
  273. startInfo.CreateNoWindow = true;
  274. System.Diagnostics.Process process = System.Diagnostics.Process.Start(startInfo);
  275. string result = process.StandardOutput.ReadToEnd();
  276. string error = process.StandardError.ReadToEnd();
  277. process.WaitForExit();
  278. if (error != null && error.Length > 0)
  279. {
  280. Debug.LogErrorFormat("[AVProMovieCapture] Failed to codesign app bundle, error: {0}", error);
  281. }
  282. else if (result != null && result.Length > 0)
  283. {
  284. Debug.LogFormat("[AVProMovieCapture] {0}", result);
  285. }
  286. #endif
  287. #else
  288. Debug.LogWarning("- Unable to codesign application");
  289. #endif
  290. }
  291. }
  292. } // namespace RenderHeads.Media.AVProMovieCapture.Editor
  293. #endif // UNITY_EDITOR && UNITY_2017_1_OR_NEWER