using System; using System.Collections.Generic; using System.Linq; using UnityEditor.AddressableAssets.Build.BuildPipelineTasks; using UnityEditor.AddressableAssets.Build.DataBuilders; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using UnityEditor.Build.Content; using UnityEditor.Build.Pipeline; using UnityEditor.Build.Pipeline.Interfaces; using UnityEditor.Build.Pipeline.Tasks; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.AddressableAssets.Initialization; using UnityEngine.AddressableAssets.ResourceLocators; namespace UnityEditor.AddressableAssets.Build.AnalyzeRules { class BundleRuleBase : AnalyzeRule { [NonSerialized] internal List m_AddressableAssets = new List(); [NonSerialized] internal Dictionary> m_ResourcesToDependencies = new Dictionary>(); [NonSerialized] internal readonly List m_Locations = new List(); [NonSerialized] internal readonly List m_AllBundleInputDefs = new List(); [NonSerialized] internal readonly Dictionary m_BundleToAssetGroup = new Dictionary(); [NonSerialized] internal ExtractDataTask m_ExtractData = new ExtractDataTask(); internal IList RuntimeDataBuildTasks(string builtinShaderBundleName) { var buildTasks = new List(); // Setup buildTasks.Add(new SwitchToBuildPlatform()); buildTasks.Add(new RebuildSpriteAtlasCache()); // Player Scripts buildTasks.Add(new BuildPlayerScripts()); // Dependency buildTasks.Add(new CalculateSceneDependencyData()); buildTasks.Add(new CalculateAssetDependencyData()); buildTasks.Add(new StripUnusedSpriteSources()); buildTasks.Add(new CreateBuiltInShadersBundle(builtinShaderBundleName)); // Packing buildTasks.Add(new GenerateBundlePacking()); buildTasks.Add(new UpdateBundleObjectLayout()); buildTasks.Add(new GenerateBundleCommands()); buildTasks.Add(new GenerateSubAssetPathMaps()); buildTasks.Add(new GenerateBundleMaps()); return buildTasks; } internal AddressableAssetsBuildContext GetBuildContext(AddressableAssetSettings settings) { ResourceManagerRuntimeData runtimeData = new ResourceManagerRuntimeData(); runtimeData.LogResourceManagerExceptions = settings.buildSettings.LogResourceManagerExceptions; var aaContext = new AddressableAssetsBuildContext { settings = settings, runtimeData = runtimeData, bundleToAssetGroup = m_BundleToAssetGroup, locations = m_Locations }; return aaContext; } protected bool IsValidPath(string path) { return AddressableAssetUtility.IsPathValidForEntry(path) && !path.ToLower().Contains("/resources/") && !path.ToLower().StartsWith("resources/"); } internal ReturnCode RefreshBuild(AddressableAssetsBuildContext buildContext) { var settings = buildContext.settings; var context = new AddressablesDataBuilderInput(settings); var buildTarget = context.Target; var buildTargetGroup = context.TargetGroup; var buildParams = new AddressableAssetsBundleBuildParameters(settings, m_BundleToAssetGroup, buildTarget, buildTargetGroup, settings.buildSettings.bundleBuildPath); var builtinShaderBundleName = settings.DefaultGroup.Name.ToLower().Replace(" ", "").Replace('\\', '/').Replace("//", "/") + "_unitybuiltinshaders.bundle"; var buildTasks = RuntimeDataBuildTasks(builtinShaderBundleName); buildTasks.Add(m_ExtractData); IBundleBuildResults buildResults; var exitCode = ContentPipeline.BuildAssetBundles(buildParams, new BundleBuildContent(m_AllBundleInputDefs), out buildResults, buildTasks, buildContext); GenerateLocationListsTask.Run(buildContext, m_ExtractData.WriteData); return exitCode; } internal List GetAllBundleDependencies() { var explicitGuids = m_ExtractData.WriteData.AssetToFiles.Keys; var implicitGuids = GetImplicitGuidToFilesMap().Keys; var allBundleGuids = explicitGuids.Union(implicitGuids); return allBundleGuids.ToList(); } internal void IntersectResourcesDepedenciesWithBundleDependencies(List bundleDependencyGuids) { foreach (var key in m_ResourcesToDependencies.Keys) { var bundleDependencies = bundleDependencyGuids.Intersect(m_ResourcesToDependencies[key]).ToList(); m_ResourcesToDependencies[key].Clear(); m_ResourcesToDependencies[key].AddRange(bundleDependencies); } } internal virtual void BuiltInResourcesToDependenciesMap(string[] resourcePaths) { foreach (string path in resourcePaths) { string[] dependencies = AssetDatabase.GetDependencies(path); if (!m_ResourcesToDependencies.ContainsKey(path)) m_ResourcesToDependencies.Add(path, new List()); m_ResourcesToDependencies[path].AddRange(from dependency in dependencies select new GUID(AssetDatabase.AssetPathToGUID(dependency))); } } internal void CalculateInputDefinitions(AddressableAssetSettings settings) { foreach (AddressableAssetGroup group in settings.groups) { if (group.HasSchema()) { var schema = group.GetSchema(); List bundleInputDefinitions = new List(); BuildScriptPackedMode.PrepGroupBundlePacking(group, bundleInputDefinitions, m_Locations, schema.BundleMode); for (int i = 0; i < bundleInputDefinitions.Count; i++) { if (m_BundleToAssetGroup.ContainsKey(bundleInputDefinitions[i].assetBundleName)) bundleInputDefinitions[i] = CreateUniqueBundle(bundleInputDefinitions[i]); m_BundleToAssetGroup.Add(bundleInputDefinitions[i].assetBundleName, schema.Group.Guid); } m_AllBundleInputDefs.AddRange(bundleInputDefinitions); } } } internal AssetBundleBuild CreateUniqueBundle(AssetBundleBuild bid) { int count = 1; var newName = bid.assetBundleName; while (m_BundleToAssetGroup.ContainsKey(newName) && count < 1000) newName = bid.assetBundleName.Replace(".bundle", string.Format("{0}.bundle", count++)); return new AssetBundleBuild { assetBundleName = newName, addressableNames = bid.addressableNames, assetBundleVariant = bid.assetBundleVariant, assetNames = bid.assetNames }; } internal List GetImplicitGuidsForBundle(string fileName) { List guids = (from id in m_ExtractData.WriteData.FileToObjects[fileName] where !m_ExtractData.WriteData.AssetToFiles.Keys.Contains(id.guid) select id.guid).ToList(); return guids; } internal Dictionary> GetImplicitGuidToFilesMap() { Dictionary> implicitGuids = new Dictionary>(); IEnumerable> validImplicitGuids = from fileToObject in m_ExtractData.WriteData.FileToObjects from objectId in fileToObject.Value where !m_ExtractData.WriteData.AssetToFiles.Keys.Contains(objectId.guid) select new KeyValuePair(objectId, fileToObject.Key); //Build our Dictionary from our list of valid implicit guids (guids not already in explicit guids) foreach (var objectIdToFile in validImplicitGuids) { if (!implicitGuids.ContainsKey(objectIdToFile.Key.guid)) implicitGuids.Add(objectIdToFile.Key.guid, new List()); implicitGuids[objectIdToFile.Key.guid].Add(objectIdToFile.Value); } return implicitGuids; } internal List CalculateBuiltInResourceDependenciesToBundleDependecies(AddressableAssetSettings settings, string[] builtInResourcesPaths) { List results = new List(); if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) { Debug.LogError("Cannot run Analyze with unsaved scenes"); results.Add(new AnalyzeResult{resultName = ruleName + "Cannot run Analyze with unsaved scenes"}); return results; } m_AddressableAssets = (from aaGroup in settings.groups from entry in aaGroup.entries select new GUID(entry.guid)).ToList(); BuiltInResourcesToDependenciesMap(builtInResourcesPaths); CalculateInputDefinitions(settings); var context = GetBuildContext(settings); ReturnCode exitCode = RefreshBuild(context); if (exitCode < ReturnCode.Success) { Debug.LogError("Analyze build failed. " + exitCode); results.Add(new AnalyzeResult{resultName = ruleName + "Analyze build failed. " + exitCode}); return results; } IntersectResourcesDepedenciesWithBundleDependencies(GetAllBundleDependencies()); results = (from resource in m_ResourcesToDependencies.Keys from dependency in m_ResourcesToDependencies[resource] where m_ExtractData.WriteData.AssetToFiles.ContainsKey(dependency) let assetPath = AssetDatabase.GUIDToAssetPath(dependency.ToString()) let files = m_ExtractData.WriteData.AssetToFiles[dependency] from file in files where m_ExtractData.WriteData.FileToBundle.ContainsKey(file) let bundle = m_ExtractData.WriteData.FileToBundle[file] select new AnalyzeResult{resultName = resource + kDelimiter + bundle + kDelimiter + assetPath, severity = MessageType.Warning}).ToList(); if (results.Count == 0) results.Add(new AnalyzeResult{resultName = ruleName + " - No issues found."}); return results; } public override void ClearAnalysis() { m_Locations.Clear(); m_AddressableAssets.Clear(); m_AllBundleInputDefs.Clear(); m_BundleToAssetGroup.Clear(); m_ResourcesToDependencies.Clear(); m_ExtractData = new ExtractDataTask(); base.ClearAnalysis(); } } }