//----------------------------------------------------------------------------- // Copyright 2012-2024 RenderHeads Ltd. All rights reserved. //----------------------------------------------------------------------------- #if (UNITY_IOS || UNITY_TVOS || UNITY_VISIONOS) && UNITY_2017_1_OR_NEWER // Unity versions where xcframework support was added // 2023.2.18f1 // 2022.3.23f1 // 2021.3.37f1 // There has to be a better way... #if UNITY_2023_2_OR_NEWER && !(UNITY_2023_2_0 || UNITY_2023_2_1 || UNITY_2023_2_2 || UNITY_2023_2_3 || UNITY_2023_2_4 || UNITY_2023_2_5 || UNITY_2023_2_6 || UNITY_2023_2_7 || UNITY_2023_2_8 || UNITY_2023_2_9 || UNITY_2023_2_10 || UNITY_2023_2_11 || UNITY_2023_2_12 || UNITY_2023_2_13 || UNITY_2023_2_14 || UNITY_2023_2_15 || UNITY_2023_2_16 || UNITY_2023_2_17) #define AVPROVIDEO_UNITY_SUPPORTS_XCFRAMEWORKS #elif UNITY_2023_1_OR_NEWER #define AVPROVIDEO_UNITY_DOES_NOT_SUPPORT_XCFRAMEWORKS #elif UNITY_2022_3_OR_NEWER && !(UNITY_2022_3_0 || UNITY_2022_3_1 || UNITY_2022_3_2 || UNITY_2022_3_3 || UNITY_2022_3_4 || UNITY_2022_3_5 || UNITY_2022_3_6 || UNITY_2022_3_7 || UNITY_2022_3_8 || UNITY_2022_3_9 || UNITY_2022_3_10 || UNITY_2022_3_11 || UNITY_2022_3_12 || UNITY_2022_3_13 || UNITY_2022_3_14 || UNITY_2022_3_15 || UNITY_2022_3_16 || UNITY_2022_3_17 || UNITY_2022_3_18 || UNITY_2022_3_19 || UNITY_2022_3_20 || UNITY_2022_3_21 || UNITY_2022_3_22) #define AVPROVIDEO_UNITY_SUPPORTS_XCFRAMEWORKS #elif UNITY_2022_1_OR_NEWER #define AVPROVIDEO_UNITY_DOES_NOT_SUPPORT_XCFRAMEWORKS #elif UNITY_2021_3_OR_NEWER && !(UNITY_2021_3_0 || UNITY_2021_3_1 || UNITY_2021_3_2 || UNITY_2021_3_3 || UNITY_2021_3_4 || UNITY_2021_3_5 || UNITY_2021_3_6 || UNITY_2021_3_7 || UNITY_2021_3_8 || UNITY_2021_3_9 || UNITY_2021_3_10 || UNITY_2021_3_11 || UNITY_2021_3_12 || UNITY_2021_3_13 || UNITY_2021_3_14 || UNITY_2021_3_15 || UNITY_2021_3_16 || UNITY_2021_3_17 || UNITY_2021_3_18 || UNITY_2021_3_19 || UNITY_2021_3_20 || UNITY_2021_3_21 || UNITY_2021_3_22 || UNITY_2021_3_23 || UNITY_2021_3_24 || UNITY_2021_3_25 || UNITY_2021_3_26 || UNITY_2021_3_27 || UNITY_2021_3_28 || UNITY_2021_3_29 || UNITY_2021_3_30 || UNITY_2021_3_31 || UNITY_2021_3_32 || UNITY_2021_3_33 || UNITY_2021_3_34 || UNITY_2021_3_35 || UNITY_2021_3_36) #define AVPROVIDEO_UNITY_SUPPORTS_XCFRAMEWORKS #else #define AVPROVIDEO_UNITY_DOES_NOT_SUPPORT_XCFRAMEWORKS #endif #if UNITY_2022_3 || UNITY_6000_0_OR_NEWER #define UNITY_SUPPORTS_VISIONOS #endif using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.iOS.Xcode; using System; using System.Collections.Generic; using System.IO; namespace RenderHeads.Media.AVProVideo.Editor { public class PostProcessBuild_iOS { const string AVProVideoPluginName = "AVProVideo.xcframework"; const string AVProVideoBootstrap = "extern void AVPPluginUnityRegisterRenderingPlugin(void*);\nvoid AVPPluginBootstrap(void) {\n\tAVPPluginUnityRegisterRenderingPlugin(UnityRegisterRenderingPluginV5);\n}\n"; const string AVProVideoForceSwift = "import Foundation\n"; private class Platform { public BuildTarget target { get; } public string name { get; } public string guid { get; } public static Platform GetPlatformForTarget(BuildTarget target) { switch (target) { case BuildTarget.iOS: return new Platform(BuildTarget.iOS, "iOS", "a7ee58e0e533849d3a37458bc7df6df7"); case BuildTarget.tvOS: return new Platform(BuildTarget.tvOS, "tvOS", "f83f62879d8fb417cb18d0547c9bfd02"); #if UNITY_SUPPORTS_VISIONOS case BuildTarget.VisionOS: return new Platform(BuildTarget.VisionOS, "visionOS", "fe151797423674af0941aae11c872b90"); #endif default: return null; } } private Platform(BuildTarget target, string name, string guid) { this.target = target; this.name = name; this.guid = guid; } } /// /// Get the plugin path for the platform specified /// /// The platform /// The plugin's file name /// The path of the plugin within Unity's assets folder private static string PluginPathForPlatform(Platform platform, string pluginName) { // See if we can find the plugin by GUID string pluginPath = AssetDatabase.GUIDToAssetPath(platform.guid); // If not, try and find it by name if (pluginPath.Length == 0) { Debug.LogWarningFormat("[AVProVideo] Failed to find plugin by GUID, will attempt to find it by name."); string[] guids = AssetDatabase.FindAssets(pluginName); if (guids != null && guids.Length > 0) { foreach (string guid in guids) { string assetPath = AssetDatabase.GUIDToAssetPath(guid); if (assetPath.Contains(platform.name)) { pluginPath = assetPath; break; } } } } if (pluginPath.Length > 0) { Debug.LogFormat("[AVProVideo] Found plugin at '{0}'", pluginPath); } return pluginPath; } /// /// Gets the target guid if Unity's framework target from the project provided /// /// The project to get the guid from /// private static string GetUnityFrameworkTargetGuid(PBXProject project) { return project.GetUnityFrameworkTargetGuid(); } /// /// Copies a directory. /// /// /// Intended for use outside of Unity's project structure, this will skip meta files when copying. /// /// The directory info of the directory to copy /// The directory info of the destination directory private static void CopyDirectory(DirectoryInfo srcDirInfo, DirectoryInfo dstDirInfo) { // Make sure the target directory exists Directory.CreateDirectory(dstDirInfo.FullName); // Copy over the sub-directories foreach (DirectoryInfo subSrcDirInfo in srcDirInfo.GetDirectories()) { DirectoryInfo subDstDirInfo = dstDirInfo.CreateSubdirectory(subSrcDirInfo.Name); CopyDirectory(subSrcDirInfo, subDstDirInfo); } // Copy over the files foreach (FileInfo srcFileInfo in srcDirInfo.GetFiles()) { if (srcFileInfo.Extension == ".meta") { // Do not want to copy Unity's meta files into the built project continue; } else if (srcFileInfo.Name == ".DS_Store") { // Do not want to copy .DS_Store files into the built project continue; } else { srcFileInfo.CopyTo(Path.Combine(dstDirInfo.FullName, srcFileInfo.Name), true); } } } /// /// Copies a directory. /// /// /// Intended for use outside of Unity's project structure, this will skip meta files when copying. /// /// The path of the directory to copy /// The path where the directory will be copied to private static void CopyDirectory(string src, string dst) { CopyDirectory(new DirectoryInfo(src), new DirectoryInfo(dst)); } /// /// Tests the target build platform to see if it's supported by this script /// /// The target build platform /// true if the build target is supported, false otherwise private static bool IsBuildTargetSupported(BuildTarget target) { switch (target) { case BuildTarget.iOS: case BuildTarget.tvOS: #if UNITY_SUPPORTS_VISIONOS case BuildTarget.VisionOS: #endif return true; default: return false; } } /// /// Gets the Xcode project name for the target specified. /// /// The build target /// The Xcode project name private static string GetXcodeProjectNameForBuildTarget(BuildTarget target) { switch (target) { case BuildTarget.iOS: case BuildTarget.tvOS: return "Unity-iPhone.xcodeproj"; #if UNITY_SUPPORTS_VISIONOS case BuildTarget.VisionOS: return "Unity-VisionOS.xcodeproj"; #endif default: Debug.LogError($"[AVProVideo] GetXcodeProjectNameForBuildTarget - unrecognised build target: {target}"); return null; } } // Converts the Unity asset path to the expected path in the built Xcode project. private static string ConvertPluginAssetPathToXcodeProjectPath(string pluginPath, string subFolder) { List components = new List(pluginPath.Split(new char[] { '/' })); #if UNITY_TVOS // Unity just copies the xcframework into the frameworks folder string frameworkPath = Path.Combine(subFolder, components[^1]); #else #if UNITY_VISIONOS // For reasons unknown unity puts everything under an ARM64 folder on visionOS components.Insert(0, "ARM64"); components.Insert(0, subFolder); #else components[0] = subFolder; #endif #if UNITY_2019_1_OR_NEWER string frameworkPath = string.Join("/", components); #else string frameworkPath = string.Join("/", components.ToArray()); #endif #endif return frameworkPath; } // private static void StripMetaFilesFromDirectory(DirectoryInfo dirInfo) { // Remove any meta files foreach (FileInfo srcFileInfo in dirInfo.GetFiles()) { if (srcFileInfo.Extension == ".meta") { Debug.Log($"[AVProVideo] Deleting {srcFileInfo.FullName}"); File.Delete(srcFileInfo.FullName); } else if (srcFileInfo.Name == ".DS_Store") { Debug.Log($"[AVProVideo] Deleting {srcFileInfo.FullName}"); File.Delete(srcFileInfo.FullName); } } // Do the same for any sub-directories foreach (DirectoryInfo subDirInfo in dirInfo.GetDirectories("*")) { StripMetaFilesFromDirectory(subDirInfo); } } /// /// Post-process the generated Xcode project to add the plugin and any build configuration required. /// /// The target build platform /// The path to the built project [PostProcessBuild] public static void ModifyProject(BuildTarget target, string path) { if (!IsBuildTargetSupported(target)) { // Nothing to be done return; } Debug.Log("[AVProVideo] Post-processing generated Xcode project..."); Platform platform = Platform.GetPlatformForTarget(target); if (platform == null) { Debug.LogWarningFormat("[AVProVideo] Unknown build target: {0}, stopping", target); return; } // Create the path to the generated Xcode project file string xcodeProjectName = GetXcodeProjectNameForBuildTarget(target); if (xcodeProjectName == null) { return; } string xcodeProjectPath = Path.Combine(path, xcodeProjectName, "project.pbxproj"); Debug.Log($"[AVProVideo] Opening Xcode project at: {path}"); // Open the project PBXProject project = new PBXProject(); project.ReadFromFile(xcodeProjectPath); // Attempt to find the plugin path string pluginPath = PluginPathForPlatform(platform, AVProVideoPluginName); if (pluginPath.Length == 0) { Debug.LogErrorFormat("[AVProVideo] Failed to find '{0}' for '{1}' in the Unity project. Something is horribly wrong, please reinstall AVPro Video.", AVProVideoPluginName, platform); return; } Debug.Log($"[AVProVideo] Plugin path: {pluginPath}"); string destPluginPath = Path.Combine("Libraries", "AVProVideo"); Directory.CreateDirectory(Path.Combine(path, destPluginPath)); // Get the Unity framework target GUID string unityFrameworkTargetGuid = GetUnityFrameworkTargetGuid(project); #if AVPROVIDEO_UNITY_DOES_NOT_SUPPORT_XCFRAMEWORKS // Get the path to the xcframework string xcframeworkPath = Path.Combine(destPluginPath, AVProVideoPluginName); // Copy over the xcframework to the generated xcode project Debug.Log($"[AVProVideo] Copying AVProVideo.xcframework into the Xcode project at {destPluginPath}"); CopyDirectory(pluginPath, Path.Combine(path, xcframeworkPath)); if (!project.ContainsFileByProjectPath(xcframeworkPath)) { Debug.Log("[AVProVideo] Adding AVProVideo.xcframework to the UnityFramework target"); // Add the xcframework and sundry files to the project string xcframeworkGuid = project.AddFile(xcframeworkPath, xcframeworkPath); // Get the frameworks build phase and add the xcframework to it string frameworksBuildPhaseForUnityFrameworkTarget = project.GetFrameworksBuildPhaseByTarget(unityFrameworkTargetGuid); project.AddFileToBuildSection(unityFrameworkTargetGuid, frameworksBuildPhaseForUnityFrameworkTarget, xcframeworkGuid); } #else // If we've upgraded Unity to a version that supports xcframeworks we need to purge the meta files from the plugin // string xcframeworkPath = ConvertPluginAssetPathToXcodeProjectPath(pluginPath, "Frameworks"); string xcframeworkPath = string.Empty; IReadOnlyList paths = project.GetRealPathsOfAllFiles(PBXSourceTree.Source); foreach (string p in paths) { if (p.EndsWith(AVProVideoPluginName)) { xcframeworkPath = p; break; } } if (!string.IsNullOrEmpty(xcframeworkPath)) { string dest_xcframeworkPath = Path.Combine(path, xcframeworkPath); Debug.Log($"[AVProVideo] xcframework path is: {dest_xcframeworkPath}"); StripMetaFilesFromDirectory(new DirectoryInfo(dest_xcframeworkPath)); } // string dest_xcframeworkPath = Path.Combine(path, xcframeworkPath); // if (!project.ContainsFileByProjectPath(xcframeworkPath)) // { // Debug.Log("[AVProVideo] Stripping meta files from AVProVideo.xcframework"); // StripMetaFilesFromDirectory(new DirectoryInfo(dest_xcframeworkPath)); // } else { Debug.LogError($"[AVProVideo] Failed to find AVProPlugin.xcframework in the built project"); } #endif Debug.Log("[AVProVideo] Writing AVProVideoBootstrap.m to the UnityFramework target"); string bootstrapPath = Path.Combine(destPluginPath, "AVProVideoBootstrap.m"); File.WriteAllText(Path.Combine(path, bootstrapPath), AVProVideoBootstrap); string bootstrapGuid = project.AddFile(bootstrapPath, bootstrapPath); project.AddFileToBuild(unityFrameworkTargetGuid, bootstrapGuid); string forceSwiftPath = Path.Combine(destPluginPath, "AVProVideoForceSwift.swift"); Debug.Log("[AVProVideo] Writing AVProVideoForceSwift.swift to the UnityFramework target"); File.WriteAllText(Path.Combine(path, forceSwiftPath), AVProVideoForceSwift); string forceSwiftGuid = project.AddFile(forceSwiftPath, forceSwiftPath); project.AddFileToBuild(unityFrameworkTargetGuid, forceSwiftGuid); // Make sure the swift version is set to 5.0 string swiftVersionStr = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "SWIFT_VERSION"); decimal swiftVersion; if (!Decimal.TryParse(swiftVersionStr, out swiftVersion) || (swiftVersion < 5)) { Debug.Log("[AVProVideo] setting SWIFT_VERSION to 5.0 for the UnityFramework target"); project.SetBuildProperty(unityFrameworkTargetGuid, "SWIFT_VERSION", "5.0"); } // Done project.WriteToFile(xcodeProjectPath); Debug.Log("[AVProVideo] Finished modifying Xcode project"); } } } #endif