PostProcessBuild_iOS.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #if (UNITY_IOS || UNITY_TVOS) && UNITY_2017_1_OR_NEWER
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEditor;
  5. using UnityEditor.Callbacks;
  6. using UnityEditor.iOS.Xcode;
  7. using UnityEditor.iOS.Xcode.Extensions;
  8. //-----------------------------------------------------------------------------
  9. // Copyright 2012-2021 RenderHeads Ltd. All rights reserved.
  10. //-----------------------------------------------------------------------------
  11. namespace RenderHeads.Media.AVProVideo.Editor
  12. {
  13. public class PostProcessBuild_iOS
  14. {
  15. const string PluginName = "AVProVideo.framework";
  16. // Simple holder for major.minor version number
  17. private readonly struct Version
  18. {
  19. public static Version unknown = new Version(0, 0);
  20. public static Version _10_0 = new Version(10, 0);
  21. public static Version _12_2 = new Version(12, 2);
  22. public readonly int major;
  23. public readonly int minor;
  24. public Version(int major, int minor)
  25. {
  26. this.major = major;
  27. this.minor = minor;
  28. }
  29. public static bool operator ==(Version lhs, Version rhs) => (lhs.major == rhs.major && lhs.minor == rhs.minor);
  30. public static bool operator !=(Version lhs, Version rhs) => (lhs.major != rhs.major || lhs.minor != rhs.minor);
  31. public static bool operator <(Version lhs, Version rhs) => (lhs.major < rhs.major) || (lhs.major == rhs.major && lhs.minor < rhs.minor);
  32. public static bool operator >(Version lhs, Version rhs) => (lhs.major > rhs.major) || (lhs.major == rhs.major && lhs.minor > rhs.minor);
  33. public override bool Equals(object obj) { return base.Equals(obj); }
  34. public override int GetHashCode() { return base.GetHashCode(); }
  35. public override string ToString() { return string.Format("{0}.{1}", major, minor); }
  36. }
  37. private class Platform
  38. {
  39. public BuildTarget target { get; }
  40. public string name { get; }
  41. public string guid { get; }
  42. // Accessor for PlayerSettings.Platform.targetOSVersionString
  43. public string targetOSVersionString
  44. {
  45. get
  46. {
  47. switch (target)
  48. {
  49. case BuildTarget.iOS:
  50. return PlayerSettings.iOS.targetOSVersionString;
  51. case BuildTarget.tvOS:
  52. return PlayerSettings.tvOS.targetOSVersionString;
  53. default:
  54. return null;
  55. }
  56. }
  57. }
  58. // Will lazily set version from targetOSVersionString when called for the first time
  59. private Version _version = Version.unknown;
  60. public Version targetOSVersion
  61. {
  62. get
  63. {
  64. if (_version == Version.unknown)
  65. {
  66. if (targetOSVersionString != null)
  67. {
  68. string[] version = targetOSVersionString.Split('.');
  69. if (version != null && version.Length >= 1)
  70. {
  71. int major = 0;
  72. if (int.TryParse(version[0], out major) && version.Length >= 2)
  73. {
  74. int minor = 0;
  75. if (int.TryParse(version[1], out minor))
  76. {
  77. _version = new Version(major, minor);
  78. }
  79. }
  80. }
  81. }
  82. if (_version == Version.unknown)
  83. {
  84. // 10.0 is the minumum version we support so default to this
  85. _version = Version._10_0;
  86. }
  87. }
  88. return _version;
  89. }
  90. }
  91. public static Platform GetPlatformForTarget(BuildTarget target)
  92. {
  93. switch (target)
  94. {
  95. case BuildTarget.iOS:
  96. return new Platform(BuildTarget.iOS, "iOS", "2a1facf97326449499b63c03811b1ab2");
  97. case BuildTarget.tvOS:
  98. return new Platform(BuildTarget.tvOS, "tvOS", "bcf659e3a94d748d6a100d5531540d1a");
  99. default:
  100. return null;
  101. }
  102. }
  103. private Platform(BuildTarget target, string name, string guid)
  104. {
  105. this.target = target;
  106. this.name = name;
  107. this.guid = guid;
  108. }
  109. }
  110. private static string PluginPathForPlatform(Platform platform)
  111. {
  112. // See if we can find the plugin by GUID
  113. string pluginPath = AssetDatabase.GUIDToAssetPath(platform.guid);
  114. // If not, try and find it by name
  115. if (pluginPath.Length == 0)
  116. {
  117. Debug.LogWarningFormat("[AVProVideo] Failed to find plugin by GUID, will attempt to find it by name.");
  118. string[] guids = AssetDatabase.FindAssets(PluginName);
  119. if (guids != null && guids.Length > 0)
  120. {
  121. foreach (string guid in guids)
  122. {
  123. string assetPath = AssetDatabase.GUIDToAssetPath(guid);
  124. if (assetPath.Contains(platform.name))
  125. {
  126. pluginPath = assetPath;
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. if (pluginPath.Length > 0)
  133. {
  134. Debug.LogFormat("[AVProVideo] Found plugin at '{0}'", pluginPath);
  135. }
  136. return pluginPath;
  137. }
  138. // Converts the Unity asset path to the expected path in the built Xcode project.
  139. private static string ConvertPluginAssetPathToXcodeProjectFrameworkPath(string pluginPath)
  140. {
  141. List<string> components = new List<string>(pluginPath.Split(new char[] { '/' }));
  142. components[0] = "Frameworks";
  143. #if UNITY_2019_1_OR_NEWER
  144. string frameworkPath = string.Join("/", components);
  145. #else
  146. string frameworkPath = string.Join("/", components.ToArray());
  147. #endif
  148. return frameworkPath;
  149. }
  150. // Helper to set the file execute bits
  151. private static void SetFileExecutePermission(string path)
  152. {
  153. #if UNITY_EDITOR_OSX
  154. Debug.LogFormat("[AVProVideo] Checking permissions on {0}", path);
  155. string cmd = string.Format("if [ ! -x \"{0}\" ]; then echo \"Missing execute permissions, fixing...\"; chmod a+x \"{0}\"; else echo \"All good\"; fi", path);
  156. string args = string.Format("-c \"{0}\"", cmd.Replace("\"", "\\\""));
  157. System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo("/bin/sh");
  158. startInfo.Arguments = args;
  159. startInfo.RedirectStandardOutput = true;
  160. startInfo.RedirectStandardError = true;
  161. startInfo.UseShellExecute = false;
  162. startInfo.CreateNoWindow = true;
  163. System.Diagnostics.Process process = System.Diagnostics.Process.Start(startInfo);
  164. string result = process.StandardOutput.ReadToEnd();
  165. string error = process.StandardError.ReadToEnd();
  166. process.WaitForExit();
  167. if (error != null && error.Length > 0)
  168. {
  169. Debug.LogErrorFormat("[AVProVideo] Failed to set execute permissions on the plugin binary, error: {0}", error);
  170. }
  171. else if (result != null && result.Length > 0)
  172. {
  173. Debug.LogFormat("[AVProVideo] {0}", result);
  174. }
  175. #else
  176. Debug.LogWarningFormat("[AVProVideo] Project is not being built on macOS so we are unable to check the file permissions on the plugin binary. You need to make sure that the execute bits are set on \"AVProVideo.framework/AVProVideo\" before building the Xcode project.");
  177. #endif
  178. }
  179. [PostProcessBuild]
  180. public static void ModifyProject(BuildTarget target, string path)
  181. {
  182. if (target != BuildTarget.iOS && target != BuildTarget.tvOS)
  183. return;
  184. Debug.Log("[AVProVideo] Post-processing Xcode project.");
  185. Platform platform = Platform.GetPlatformForTarget(target);
  186. if (platform == null)
  187. {
  188. Debug.LogWarningFormat("[AVProVideo] Unknown build target: {0}", target.ToString());
  189. return;
  190. }
  191. string projectPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
  192. PBXProject project = new PBXProject();
  193. project.ReadFromFile(projectPath);
  194. // Attempt to find the plugin path
  195. string pluginPath = PluginPathForPlatform(platform);
  196. if (pluginPath.Length > 0)
  197. {
  198. #if UNITY_2019_3_OR_NEWER
  199. string targetGuid = project.GetUnityMainTargetGuid();
  200. #else
  201. string targetGuid = project.TargetGuidByName(PBXProject.GetUnityTargetName());
  202. #endif
  203. string frameworkPath = ConvertPluginAssetPathToXcodeProjectFrameworkPath(pluginPath);
  204. string fileGuid = project.FindFileGuidByProjectPath(frameworkPath);
  205. if (fileGuid != null)
  206. {
  207. // Make sure the plugin binary has execute permissions set.
  208. // For reasons unknown these are being lost somewhere between the plugin package being built and imported from the asset store.
  209. string binaryPath = System.IO.Path.Combine(path, frameworkPath, "AVProVideo");
  210. SetFileExecutePermission(binaryPath);
  211. Debug.LogFormat("[AVProVideo] Adding 'AVProVideo.framework' to the list of embedded frameworks");
  212. PBXProjectExtensions.AddFileToEmbedFrameworks(project, targetGuid, fileGuid);
  213. Debug.LogFormat("[AVProVideo] Setting 'LD_RUNPATH_SEARCH_PATHS' to '$(inherited) @executable_path/Frameworks'");
  214. project.SetBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", "$(inherited) @executable_path/Frameworks");
  215. }
  216. else
  217. {
  218. Debug.LogWarningFormat("[AVProVideo] Failed to find {0} in the generated project. You will need to manually set {0} to 'Embed & Sign' in the Xcode project's framework list.", PluginName);
  219. }
  220. // See if we need to enable embedding of Swift binaries
  221. if (platform.targetOSVersion < Version._12_2)
  222. {
  223. Debug.LogFormat("[AVProVideo] Target OS version '{0}' is < 12.2, setting 'ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES' to 'YES'", platform.targetOSVersion);
  224. project.SetBuildProperty(targetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
  225. }
  226. Debug.LogFormat("[AVProVideo] Writing out Xcode project file");
  227. project.WriteToFile(projectPath);
  228. }
  229. else
  230. {
  231. Debug.LogErrorFormat("Failed to find '{0}' for '{1}' in the Unity project. Something is horribly wrong, please reinstall AVPro Video.", PluginName, platform);
  232. }
  233. }
  234. }
  235. }
  236. #endif