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

506 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;
/// <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/";
#region ChannelSetting
/// <summary>
/// 宏列表key
/// </summary>
private const string DefineListKey = "Define_list:";
/// <summary>
/// 包名列表key
/// </summary>
private const string PackageNameKey = "PackageName:";
/// <summary>
/// 上传AppConfig id
/// </summary>
private const string AppConfigAccessKeyId = "ConfigAccessKeyId:";
/// <summary>
/// 上传AppConfig Key
/// </summary>
private const string AppConfigAccessKeySecret = "ConfigAccessKeySecret:";
/// <summary>
/// 上传AppConfig桶名
/// </summary>
private const string AppConfigBucketName = "ConfigBucketName:";
/// <summary>
/// 上传AppConfig url
/// </summary>
private const string AppConfigURL = "ConfigURL:";
/// <summary>
/// 应用access
/// </summary>
private const string AppAccessKey = "AppAccessKey:";
/// <summary>
/// 应用key
/// </summary>
private const string AppKey = "AppKey:";
private const string CRASH_SIGHT_KEY_ANDROID = "CrashSight_Android:";
private const string CRASH_SIGHT_KEY_IOS = "CrashSight_iOS:";
private const string ChannelSetting = "/../ChannelSetting.txt";
#endregion
//[MenuItem("Tools/Modify Settings from Text File")]
private static void _InitScriptDefines()
{
var hasYooAsset = false;
// 判断本地是否有define_list.txt文件
string filePath = Application.dataPath + ChannelSetting;
List<string> appendDefineList = new();
if (EditorUserBuildSettings.development)
{
appendDefineList.Add(DEBUG);
Debug.Log("追加宏定义:" + DEBUG);
}
string newPackageName = string.Empty;
if (File.Exists(filePath))
{
string fileContents = 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(AppConfigAccessKeyId))
{
int prefixLength = AppConfigAccessKeyId.Length;
YooAssetBuildBase.appConfigAccessKeyId = line[prefixLength..].Trim();
Debug.Log($"设置AppConfig上传id:{YooAssetBuildBase.appConfigAccessKeyId}");
}
else if (line.StartsWith(AppConfigAccessKeySecret))
{
int prefixLegth = AppConfigAccessKeySecret.Length;
YooAssetBuildBase.appConfigAccessKeySecret = line[prefixLegth..].Trim();
Debug.Log($"设置AppConfig上传key:{YooAssetBuildBase.appConfigAccessKeySecret}");
}
else if (line.StartsWith(AppConfigBucketName))
{
int prefixLegth = AppConfigBucketName.Length;
YooAssetBuildBase.appConfigBucketName = line[prefixLegth..].Trim();
Debug.Log($"设置AppConfig上传桶名:{YooAssetBuildBase.appConfigBucketName}");
}
else if (line.StartsWith(AppConfigURL))
{
int prefixLegth = AppConfigURL.Length;
YooAssetBuildBase.appConfigURL = line[prefixLegth..].Trim();
Debug.Log($"设置AppConfig上传url:{YooAssetBuildBase.appConfigURL}");
}
else if (line.StartsWith(AppAccessKey))
{
int prefixLegth = AppAccessKey.Length;
YooAssetBuildBase.httpAccessKey = line[prefixLegth..].Trim();
Debug.Log($"设置http请求id:{YooAssetBuildBase.httpAccessKey}");
}
else if (line.StartsWith(AppKey))
{
int prefixLegth = AppKey.Length;
YooAssetBuildBase.httpKey = line[prefixLegth..].Trim();
Debug.Log($"设置http请求id:{YooAssetBuildBase.httpKey}");
}
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<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));
}
//只有安卓平台下修改
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);
}
/// <summary>
/// 批处理构建Resources和Player
/// jenkins打包新包时使用该方法
/// </summary>
public static void BuildContentAndPlayer()
{
try
{
_InitScriptDefines();
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
{
_InitScriptDefines();
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);
Dictionary<string, string> headData = PostHeader.PostHeaderInfo(new Dictionary<string, string>
{
{ "plaintext", key },
{ "tag", loginKey },
{ "timestamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString() }
});
LoginKeyData loginKeyData = new()
{
plaintext = key,
tag = loginKey,
};
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;
}
}
private class LoginKeyData
{
public string plaintext;
public string tag;
}
private class LoginKeyResData
{
public int code;
public string message;
public object data;
}
}