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 HybridCLR.Editor.Installer; using UnityEditor.SceneManagement; using BuildTarget = UnityEditor.BuildTarget; using HybridCLR.Editor.HotUpdate; using HybridCLR.Editor; using System.IO; /// /// 编辑器下打包 /// class BuildLauncher { private const string HybirdCLRGenerateDir = "/HybridCLRGenerate"; private const string HybirdCLRGenerateLinkPath = "Assets/HybridCLRGenerate/link.xml"; private const string GameLauncherSceneName = "Assets/Scenes/GameLauncher.unity"; //"/var/www/html/nld/Build/$WORKSPACE/$PLATFORM/$VERSION" private const string RemotePath = "/var/www/html/nld/Build/{0}/{1}/{2}"; private const string RemoteHost = "132.232.113.98"; private const string RemoteUser = "root"; private static bool enableHotUpdate; private static bool hasYooAsset = true; /// /// 开启热更 /// public static void SetEnableHotUpdate(bool isEnable) { Debug.Log($"是否开启热更新:{isEnable}"); var gameLauncherScene = EditorSceneManager.OpenScene(GameLauncherSceneName, OpenSceneMode.Additive); if (!gameLauncherScene.IsValid()) { Debug.LogError($"无法加载场景{GameLauncherSceneName}"); return; } var gameLauncher = UnityEngine.Object.FindObjectOfType(); if (gameLauncher == null) { Debug.LogError("找不到GameLauncher"); return; } gameLauncher.enableHotUpdate = isEnable; EditorSceneManager.MarkSceneDirty(gameLauncherScene); EditorSceneManager.SaveScene(gameLauncherScene); EditorSceneManager.CloseScene(gameLauncherScene, false); HybridCLR.Editor.SettingsUtil.Enable = isEnable; if (!isEnable) { //删除HybirdCLR生成的link文件 AssetDatabase.DeleteAsset(HybirdCLRGenerateLinkPath); } } /// /// 构建热更 /// public static void BuildHotUpdateDlls() { var controller = new InstallerController(); if (!controller.HasInstalledHybridCLR()) controller.InstallDefaultHybridCLR(); BuildUtils.SetFileReadable(Application.dataPath + HybirdCLRGenerateDir); PrebuildCommand.GenerateAll(); HotUpadeEditorHelper.CopyHotUpdateDll(); HotUpadeEditorHelper.CopyMetaDataDll(); } /// /// 打包面板初始化 /// public static void Init(BuildTarget buildTarget, string workSpace, string appVersion, string profileName, bool developmentBuild, bool hotUpdate) { EditorUserBuildSettings.development = developmentBuild; YooAssetBuildBase.BuildTargetPlatform = buildTarget; YooAssetBuildBase.profileName = profileName; YooAssetBuildBase.JudgmentPlatform(buildTarget.ToString()); YooAssetBuildBase.workSpace = string.IsNullOrEmpty(workSpace) ? "WorkSpace" : workSpace; PlayerSettings.bundleVersion = appVersion; YooAssetBuildBase.appVersion = appVersion; enableHotUpdate = hotUpdate; } /// /// 编辑器下构建资源 /// public static void BuildResource() { if (hasYooAsset) { var info = new UpdateLocalInfo(); info.workSpace = YooAssetBuildBase.workSpace; info.version = YooAssetBuildBase.appVersion; info.isOss = YooAssetBuildBase.isOss; info.buildTicks = DateTime.Now.Ticks; PackDataInst.CreateResourceJson(info); ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version); } SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) { BuildHotUpdateDlls(); } YooAssetBuildBase.BuildResourceInEditor(); } /// /// 编辑器下构建资源和Player /// public static void BuildResourceAndPlayer() { if (hasYooAsset) { var info = new UpdateLocalInfo(); info.workSpace = YooAssetBuildBase.workSpace; info.version = YooAssetBuildBase.appVersion; info.isOss = YooAssetBuildBase.isOss; info.buildTicks = DateTime.Now.Ticks; PackDataInst.CreateResourceJson(info); ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version); } SetEnableHotUpdate(enableHotUpdate); if (enableHotUpdate) { BuildHotUpdateDlls(); } YooAssetBuildBase.BuildContentAndPlayerInEditor(RemoteUser, RemoteHost, RemotePath); } /// /// 只构建Player /// public static void BuildPlayer(BuildTarget buildTarget, bool isDev = false) { YooAssetBuildBase.BuildPlayerInEditor(); } /// /// 编辑器下热更 /// public static void UpdatePreviousBuild(string workSpace, string version, BuildTarget buildTarget) { try { if (enableHotUpdate) BuildHotUpdateDlls(); YooAssetBuildBase.BuildOnlyResUpdate(); YooAssetBuildBase.UploadToServerYooAsset(workSpace, version, buildTarget, RemoteUser, RemoteHost, RemotePath); } catch (Exception e) { DebugUtil.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}"); throw; } } } /// /// Jenkins打包 /// internal class BatchBuild { /// /// 批处理更新设置 /// jenkins打包新包时使用该方法 /// public static void ApplyPlayerSetting() { YooAssetBuildBase.Init(); } private 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 } private 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路径 /// /// private static string GetAotDllStorePath(string version) { return $"../StoreAotDll/{version}"; } }