#if UNITY_EDITOR_OSX using UnityEngine; using UnityEditor; using UnityEditor.Build.Reporting; using UnityEditor.iOS.Xcode; using System.IO; #endif namespace BFEditor.Build { public static class BuildIOSUtils { #if UNITY_EDITOR_OSX const string IOS_DEFINE_SYMBOLS = "THREAD_SAFE;USE_AB"; const string DEV_XCODE_LOCAL_PATH = "BFVersions/ios/dev"; const string RELEASE_XCODE_LOCAL_PATH = "BFVersions/ios/release"; static string devXCodePath = Application.dataPath + "/../" + DEV_XCODE_LOCAL_PATH; static string releaseXCodePath = Application.dataPath + "/../" + RELEASE_XCODE_LOCAL_PATH; static string devOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/dev/ExportOptions.plist"; static string releaseOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/release/ExportOptions.plist"; static string publishOptionsPListPath = Application.dataPath + "/../" + "BFVersions/ios/exports/dis/ExportOptions.plist"; public static bool BuildIOSPlayer(BuildInfo buildInfo) { var buildTarget = BuildTarget.iOS; // 检查平台 if (EditorUserBuildSettings.activeBuildTarget != buildTarget) { Debug.LogError("[bferror]当前没有在对应平台"); return false; } // 重新生成XLua CompileScriptsUtils.RegenerateXLuaCode(true); // 打包设置 BuildSettings(buildInfo); // 开始打包 var bpOptions = GetBuildOptions(buildInfo); var report = BuildPipeline.BuildPlayer(bpOptions); if (report.summary.result == BuildResult.Succeeded) { return BuildIpaFromXCode(buildInfo); } else { Debug.LogError("[bferror]unity打包xcode失败"); return false; } } /// /// 打包设置 /// static void BuildSettings(BuildInfo buildInfo) { // 设置bundleVersion PlayerSettings.bundleVersion = buildInfo.version; // 设置buildNumber PlayerSettings.iOS.buildNumber = buildInfo.version_code.ToString(); // 设置竖屏 PlayerSettings.defaultInterfaceOrientation = UIOrientation.Portrait; PlayerSettings.allowedAutorotateToPortrait = false; PlayerSettings.allowedAutorotateToPortraitUpsideDown = false; PlayerSettings.allowedAutorotateToLandscapeLeft = false; PlayerSettings.allowedAutorotateToLandscapeRight = false; // 允许Xcode根据appleDeveloperTeamID自动签署应用程序 PlayerSettings.iOS.appleEnableAutomaticSigning = !buildInfo.IsPublish(); // 使用手动签名时iOS资源调配配置文件的类型,自动 PlayerSettings.iOS.iOSManualProvisioningProfileType = buildInfo.IsPublish() ? ProvisioningProfileType.Distribution : ProvisioningProfileType.Automatic; // 关闭启动动画 PlayerSettings.SplashScreen.show = false; // 设置包名 PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.iOS, buildInfo.bundleName); Debug.Log("[bfinfo]设置包名:" + buildInfo.bundleName); // 是否跳过版本控制 var symbols = IOS_DEFINE_SYMBOLS; if (buildInfo.skipVersion) { symbols = symbols + ";SKIP_VERSION;"; } PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.iOS, symbols); Debug.Log("[bfinfo]设置defineSymbols: " + symbols); // 是否是dev var development = buildInfo.IsDevChannel(); EditorUserBuildSettings.development = development; // 商品名称 if (buildInfo.IsPublish()) { PlayerSettings.productName = "Heroic Expedition"; } else { PlayerSettings.productName = development ? "b6-dev" : "b6-release"; } // BuildType设置dev/release EditorUserBuildSettings.iOSBuildConfigType = development ? iOSBuildType.Debug : iOSBuildType.Release; // 使用IL2CPP var scriptImp = ScriptingImplementation.IL2CPP; PlayerSettings.SetScriptingBackend(BuildTargetGroup.iOS, scriptImp); // 目标平台架构,目前支持ARM64 PlayerSettings.SetArchitecture(BuildTargetGroup.iOS, 1); } /// /// 获取打包参数 /// static BuildPlayerOptions GetBuildOptions(BuildInfo buildInfo) { var bpOptions = new BuildPlayerOptions(); bpOptions.scenes = AssetBundleUtils.GetBuildScenes(); bpOptions.target = BuildTarget.iOS; var path = buildInfo.IsReleaseChannel() ? RELEASE_XCODE_LOCAL_PATH : DEV_XCODE_LOCAL_PATH; bpOptions.locationPathName = path; Debug.Log("[bfinfo]xcode path : " + path); var absolutePath = buildInfo.IsReleaseChannel() ? releaseXCodePath : devXCodePath; if (Directory.Exists(absolutePath)) { Directory.Delete(absolutePath, true); } if (!buildInfo.IsPublish()) { var options = BuildOptions.Development | BuildOptions.ConnectWithProfiler | BuildOptions.AllowDebugging; bpOptions.options = options; } return bpOptions; } /// /// 打包ipa /// static bool BuildIpaFromXCode(BuildInfo buildInfo) { // 修改XCode设置 FixXCodeProject(buildInfo); // 权限 UnlockKeyChain(); // archive if (!Archive(buildInfo)) { return false; } // 导出ipa if (!ExportIpa(buildInfo)) { return false; } return true; } /// /// xCode工程设置 /// static void FixXCodeProject(BuildInfo buildInfo) { var isDev = buildInfo.IsDevChannel(); var xCodeProjectPath = isDev ? devXCodePath : releaseXCodePath; var path = xCodeProjectPath + "/Unity-iPhone.xcodeproj/project.pbxproj"; var pbxProject = new PBXProject(); pbxProject.ReadFromFile(path); var targetGuid = pbxProject.TargetGuidByName("Unity-iPhone"); pbxProject.AddBuildProperty(targetGuid, "OTHER_LDFLAGS", "-ObjC"); pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO"); pbxProject.SetBuildProperty(targetGuid, "ENABLE_BITCODE", "NO"); pbxProject.SetBuildProperty(targetGuid, "DEVELOPMENT_TEAM", "49QQW8856Q"); if (buildInfo.IsPublish()) { pbxProject.SetBuildProperty(targetGuid, "PROVISIONING_PROFILE_SPECIFIER", "ub_appstore_dis"); } // 添加系统库 AddSystemLibReferenceToProject(pbxProject, targetGuid, "libsqlite3.tbd"); AddSystemLibReferenceToProject(pbxProject, targetGuid, "libz.1.tbd"); AddSystemLibReferenceToProject(pbxProject, targetGuid, "libiconv.2.tbd"); AddSystemLibReferenceToProject(pbxProject, targetGuid, "libresolv.9.tbd"); var pListPath = Path.Combine(xCodeProjectPath, "Info.plist"); var pList = new PlistDocument(); pList.ReadFromFile(pListPath); // 版本号 var vKey = "CFBundleShortVersionString"; var vValue = new PlistElementString(buildInfo.version); var pListRoot = pList.root; var rootDict = pListRoot.values; if (!rootDict.ContainsKey(vKey)) { rootDict.Add(vKey, vValue); } else { rootDict[vKey] = vValue; } // VersionCode var vCodeKey = "CFBundleVersion"; var vCodeValue = new PlistElementString(buildInfo.version_code.ToString()); if (!rootDict.ContainsKey(vCodeKey)) { rootDict.Add(vCodeKey, vCodeValue); } else { rootDict[vCodeKey] = vCodeValue; } // 数美SDK会使用位置,必须加入这个说明 var localtionKey = "NSLocationWhenInUseUsageDescription"; var localtionValue = new PlistElementString("We use your location to give you a better localization."); if (!rootDict.ContainsKey(localtionKey)) { rootDict.Add(localtionKey, localtionValue); } else { rootDict[localtionKey] = localtionValue; } // 提审提示缺少出口合规证明,这里直接设置为false即可 var encryptionKey = "ITSAppUsesNonExemptEncryption"; var encryptionValue = new PlistElementBoolean(false); if (!rootDict.ContainsKey(encryptionKey)) { rootDict.Add(encryptionKey, encryptionValue); } else { rootDict[encryptionKey] = encryptionValue; } pList.WriteToFile(pListPath); pbxProject.WriteToFile(path); } //添加系统lib方法 static void AddSystemLibReferenceToProject(PBXProject pbxProject, string targetGuid, string lib) { var fileGuid = pbxProject.AddFile("usr/lib/" + lib, "Frameworks/" + lib, PBXSourceTree.Sdk); pbxProject.AddFileToBuild(targetGuid, fileGuid); } /// /// Archive /// static bool Archive(BuildInfo buildInfo) { Debug.Log("[bfinfo]正在archive..."); var result = true; var xCodeProjectPath = buildInfo.IsDevChannel() ? devXCodePath : releaseXCodePath; var archivePath = xCodeProjectPath + "/build/archive/Unity-iPhone.xcarchive"; var args = string.Format("archive -scheme Unity-iPhone -configuration Release -archivePath {0}", archivePath); BFEditorUtils.RunCommond("xcodebuild", args, xCodeProjectPath, (info) => { Debug.Log(info); }, (error) => { if (error.Contains("ARCHIVE FAILED")) // 失败标志 { result = false; } Debug.LogError("[bferror] " + error); } ); return result; } /// /// 导出ipa /// static bool ExportIpa(BuildInfo buildInfo) { Debug.Log("[bfinfo]正在导出ipa..."); var result = false; var xCodeProjectPath = buildInfo.IsDevChannel() ? devXCodePath : releaseXCodePath; string exportPListPath; if (buildInfo.IsPublish()) { exportPListPath = publishOptionsPListPath; } else { exportPListPath = buildInfo.IsDevChannel() ? devOptionsPListPath : releaseOptionsPListPath; } var ipaPath = xCodeProjectPath + "/build/ipa"; var archivePath = xCodeProjectPath + "/build/archive/Unity-iPhone.xcarchive"; var args = string.Format("-exportArchive -archivePath {0} -exportPath {1} -exportOptionsPlist {2} -allowProvisioningUpdates", archivePath, ipaPath, exportPListPath); BFEditorUtils.RunCommond("xcodebuild", args, null, (info) => { if(info.Contains("EXPORT SUCCEEDED")) { result = true; } }, (error) => { } ); return result; } /// /// 远程打包签名ipa时需要钥匙串权限 /// static void UnlockKeyChain() { BFEditorUtils.RunCommond("security", "-v unlock-keychain -p '123456' /Users/aoddabao/Library/Keychains/login.keychain-db", null, (msg) => { Debug.Log(msg); }, (msg) => { Debug.LogError(msg); } ); } #endif } }