private void CreateReportFile(AssetBundleBuilder.BuildParametersContext buildParameters, BuildMapContext buildMapContext) { PatchManifest patchManifest = AssetBundleBuilderHelper.LoadPatchManifestFile(buildParameters.PipelineOutputDirectory, buildParameters.Parameters.BuildVersion); BuildReport buildReport = new BuildReport(); // 概述信息 { buildReport.Summary.UnityVersion = UnityEngine.Application.unityVersion; buildReport.Summary.BuildTime = DateTime.Now.ToString(); buildReport.Summary.BuildSeconds = (int)buildParameters.GetBuildingSeconds(); buildReport.Summary.BuildTarget = buildParameters.Parameters.BuildTarget; buildReport.Summary.BuildMode = buildParameters.Parameters.BuildMode; buildReport.Summary.BuildVersion = buildParameters.Parameters.BuildVersion; buildReport.Summary.BuildinTags = buildParameters.Parameters.BuildinTags; buildReport.Summary.EnableAddressable = buildParameters.Parameters.EnableAddressable; buildReport.Summary.AppendFileExtension = buildParameters.Parameters.AppendFileExtension; buildReport.Summary.CopyBuildinTagFiles = buildParameters.Parameters.CopyBuildinTagFiles; buildReport.Summary.AutoCollectShaders = AssetBundleCollectorSettingData.Setting.AutoCollectShaders; buildReport.Summary.ShadersBundleName = AssetBundleCollectorSettingData.Setting.ShadersBundleName; buildReport.Summary.EncryptionServicesClassName = buildParameters.Parameters.EncryptionServices == null ? "null" : buildParameters.Parameters.EncryptionServices.GetType().FullName; // 构建参数 buildReport.Summary.CompressOption = buildParameters.Parameters.CompressOption; buildReport.Summary.DisableWriteTypeTree = buildParameters.Parameters.DisableWriteTypeTree; buildReport.Summary.IgnoreTypeTreeChanges = buildParameters.Parameters.IgnoreTypeTreeChanges; // 构建结果 buildReport.Summary.AssetFileTotalCount = buildMapContext.AssetFileCount; buildReport.Summary.AllBundleTotalCount = GetAllBundleCount(patchManifest); buildReport.Summary.AllBundleTotalSize = GetAllBundleSize(patchManifest); buildReport.Summary.BuildinBundleTotalCount = GetBuildinBundleCount(patchManifest); buildReport.Summary.BuildinBundleTotalSize = GetBuildinBundleSize(patchManifest); buildReport.Summary.EncryptedBundleTotalCount = GetEncryptedBundleCount(patchManifest); buildReport.Summary.EncryptedBundleTotalSize = GetEncryptedBundleSize(patchManifest); buildReport.Summary.RawBundleTotalCount = GetRawBundleCount(patchManifest); buildReport.Summary.RawBundleTotalSize = GetRawBundleSize(patchManifest); } // 资源对象列表 buildReport.AssetInfos = new List <ReportAssetInfo>(patchManifest.AssetList.Count); foreach (var patchAsset in patchManifest.AssetList) { var mainBundle = patchManifest.BundleList[patchAsset.BundleID]; ReportAssetInfo reportAssetInfo = new ReportAssetInfo(); reportAssetInfo.Address = patchAsset.Address; reportAssetInfo.AssetPath = patchAsset.AssetPath; reportAssetInfo.AssetTags = patchAsset.AssetTags; reportAssetInfo.AssetGUID = AssetDatabase.AssetPathToGUID(patchAsset.AssetPath); reportAssetInfo.MainBundleName = mainBundle.BundleName; reportAssetInfo.MainBundleSize = mainBundle.SizeBytes; reportAssetInfo.DependBundles = GetDependBundles(patchManifest, patchAsset); reportAssetInfo.DependAssets = GetDependAssets(buildMapContext, mainBundle.BundleName, patchAsset.AssetPath); buildReport.AssetInfos.Add(reportAssetInfo); } // 资源包列表 buildReport.BundleInfos = new List <ReportBundleInfo>(patchManifest.BundleList.Count); foreach (var patchBundle in patchManifest.BundleList) { ReportBundleInfo reportBundleInfo = new ReportBundleInfo(); reportBundleInfo.BundleName = patchBundle.BundleName; reportBundleInfo.Hash = patchBundle.Hash; reportBundleInfo.CRC = patchBundle.CRC; reportBundleInfo.SizeBytes = patchBundle.SizeBytes; reportBundleInfo.Tags = patchBundle.Tags; reportBundleInfo.Flags = patchBundle.Flags; buildReport.BundleInfos.Add(reportBundleInfo); } // 删除旧文件 string filePath = $"{buildParameters.PipelineOutputDirectory}/{YooAssetSettingsData.GetReportFileName(buildParameters.Parameters.BuildVersion)}"; if (File.Exists(filePath)) { File.Delete(filePath); } // 序列化文件 BuildReport.Serialize(filePath, buildReport); BuildRunner.Log($"资源构建报告文件创建完成:{filePath}"); }
void IBuildTask.Run(BuildContext context) { var buildParameters = context.GetContextObject <AssetBundleBuilder.BuildParametersContext>(); buildParameters.BeginWatch(); // 检测构建平台是否合法 if (buildParameters.Parameters.BuildTarget == BuildTarget.NoTarget) { throw new Exception("请选择目标平台"); } // 检测构建版本是否合法 if (buildParameters.Parameters.BuildVersion <= 0) { throw new Exception("请先设置版本号"); } // 检测输出目录是否为空 if (string.IsNullOrEmpty(buildParameters.PipelineOutputDirectory)) { throw new Exception("输出目录不能为空"); } // 增量更新时候的必要检测 var buildMode = buildParameters.Parameters.BuildMode; if (buildMode == EBuildMode.IncrementalBuild) { // 检测历史版本是否存在 if (AssetBundleBuilderHelper.HasAnyPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot) == false) { throw new Exception("没有发现任何历史版本,请尝试强制重建"); } // 检测构建版本是否合法 int maxPackageVersion = AssetBundleBuilderHelper.GetMaxPackageVersion(buildParameters.Parameters.BuildTarget, buildParameters.Parameters.OutputRoot); if (buildParameters.Parameters.BuildVersion <= maxPackageVersion) { throw new Exception("构建版本不能小于历史版本"); } // 检测补丁包是否已经存在 string packageDirectory = buildParameters.GetPackageDirectory(); if (Directory.Exists(packageDirectory)) { throw new Exception($"补丁包已经存在:{packageDirectory}"); } // 检测内置资源分类标签是否一致 var oldPatchManifest = AssetBundleBuilderHelper.GetOldPatchManifest(buildParameters.PipelineOutputDirectory); if (buildParameters.Parameters.BuildinTags != oldPatchManifest.BuildinTags) { throw new Exception($"增量更新时内置资源标签必须一致:{buildParameters.Parameters.BuildinTags} != {oldPatchManifest.BuildinTags}"); } } // 如果是强制重建 if (buildMode == EBuildMode.ForceRebuild) { // 删除平台总目录 string platformDirectory = $"{buildParameters.Parameters.OutputRoot}/{buildParameters.Parameters.BuildTarget}"; if (EditorTools.DeleteDirectory(platformDirectory)) { BuildRunner.Log($"删除平台总目录:{platformDirectory}"); } } // 如果输出目录不存在 if (EditorTools.CreateDirectory(buildParameters.PipelineOutputDirectory)) { BuildRunner.Log($"创建输出目录:{buildParameters.PipelineOutputDirectory}"); } }
/// <summary> /// 验证构建结果 /// </summary> private void VerifyingBuildingResult(BuildContext context, AssetBundleManifest unityManifest) { var buildParameters = context.GetContextObject <AssetBundleBuilder.BuildParametersContext>(); var buildMapContext = context.GetContextObject <BuildMapContext>(); string[] buildedBundles = unityManifest.GetAllAssetBundles(); // 1. 过滤掉原生Bundle List <BuildBundleInfo> expectBundles = new List <BuildBundleInfo>(buildedBundles.Length); foreach (var bundleInfo in buildMapContext.BundleInfos) { if (bundleInfo.IsRawFile == false) { expectBundles.Add(bundleInfo); } } // 2. 验证数量 if (buildedBundles.Length != expectBundles.Count) { Debug.LogWarning($"构建过程中可能存在无效的资源,导致和预期构建的Bundle数量不一致!"); } // 3. 正向验证Bundle foreach (var bundleName in buildedBundles) { if (buildMapContext.IsContainsBundle(bundleName) == false) { throw new Exception($"Should never get here !"); } } // 4. 反向验证Bundle bool isPass = true; foreach (var expectBundle in expectBundles) { bool isMatch = false; foreach (var buildedBundle in buildedBundles) { if (buildedBundle == expectBundle.BundleName) { isMatch = true; break; } } if (isMatch == false) { isPass = false; Debug.LogWarning($"没有找到预期构建的Bundle文件 : {expectBundle.BundleName}"); } } if (isPass == false) { throw new Exception("构建结果验证没有通过,请参考警告日志!"); } // 5. 验证Asset var buildMode = buildParameters.Parameters.BuildMode; if (buildMode == EBuildMode.ForceRebuild || buildMode == EBuildMode.IncrementalBuild) { int progressValue = 0; foreach (var buildedBundle in buildedBundles) { string filePath = $"{buildParameters.PipelineOutputDirectory}/{buildedBundle}"; string[] allBuildinAssetPaths = GetAssetBundleAllAssets(filePath); string[] expectBuildinAssetPaths = buildMapContext.GetBuildinAssetPaths(buildedBundle); if (expectBuildinAssetPaths.Length != allBuildinAssetPaths.Length) { Debug.LogWarning($"构建的Bundle文件内的资源对象数量和预期不匹配 : {buildedBundle}"); isPass = false; continue; } foreach (var buildinAssetPath in allBuildinAssetPaths) { var guid = AssetDatabase.AssetPathToGUID(buildinAssetPath); if (string.IsNullOrEmpty(guid)) { Debug.LogWarning($"无效的资源路径,请检查路径是否带有特殊符号或中文:{buildinAssetPath}"); isPass = false; continue; } bool isMatch = false; foreach (var exceptBuildAssetPath in expectBuildinAssetPaths) { var guidExcept = AssetDatabase.AssetPathToGUID(exceptBuildAssetPath); if (guid == guidExcept) { isMatch = true; break; } } if (isMatch == false) { Debug.LogWarning($"在构建的Bundle文件里发现了没有匹配的资源对象:{buildinAssetPath}"); isPass = false; continue; } } EditorTools.DisplayProgressBar("验证构建结果", ++progressValue, buildedBundles.Length); } EditorTools.ClearProgressBar(); if (isPass == false) { throw new Exception("构建结果验证没有通过,请参考警告日志!"); } } // 卸载所有加载的Bundle BuildRunner.Log("构建结果验证成功!"); }