public static void FinishCollectingDetails(List<AssetInfo> assetsInfo, BuildInfoAssetDetailsCollector collector) { using (ProfileSection.Create("Collecting Assets' Details")) { var details = new List<AssetProperty>(); foreach (var assetInfo in assetsInfo) { if (assetInfo.details != null) { continue; } Log.Debug("Load main asset at: {0}", assetInfo.path); var mainAsset = AssetDatabase.LoadMainAssetAtPath(assetInfo.path); if (mainAsset != null) { details.Clear(); Log.Debug("Collecting details for asset: {0}", assetInfo.path); if (collector.CollectForAsset(details, mainAsset, assetInfo.path)) { assetInfo.details = details.ToArray(); } } } } using (ProfileSection.Create("Cleaning Up Assets' Details")) { // sanitize and sort stuff foreach (var assetInfo in assetsInfo) { assetInfo.details = BuildInfoProcessorUtils.CleanUpAssetsDetails(assetInfo.details, assetInfo.path); } } }
public static void DiscoverDependenciesAndMissingAtlases(List<AssetInfo> assetsInfo, Dictionary<string, AssetInfo> assetsUsedByScenes, BuildInfoAssetDetailsCollector collector) { using (ProfileSection.Create("Resolving dependencies")) { Dictionary<string, AssetInfo> discoveredAtlases = new Dictionary<string, AssetInfo>(); Action<UnitySprite, string> legacySpriteAtlasHandler = null; if (UnityVersionAgnostic.IsUsingLegacySpriteAtlases) { legacySpriteAtlasHandler = CreateLegacyAtlasHandler((atlasPageName, atlasPage) => { AssetInfo entry; if (assetsUsedByScenes.TryGetValue(atlasPageName, out entry)) return entry; else if (discoveredAtlases.TryGetValue(atlasPageName, out entry)) return entry; entry = new AssetInfo() { path = atlasPageName }; discoveredAtlases.Add(atlasPageName, entry); if (collector != null) { List<AssetProperty> details = new List<AssetProperty>(); Log.Debug("Collecting details for asset: {0}", atlasPageName); collector.CollectForAsset(details, atlasPage, atlasPageName); entry.details = details.ToArray(); } return entry; }); } // now resolve dependencies and references foreach (var assetInfo in assetsInfo) { Log.Debug("Collecting dependencies for asset: {0}", assetInfo.path); var dependencies = AssetDatabase.GetDependencies(new[] { assetInfo.path }); bool isPossiblyLeakingLegacyAtlasses = legacySpriteAtlasHandler != null && assetInfo.scenes.Count == 0 && (assetInfo.path.IndexOf("/Resources/", StringComparison.OrdinalIgnoreCase) >= 0) && (assetInfo.path.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase) || assetInfo.path.EndsWith(".asset", StringComparison.OrdinalIgnoreCase)); // new sprites need to load their data manually if (UnityVersionAgnostic.IsUsingSpriteAtlases && assetInfo.path.EndsWith(".spriteatlas")) { foreach (var pageInfo in GetAtlasAssetPages(assetInfo, collector)) { if (discoveredAtlases.ContainsKey(pageInfo.path)) { Log.Warning("Atlas already discovered: {0}", pageInfo.path); } else { discoveredAtlases.Add(pageInfo.path, pageInfo); assetInfo.dependencies.Add(pageInfo.path); } } assetInfo.dependencies.Sort(); } bool mayHaveAnySpritesFromAtlases = false; foreach (var dependency in dependencies) { if (dependency == assetInfo.path) continue; int dependencyIndex = assetInfo.dependencies.BinarySearch(dependency); if (dependencyIndex < 0) { assetInfo.dependencies.Insert(~dependencyIndex, dependency); } if (!isPossiblyLeakingLegacyAtlasses) continue; var textureImporter = AssetImporter.GetAtPath(dependency) as TextureImporter; if (textureImporter != null && textureImporter.qualifiesForSpritePacking) { // oh noes mayHaveAnySpritesFromAtlases = true; } } if (mayHaveAnySpritesFromAtlases) { Log.Debug("Asset {0} may be leaking some texture atlases, going to do a slow check", assetInfo.path); // sad panda var asset = AssetDatabase.LoadMainAssetAtPath(assetInfo.path); if (!asset) { Log.Warning("Unable to do a slow texture atlas check for {0}", assetInfo.path); } else { var assetDependencies = EditorUtility.CollectDependencies(new[] { asset }) .OfType<UnitySprite>(); foreach (var sprite in assetDependencies) { var spriteAssetPath = AssetDatabase.GetAssetPath(sprite); if (!string.IsNullOrEmpty(spriteAssetPath)) { legacySpriteAtlasHandler(sprite, spriteAssetPath); } } } } } assetsInfo.AddRange(discoveredAtlases.Values); } }
public static Dictionary <string, long> GetLastBuildAssetsSizes(string buildLogPath, Dictionary <string, long> scenes) { Log.Debug("Using editor log at: {0}", buildLogPath); // copying log var tempFileName = ReliablePath.GetTempFileName(); Dictionary <string, long> assets = new Dictionary <string, long>(); try { File.Copy(buildLogPath, tempFileName, true); List <string> assetsLines = null; #if UNITY_2017_1_OR_NEWER var parsingLevels = false; var levelSizeRegex = new Regex(@"Level \d+ '(.*)' uses .* ([\d.]+) ([MK]B) uncompressed", RegexOptions.IgnoreCase | UnityVersionAgnostic.CompiledRegexOptions); #endif int lineCount = 0; using (ProfileSection.Create("Reading Log")) using (var reader = new StreamReader(File.Open(tempFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))) { for (string line = reader.ReadLine(); line != null; line = reader.ReadLine(), ++lineCount) { if (line.StartsWith("Used Assets") && line.EndsWith("sorted by uncompressed size:")) { Log.Debug("Found assets marker in the log at line {0}", lineCount); if (assetsLines == null) { assetsLines = new List <string>(); } else { assetsLines.Clear(); } } else if (assetsLines != null) { assetsLines.Add(line); } #if UNITY_2017_1_OR_NEWER if (line.Equals("***Player size statistics***", StringComparison.OrdinalIgnoreCase)) { Log.Debug("Found level statistics start at line {0}", lineCount); scenes.Clear(); parsingLevels = true; } else if (parsingLevels) { var match = levelSizeRegex.Match(line); if (match.Success) { Log.Debug("Found level size at line {0}: {1}", lineCount, match.Value); var path = match.Groups[1].Value; var size = SafeParseUnitySize(match.Groups[2].Value, match.Groups[3].Value, path); long existingSize; if (!scenes.TryGetValue(path, out existingSize)) { scenes.Add(path, size); } else { scenes[path] += size; } } else { Log.Debug("Level statistics ended at {0}", lineCount); parsingLevels = false; } } #endif } if (assetsLines == null) { throw new ArgumentException("No asset info found in the log"); } } // now go through all the lines until separator is found using (ProfileSection.Create("Parsing Assets")) { // line example: 8.1 mb 1.1% Assets/UI/Menu/HUB/SpriteSheets/HUBSpriteSheetMapRegions.png // unity 5.6.1 has a bug: 2.7 mb 1.$% Resources/unity_builtin_extra // mac: 2.7 mb inf% Resources/unity_builtin_extra var assetRegex = new Regex(@"^\s*(\d+\.\d+)\s*([mk]b)\s*(?:\d+\.[\d\$]+|inf)%\s*(.*)$", UnityVersionAgnostic.CompiledRegexOptions); // get assets and sizes foreach (var line in assetsLines) { var match = assetRegex.Match(line); if (!match.Success) { break; } var path = match.Groups[3].Value; if (path.StartsWith(SpriteAtlasPrefix)) { // ignore this, on Android size seems ok, but on PC & iOS it's complete bollocks // 2017_3_1: for new sprites atlases, this entry "merges" by resolution, which is utterly useless continue; } assets.Add(path, SafeParseUnitySize(match.Groups[1].Value, match.Groups[2].Value, path)); } } } finally { try { File.Delete(tempFileName); } catch (System.Exception) { } } return(assets); }