506 lines
19 KiB
C#
506 lines
19 KiB
C#
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;
|
||
}
|
||
} |