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}";
}
}