NLDClient-yudde/ProjectNLD/Assets/Editor/Common/BuildLauncher.cs

550 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using AutoBuild.Helper;
using Cysharp.Threading.Tasks;
using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
using HybridCLR.Editor.HotUpdate;
using PhxhSDK.AOT.VersionUpdate;
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using BuildTarget = UnityEditor.BuildTarget;
using Debug = UnityEngine.Debug;
using Directory = System.IO.Directory;
using YamlDotNet.Serialization;
/// <summary>
/// Jenkins打包
/// </summary>
internal class BatchBuild
{
/// <summary>
/// 批处理更新设置
/// jenkins打包新包时使用该方法
/// </summary>
public static void ApplyPlayerSetting()
{
YooAssetBuildBase.Init();
}
public static string _oldDefines;
/// <summary>
/// 只在DEBUG下有
/// </summary>
private const string DEBUG = "DEBUG";
/// <summary>
/// dev上传地址
/// </summary>
private const string Dev_PostUrl = "http://dev_openapi.kedrgame.com/api/v1/";
/// <summary>
/// Pre上传地址
/// </summary>
private const string Pre_PostUrl = "http://pre_openapi.kedrgame.com/api/v1/";
/// <summary>
/// Release上传地址
/// </summary>
private const string Release_PostUrl = "http://openapi.kedrgame.com/api/v1/";
public static void _RevertScriptDefines()
{
var currentTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
PlayerSettings.SetScriptingDefineSymbolsForGroup(currentTargetGroup, _oldDefines);
}
/// <summary>
/// 批处理构建Resources和Player
/// jenkins打包新包时使用该方法
/// </summary>
public static void BuildContentAndPlayer()
{
try
{
InitChannelConfigFromYaml();
bool enableHotUpdate = YooAssetBuildBase.Init();
string bundleVersionCode = GetBundleVersionCode();
// 写入版本信息到Resources/LocalInfo.json
UpdateLocalInfo info = new()
{
workSpace = YooAssetBuildBase.workSpace,
accessKeyId = YooAssetBuildBase.appConfigAccessKeyId,
accessKeySecret = YooAssetBuildBase.appConfigAccessKeySecret,
bucketName = YooAssetBuildBase.appConfigBucketName,
configOriginalUrl = YooAssetBuildBase.appConfigURL,
//httpUrl = YooAssetBuildBase.httpURL,
httpAccessKey = YooAssetBuildBase.httpAccessKey,
httpKey = YooAssetBuildBase.httpKey,
version = YooAssetBuildBase.appVersion,
isDev = YooAssetBuildBase.isDev,
buildTicks = DateTime.Now.Ticks,
buildCode = bundleVersionCode,
};
PackDataInst.CreateResourceJson(info);
BuildTargetGroup currentTargetGroup =
BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup);
// 写入打包信息到Yoo_Build_Cache
#if DEVELOPMENT_BUILD || DEBUG
ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, info.buildCode,
true, defines);
#else
ExtBuildInfoHelper.CreateFirstExtBuildInfo(info.buildTicks, info.workSpace, info.version, false, defines);
#endif
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.workSpace, 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
PostLoginKey(info.workSpace, info.buildTicks, info.version); // 上传Aot loginKey
}
catch (Exception e)
{
//Build Exception用于在日志文件中定位此次打包的异常
Debug.LogError($"[Build Exception]:{e.Message} \r\n {e.StackTrace}");
throw;
}
}
/// <summary>
/// 批处理增量更新资源
/// jenkins打包增量更新时使用该方法
/// </summary>
public static void UpdatePreviousBuild()
{
try
{
InitChannelConfigFromYaml();
var enableHotUpdate = YooAssetBuildBase.Init();
ExtBuildInfoHelper.VertifyExitBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion);
if (enableHotUpdate)
{
Debug.Log("开始检查AOT版本");
if (CheckAotOutDate(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion))
{
Debug.Log("AOT版本检查通过");
BuildLauncher.BuildHotUpdateDlls();
}
else
{
Debug.LogError("AOT版本不匹配请重新打AOT包");
}
}
else
{
Debug.Log("未开启热更");
}
BuildTargetGroup currentTargetGroup =
BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget);
string defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(currentTargetGroup);
// 写入打包信息到Yoo_Build_Cache
#if DEVELOPMENT_BUILD || DEBUG
ExtBuildInfoHelper.UpdateExtBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion, true,
defines);
#else
ExtBuildInfoHelper.UpdateExtBuildInfo(YooAssetBuildBase.workSpace, YooAssetBuildBase.appVersion, false, defines);
#endif
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;
}
}
/// <summary>
/// jenkins检查AOT裁剪代码
/// </summary>
/// <param name="version"></param>
/// <returns></returns>
public static bool CheckAotOutDate(string workSpace, string version)
{
Debug.Log($"开始检查aot版本:{version}");
string stripedDllPath = GetAotDllStorePath(workSpace, version);
// 先编译dll
CompileDllCommand.CompileDllActiveBuildTarget();
// 再检查编译出来的dll是否满足以前的Aotdll
MissingMetadataChecker checker = new(stripedDllPath, new List<string>());
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;
}
/// <summary>
/// 获取缓存的AOT Dll路径
/// </summary>
/// <param name="version"></param>
public static string GetAotDllStorePath(string workSpace, string version)
{
return Application.dataPath + $"/../Yoo_Build_Cache/{workSpace}/{version}/CacheAOTDll";
}
public static string GetBundleVersionCode()
{
#if UNITY_IOS
return PlayerSettings.iOS.buildNumber;
#elif UNITY_ANDROID
return PlayerSettings.Android.bundleVersionCode.ToString();
#else
return "";
#endif
}
/// <summary>
/// 上传登录key
/// </summary>
public static void PostLoginKey(string workSpace, long buildTicks, string version)
{
string key = $"{workSpace}_{version}_{buildTicks}";
string loginKey = PhxhSDK.Utils.LoginKeyMD5(key);
string platform = string.Empty; // 0-安卓1-IOS
#if UNITY_ANDROID
platform = "0";
#elif UNITY_IOS
platform = "1";
#endif
Dictionary<string, string> headData = PostHeader.PostHeaderInfo(new Dictionary<string, string>
{
{ "plaintext", key },
{ "tag", loginKey },
{ "platform", platform },
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }
});
LoginKeyData loginKeyData = new()
{
plaintext = key,
tag = loginKey,
platform = platform,
};
try
{
if (PackDataInst.Inst.localInfo.isDev)
{
HttpHelper.PostAsync($"{Dev_PostUrl}version", loginKeyData, headData).Forget();
}
else
{
HttpHelper.PostAsync($"{Pre_PostUrl}version", loginKeyData, headData).Forget();
HttpHelper.PostAsync($"{Release_PostUrl}version", loginKeyData, headData).Forget();
}
}
catch (Exception e)
{
Debug.LogError($"[PostLoginKey Exception]:{e.Message} \r\n {e.StackTrace}");
throw;
}
}
/// <summary>
/// 自动发布debug loginKey
/// </summary>
public static void PostDebugSendKey()
{
string platform = string.Empty; // 0-安卓1-IOS
#if UNITY_ANDROID
platform = "0";
#elif UNITY_IOS
platform = "1";
#endif
Dictionary<string, string> dicHeadData = PostHeader.PostHeaderInfo(new Dictionary<string, string>
{
{ "platform", platform },
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }
});
SendLoginKeyData sendLoginKeyData = new()
{
platform = platform,
};
HttpHelper.PostAsync($"{Dev_PostUrl}sendVersion", sendLoginKeyData, dicHeadData).Forget();
}
private class LoginKeyData
{
public string plaintext;
public string tag;
public string platform;
}
private class SendLoginKeyData
{
public string platform;
}
private class LoginKeyResData
{
public int code;
public string message;
public object data;
}
private class ChannelConfig
{
public List<string> Define_list { get; set; }
public string PackageName { get; set; }
public int BuildCode { get; set; }
public string ConfigAccessKeyId { get; set; }
public string ConfigAccessKeySecret { get; set; }
public string ConfigBucketName { get; set; }
public string ConfigURL { get; set; }
public string AppAccessKey { get; set; }
public string AppKey { get; set; }
public CrashSightConfig CrashSight_Android { get; set; }
public CrashSightConfig CrashSight_iOS { get; set; }
}
private class CrashSightConfig
{
[YamlMember(Alias = "id")] public string Id { get; set; }
[YamlMember(Alias = "key")] public string Key { get; set; }
}
private const string ChannelSettingYaml = "/../ChannelSetting.yaml";
/// <summary>
/// 从YAML文件初始化渠道配置
/// </summary>
private static void InitChannelConfigFromYaml()
{
var hasYooAsset = false;
List<string> appendDefineList = new();
// 判断是否为开发模式
if (EditorUserBuildSettings.development)
{
appendDefineList.Add(DEBUG);
Debug.Log("追加宏定义:" + DEBUG);
}
string yamlPath = Application.dataPath + ChannelSettingYaml;
if (File.Exists(yamlPath))
{
try
{
// 忽略 YAML 中多余的字段
var deserializer = new DeserializerBuilder()
.IgnoreUnmatchedProperties()
.Build();
string yamlContent = File.ReadAllText(yamlPath);
var config = deserializer.Deserialize<ChannelConfig>(yamlContent);
// 处理宏定义
if (config.Define_list != null)
{
foreach (string define in config.Define_list)
{
string cleanDefine = define.Trim();
if (!string.IsNullOrEmpty(cleanDefine))
{
appendDefineList.Add(cleanDefine);
Debug.Log("追加宏定义:" + cleanDefine);
}
}
}
// 处理包名
if (!string.IsNullOrEmpty(config.PackageName))
{
Debug.Log("设置新包名:" + config.PackageName);
if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
PlayerSettings.applicationIdentifier = config.PackageName;
}
}
// 处理BuildCode
if (config.BuildCode != 0)
{
Debug.Log("设置BuildCode:" + config.BuildCode);
#if UNITY_IOS
PlayerSettings.iOS.buildNumber = config.BuildCode.ToString();
#elif UNITY_ANDROID
PlayerSettings.Android.bundleVersionCode = config.BuildCode;
#endif
}
// 处理AppConfig上传id
if (config.ConfigAccessKeyId != null)
{
YooAssetBuildBase.appConfigAccessKeyId = config.ConfigAccessKeyId;
Debug.Log($"设置AppConfig上传id:{YooAssetBuildBase.appConfigAccessKeyId}");
}
// 处理AppConfig上传key
if (config.ConfigAccessKeySecret != null)
{
YooAssetBuildBase.appConfigAccessKeySecret = config.ConfigAccessKeySecret;
Debug.Log($"设置AppConfig上传key:{YooAssetBuildBase.appConfigAccessKeySecret}");
}
// 处理AppConfig上传桶名
if (config.ConfigBucketName != null)
{
YooAssetBuildBase.appConfigBucketName = config.ConfigBucketName;
Debug.Log($"设置AppConfig上传桶名:{YooAssetBuildBase.appConfigBucketName}");
}
// 处理AppConfig上传url
if (config.ConfigURL != null)
{
YooAssetBuildBase.appConfigURL = config.ConfigURL;
Debug.Log($"设置AppConfig上传url:{YooAssetBuildBase.appConfigURL}");
}
// 处理http请求AccessKey
if (config.AppAccessKey != null)
{
YooAssetBuildBase.httpAccessKey = config.AppAccessKey;
Debug.Log($"设置http请求id:{YooAssetBuildBase.httpAccessKey}");
}
// 处理http请求AppKey
if (config.AppKey != null)
{
YooAssetBuildBase.httpKey = config.AppKey;
Debug.Log($"设置http请求id:{YooAssetBuildBase.httpKey}");
}
// 处理CrashSight_Android配置
if (config.CrashSight_Android != null &&
EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
{
var crashSightData = new PhxhSDK.Data.CrashSightData
{
id = config.CrashSight_Android.Id,
key = config.CrashSight_Android.Key
};
var crashSightPath = "Assets/Resources/CrashSightData.json";
var json = JsonUtility.ToJson(crashSightData);
File.WriteAllText(crashSightPath, json);
Debug.Log("CrashSight配置已保存:" + crashSightData.id + "|" + crashSightData.key);
}
// 处理CrashSight_Android配置
if (config.CrashSight_iOS != null &&
EditorUserBuildSettings.activeBuildTarget == BuildTarget.iOS)
{
var crashSightData = new PhxhSDK.Data.CrashSightData
{
id = config.CrashSight_iOS.Id,
key = config.CrashSight_iOS.Key
};
var crashSightPath = "Assets/Resources/CrashSightData.json";
var json = JsonUtility.ToJson(crashSightData);
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<string>();
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));
}
// 关闭Unity闪屏
PlayerSettings.SplashScreen.showUnityLogo = false;
}
catch (Exception e)
{
Debug.LogError($"解析YAML配置文件失败: {e.Message}\n{e.StackTrace}");
throw;
}
}
else
{
Debug.LogError($"找不到渠道配置文件: {yamlPath}");
}
}
}