#if UNITY_EDITOR using System.IO; using UnityEngine; using UnityEditor; using UnityEditor.Callbacks; using UnityEditor.Build; #if UNITY_2018_1_OR_NEWER using UnityEditor.Build.Reporting; #endif #if UNITY_IOS using UnityEditor.iOS.Xcode; #endif namespace MoreMountains.NiceVibrations { /// /// This class takes care of post processing after a build, adding properties to the XCode project or framework depending on the Unity version. /// Note that this class tries to guess paths to certain files, and while this will work in most cases, you may be in a configuration where it doesn't. /// If needed, you can override paths manually by editing the scriptable object located in NiceVibrations/Common/Resources, and called /// MMNVPathDefinition. Don't change its name! Just select it, and from the editor, specify your paths overrides. /// public class MMNVBuildPostProcessor : IPostprocessBuildWithReport { public static readonly char SEPARATOR = '/'; /// the order at which this build post processor should run public int callbackOrder => 1; /// /// On post process build, we run our treatment, and throw an error if necessary /// /// public void OnPostprocessBuild(BuildReport report) { try { if (report.summary.platform == BuildTarget.iOS) { string path = report.summary.outputPath; #if UNITY_IOS && UNITY_EDITOR ConfigureXCodeProjectForNativePlugin(path); #endif } } catch (System.Exception e) { throw new BuildFailedException(e); // make sure the build fails } } /// /// This method tries to guess the path to the plugin /// /// public static string GetPluginPath() { #if UNITY_IOS && UNITY_EDITOR string[] res = System.IO.Directory.GetFiles(Application.dataPath, "MMNVBuildPostProcessor.cs", SearchOption.AllDirectories); if (res.Length == 0) { return "error"; } string path = res[0].Replace("MMNVBuildPostProcessor.cs", "") .Replace("\\", SEPARATOR.ToString()) .Replace("/", SEPARATOR.ToString()) .Replace("" + SEPARATOR + SEPARATOR + "", SEPARATOR.ToString()); string[] explodedPath = path.Split(SEPARATOR); string finalPath = "Libraries"+SEPARATOR; bool assetsFound = false; for (int i = 0; i < explodedPath.Length; i++) { if (!assetsFound) { if (explodedPath[i] == "Assets") { assetsFound = true; } } else { finalPath += explodedPath[i] + SEPARATOR; } } finalPath = finalPath.Replace("" + SEPARATOR + SEPARATOR + "", SEPARATOR.ToString()); finalPath = finalPath.Replace("Common"+SEPARATOR+"Scripts"+SEPARATOR+"Editor", "Common"+SEPARATOR+"Plugins"+SEPARATOR+"iOS"+SEPARATOR+"Swift"+SEPARATOR); finalPath = finalPath.Replace(""+SEPARATOR+SEPARATOR+"", SEPARATOR.ToString()); Debug.Log("[MMNVBuildPostProcessor] Final path : " + finalPath); if (!finalPath.Contains("Common"+SEPARATOR+"Plugins"+ SEPARATOR+"iOS"+ SEPARATOR+"Swift")) { Debug.Log("[MMNVBuildPostProcessor] Path not found :("); return "path not found"; } Debug.Log("[MMNVBuildPostProcessor] Final path : " + finalPath); return finalPath; #else return "not iOS"; #endif } #if UNITY_IOS && UNITY_EDITOR public static void ConfigureXCodeProjectForNativePlugin(string xcodeProjectPath) { string pluginPath = GetPluginPath(); // we check if we can find a manual path override MMNVPath pathDefinition = Resources.Load("MMNVPathDefinition"); bool forceSwiftForFramework = false; bool forceSwiftForMainTarget = false; if (pathDefinition != null) { if (pathDefinition.PluginPath != "") { pluginPath = pathDefinition.PluginPath; } forceSwiftForFramework = pathDefinition.ForceAlwaysEmbedSwiftSLForFramework; forceSwiftForMainTarget = pathDefinition.ForceAlwaysEmbedSwiftSLForMainTarget; } string pbxProjectPath = PBXProject.GetPBXProjectPath(xcodeProjectPath); PBXProject pbxProject = new PBXProject(); pbxProject.ReadFromString(File.ReadAllText(pbxProjectPath)); #if UNITY_2019_3_OR_NEWER string targetGUID = pbxProject.GetUnityFrameworkTargetGuid(); WritePropertiesToFramework(pbxProject, targetGUID, pbxProjectPath, pluginPath, forceSwiftForFramework); targetGUID = pbxProject.GetUnityMainTargetGuid(); WritePropertiesToMainTarget(pbxProject, targetGUID, pbxProjectPath, pluginPath, forceSwiftForMainTarget); #else string unityTargetName = PBXProject.GetUnityTargetName(); string targetGUID = pbxProject.TargetGuidByName(unityTargetName); WritePropertiesToProject(pbxProject, targetGUID, pbxProjectPath, pluginPath, forceAlwaysEmbedSwiftStandardLibraries); #endif File.WriteAllText(pbxProjectPath, pbxProject.WriteToString()); Debug.Log("[MMNVBuildPostProcessor] Post process complete."); } /// /// Writes properties to the XCode project /// /// /// /// /// private static void WritePropertiesToProject(PBXProject pbxProject, string targetGUID, string pbxProjectPath, string pluginPath, bool forceAlwaysEmbedSwiftStandardLibraries) { pbxProject.AddFrameworkToProject(targetGUID, "CoreHaptics.framework", false); pbxProject.AddBuildProperty(targetGUID, "SWIFT_VERSION", "5.1"); pbxProject.SetBuildProperty(targetGUID, "ENABLE_BITCODE", "NO"); pbxProject.SetBuildProperty(targetGUID, "SWIFT_OBJC_BRIDGING_HEADER", pluginPath + "UnitySwift-Bridging-Header.h"); pbxProject.SetBuildProperty(targetGUID, "SWIFT_OBJC_INTERFACE_HEADER_NAME", "unityswift-Swift.h"); pbxProject.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks"); if (forceAlwaysEmbedSwiftStandardLibraries) { pbxProject.AddBuildProperty(targetGUID, "EMBEDDED_CONTENT_CONTAINS_SWIFT", "YES"); pbxProject.AddBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES"); } File.WriteAllText(pbxProjectPath, pbxProject.WriteToString()); Debug.Log("[MMNVBuildPostProcessor] Adding properties to XCode project, plugin path : " + pluginPath); } const string _privateModuleFilename = "module.modulemap"; /// /// Writes properties to the XCode framework /// /// /// /// /// private static void WritePropertiesToFramework(PBXProject pbxProject, string targetGUID, string pbxProjectPath, string pluginPath, bool forceAlwaysEmbedSwiftStandardLibraries) { string privateModuleFilename = _privateModuleFilename; string pluginRelativePath = pluginPath.Substring(10, pluginPath.Length - 10); // remove 'Libraries/' // we look for manual path overrides MMNVPath pathDefinition = Resources.Load("MMNVPathDefinition"); if (pathDefinition != null) { if (pathDefinition.ModuleFileName != "") { privateModuleFilename = pathDefinition.ModuleFileName; } if (pathDefinition.PluginRelativePath != "") { pluginRelativePath = pathDefinition.PluginRelativePath; } } Debug.Log("[MMNVBuildPostProcessor] module relative path in Unity project: " + pluginRelativePath); // Full Path to copy from string module_map_filepath = pluginPath + privateModuleFilename; Debug.Log("[MMNVBuildPostProcessor] Adding properties to XCode framework, module path : " + module_map_filepath); pbxProject.AddFrameworkToProject(targetGUID, "CoreHaptics.framework", false); pbxProject.AddBuildProperty(targetGUID, "SWIFT_VERSION", "5.1"); pbxProject.SetBuildProperty(targetGUID, "ENABLE_BITCODE", "NO"); if (forceAlwaysEmbedSwiftStandardLibraries) { pbxProject.SetBuildProperty(targetGUID, "EMBEDDED_CONTENT_CONTAINS_SWIFT", "YES"); pbxProject.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES"); } pbxProject.AddBuildProperty(targetGUID, "CLANG_ENABLE_MODULES", "YES"); pbxProject.AddBuildProperty(targetGUID, "SWIFT_INCLUDE_PATHS", pluginPath); pbxProject.AddBuildProperty(targetGUID, "LD_RUNPATH_SEARCH_PATHS", "@executable_path/Frameworks"); // we add a module reference to the pbx project string file_guid = pbxProject.AddFile(module_map_filepath, module_map_filepath, PBXSourceTree.Source); pbxProject.AddFileToBuild(targetGUID, file_guid); File.WriteAllText(pbxProjectPath, pbxProject.WriteToString()); // we copy the module file to the project string privateModuleFilepath = Application.dataPath + SEPARATOR + pluginRelativePath + SEPARATOR + privateModuleFilename; string projFileDir = System.IO.Path.GetDirectoryName(pbxProjectPath); string destination = projFileDir + SEPARATOR+".."+ SEPARATOR + module_map_filepath; if (!Directory.Exists (Path.GetDirectoryName(destination))) { Debug.Log("[MMNVBuildPostProcessor] Creating directory "+destination); Directory.CreateDirectory(Path.GetDirectoryName(destination)); } Debug.Log("[MMNVBuildPostProcessor] Copy module file to project : " + privateModuleFilepath + " -> " + destination); System.IO.File.Copy(privateModuleFilepath, destination); } /// /// Writes properties to the main target /// private static void WritePropertiesToMainTarget(PBXProject pbxProject, string targetGUID, string pbxProjectPath, string pluginPath, bool forceAlwaysEmbedSwiftStandardLibraries) { if (forceAlwaysEmbedSwiftStandardLibraries) { pbxProject.SetBuildProperty(targetGUID, "EMBEDDED_CONTENT_CONTAINS_SWIFT", "YES"); pbxProject.SetBuildProperty(targetGUID, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES"); } } #endif } } #endif