using System; using UnityEngine; using UnityEditor; using HybridCLR.Editor.Commands; using PhxhSDK.AOT.VersionUpdate; using Debug = UnityEngine.Debug; using System.Collections.Generic; using AutoBuild.Helper; using BuildTarget = UnityEditor.BuildTarget; using HybridCLR.Editor.HotUpdate; using HybridCLR.Editor; using System.IO; /// /// Jenkins打包 /// internal class BatchBuild { /// /// 批处理更新设置 /// jenkins打包新包时使用该方法 /// public static void ApplyPlayerSetting() { YooAssetBuildBase.Init(); } public static string _oldDefines; /// /// 只在DEBUG下有 /// private const string DEBUG = "DEBUG"; private const string DefineListKey = "Define_list:"; private const string PackageNameKey = "PackageName:"; private const string CRASH_SIGHT_KEY_ANDROID = "CrashSight_Android:"; private const string CRASH_SIGHT_KEY_IOS = "CrashSight_iOS:"; private const string NewPlayerSettingsPath = "/../PlayerSettings.txt"; //[MenuItem("Tools/Modify Settings from Text File")] private static void _InitScriptDefines() { var hasYooAsset = false; // 判断本地是否有define_list.txt文件 string filePath = Application.dataPath + NewPlayerSettingsPath; List appendDefineList = new(); if (EditorUserBuildSettings.development) { appendDefineList.Add(DEBUG); Debug.Log("追加宏定义:" + DEBUG); } string newPackageName = ""; if (System.IO.File.Exists(filePath)) { string fileContents = System.IO.File.ReadAllText(filePath); string[] lines = fileContents.Split('\n'); foreach (string line in lines) { if (line.StartsWith(DefineListKey)) { // 解析宏定义 string definePart = line[DefineListKey.Length..].Trim(); // 去掉 "definde:" 后的部分 string[] defines = definePart.Split(','); foreach (string define in defines) { string cleanDefine = define.Trim(new char[] { '"', ' ', '\r', '\n' }); if (!string.IsNullOrEmpty(cleanDefine)) { appendDefineList.Add(cleanDefine); Debug.Log("追加宏定义:" + cleanDefine); } } } else if (line.StartsWith(PackageNameKey)) { // 解析包名 int prefixLength = PackageNameKey.Length; newPackageName = line[prefixLength..].Trim(); Debug.Log("设置新包名:" + newPackageName); } else if (line.StartsWith(CRASH_SIGHT_KEY_ANDROID)) { Debug.Log("检测到CrashSight配置:" + line); // 判断当前打包平台 if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) { Debug.LogError("当前打包平台不是Android,无法设置CrashSight配置"); continue; } // CrashSight: id, key string crashSightPart = line[CRASH_SIGHT_KEY_ANDROID.Length..].Trim(); string[] crashSight = crashSightPart.Split(','); var crashSightData = new PhxhSDK.Data.CrashSightData { id = crashSight[0].Trim(), key = crashSight[1].Trim() }; // 保存到Resources var crashSightPath = "Assets/Resources/CrashSightData.json"; var json = JsonUtility.ToJson(crashSightData); System.IO.File.WriteAllText(crashSightPath, json); Debug.Log("CrashSight配置已保存:" + crashSightData.id + "|" + crashSightData.key); } else if (line.StartsWith(CRASH_SIGHT_KEY_IOS)) { Debug.Log("检测到CrashSight配置:" + line); // 判断当前打包平台 if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.iOS) { Debug.LogError("当前打包平台不是iOS,无法设置CrashSight配置"); continue; } // CrashSight: id, key string crashSightPart = line[CRASH_SIGHT_KEY_IOS.Length..].Trim(); string[] crashSight = crashSightPart.Split(','); var crashSightData = new PhxhSDK.Data.CrashSightData { id = crashSight[0].Trim(), key = crashSight[1].Trim() }; // 保存到Resources var crashSightPath = "Assets/Resources/CrashSightData.json"; var json = JsonUtility.ToJson(crashSightData); System.IO.File.WriteAllText(crashSightPath, json); Debug.Log("CrashSight配置已保存:" + crashSightData.id + "|" + crashSightData.key); } } } var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); var oldDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup); _oldDefines = oldDefines; // 判断是否有需要追加的宏 if (appendDefineList.Count > 0) { var newDefineList = new List(); newDefineList.AddRange(oldDefines.Split(';')); for (var i = 0; i < newDefineList.Count; i++) { var define = newDefineList[i]; if (string.IsNullOrEmpty(define)) continue; if (define == "USE_YOO") { hasYooAsset = true; } if (appendDefineList.Contains(define)) appendDefineList.Remove(define); } newDefineList.AddRange(appendDefineList); PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTargetGroup, string.Join(";", newDefineList)); } //只有安卓平台下修改 if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android && !string.IsNullOrEmpty(newPackageName)) { PlayerSettings.applicationIdentifier = newPackageName; //PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, newPackageName); } // 现已修改为使用YooAsset // 关闭Unity闪屏 PlayerSettings.SplashScreen.showUnityLogo = false; } public static void _RevertScriptDefines() { var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTargetGroup, _oldDefines); } /// /// 批处理构建Resources和Player /// jenkins打包新包时使用该方法 /// public static void BuildContentAndPlayer() { try { _InitScriptDefines(); var enableHotUpdate = YooAssetBuildBase.Init(); // 写入版本信息到Resources/LocalInfo.json UpdateLocalInfo info = new() { workSpace = YooAssetBuildBase.workSpace, version = YooAssetBuildBase.appVersion, isOss = YooAssetBuildBase.isOss, buildTicks = DateTime.Now.Ticks }; PackDataInst.CreateResourceJson(info); // 写入打包信息到Yoo_Build_Cache ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version); BuildLauncher.SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) BuildLauncher.BuildHotUpdateDlls(); YooAssetBuildBase.BuildContentAndPlayer(); // 写入打包信息到Yoo_Build_Cache ExtBuildInfoHelper.CreateVertifyBuildInfo(info.workSpace, info.version); // 还原宏 _RevertScriptDefines(); #region 打整包时缓存当前版本的AotDll string aotDllStorePath = GetAotDllStorePath(info.version); string aotDllPath = SettingsUtil.GetAssembliesPostIl2CppStripDir(EditorUserBuildSettings.activeBuildTarget); DirectoryInfo dir = new(aotDllPath); if (Directory.Exists(aotDllStorePath)) Directory.Delete(aotDllStorePath, true); Directory.CreateDirectory(aotDllStorePath); FileInfo[] files = dir.GetFiles(); foreach (FileInfo file in files) { file.CopyTo(Path.Combine(aotDllStorePath, file.Name), false); } #endregion } catch (Exception e) { //Build Exception用于在日志文件中定位此次打包的异常 Debug.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } /// /// 批处理增量更新资源 /// jenkins打包增量更新时使用该方法 /// public static void UpdatePreviousBuild() { try { _InitScriptDefines(); var enableHotUpdate = YooAssetBuildBase.Init(); ExtBuildInfoHelper.VertifyExitBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); if (enableHotUpdate) { Debug.Log("开始检查AOT版本"); if (CheckAotOutDate(YooAssetBuildBase.appVersion)) { Debug.Log("AOT版本检查通过"); BuildLauncher.BuildHotUpdateDlls(); } else { Debug.LogError("AOT版本不匹配,请重新打AOT包"); } } else { Debug.Log("未开启热更"); } // 写入打包信息到Yoo_Build_Cache ExtBuildInfoHelper.UpdateExtBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); YooAssetBuildBase.BuildOnlyResUpdate(); ExtBuildInfoHelper.CreateVertifyBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion); _RevertScriptDefines(); } catch (Exception e) { //Build Exception用于在日志文件中定位此次打包的异常 Debug.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } /// /// jenkins检查AOT裁剪代码 /// /// /// public static bool CheckAotOutDate(string version) { Debug.Log($"开始检查aot版本:{version}"); string stripedDllPath = GetAotDllStorePath(version); // 先编译dll CompileDllCommand.CompileDllActiveBuildTarget(); // 再检查编译出来的dll是否满足以前的Aotdll MissingMetadataChecker checker = new(stripedDllPath, new List()); string hotUpdateDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(EditorUserBuildSettings.activeBuildTarget); foreach (string dll in SettingsUtil.HotUpdateAssemblyFilesExcludePreserved) { string dllPath = $"{hotUpdateDir}/{dll}"; bool notAnyMissing = checker.Check(dllPath); if (!notAnyMissing) throw new Exception("AOT缺失Dll,请重新打AOT包"); } return true; } /// /// 获取缓存的AOT Dll路径 /// /// public static string GetAotDllStorePath(string version) { return $"../StoreAotDll/{version}"; } }