Пример #1
0
        private static BuildArtifactsInfo CreateForStandaloneWin(string buildPath, string dataDirectoryOverride)
        {
            var dataDirectory = DropExtension(buildPath) + "_Data";

            if (!string.IsNullOrEmpty(dataDirectoryOverride))
            {
                dataDirectory = dataDirectoryOverride;
            }

            var result = CreateFromFileSystem(dataDirectory, dataDirectory, "StreamingAssets");

            result.runtimeSize = result.runtimeSize.uncompressed + GetDirectorySizeNoThrow(ReliablePath.Combine(dataDirectory, "Mono"));

            // get the exe
            var additionalRuntimeSize = GetFileSizeNoThrow(buildPath);

            if (UnityVersionAgnostic.HasStuffInRootForStandaloneBuild)
            {
                var directory = ReliablePath.GetDirectoryName(buildPath);
                additionalRuntimeSize += GetFileSizeNoThrow(ReliablePath.Combine(directory, "UnityPlayer.dll"), logError: false);
                additionalRuntimeSize += GetFileSizeNoThrow(ReliablePath.Combine(directory, "UnityCrashHandler64.exe"), logError: false);
                additionalRuntimeSize += GetDirectorySizeNoThrow(ReliablePath.Combine(directory, "Mono"));
            }

            result.totalSize.uncompressed   += additionalRuntimeSize;
            result.runtimeSize.uncompressed += additionalRuntimeSize;

            return(result);
        }
Пример #2
0
        public static void RefreshModulesInfo(List<AssetInfo> infos, BuildArtifactsInfo artifactsInfo)
        {
            if (artifactsInfo.managedModules.Count <= 0)
                return;

            var modulesSuspects = infos.Where(x => x.path.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                                    .ToLookup(x => ReliablePath.GetFileName(x.path));

            // add modules
            foreach (var kv in artifactsInfo.managedModules)
            {
                var existingEntries = modulesSuspects[kv.Key].ToList();
                if (existingEntries.Any())
                {
                    foreach (var entry in existingEntries)
                    {
                        entry.size += kv.Value.uncompressed;
                        UpdateCompressedSize(entry, kv.Value.compressed);
                    }
                }
                else
                {
                    var entry = new AssetInfo()
                    {
                        path = kv.Key,
                        size = kv.Value.uncompressed,
                    };
                    UpdateCompressedSize(entry, kv.Value.compressed);
                    infos.Add(entry);
                }
            }
        }
Пример #3
0
        private static BuildArtifactsInfo CreateFromFileSystem(string totalSizeDir, string dataDirectory, string streamingAssetsName)
        {
            var modulesDirectory         = ReliablePath.Combine(dataDirectory, "Managed");
            var streamingAssetsDirectory = ReliablePath.Combine(dataDirectory, streamingAssetsName);

            Dictionary <string, SizePair> modules = new Dictionary <string, SizePair>();
            long runtimeSize = 0;

            if (Directory.Exists(modulesDirectory))
            {
                // dlls are included as assets, so don't count them as runtime size
                modules = Directory.GetFiles(modulesDirectory, "*.dll", SearchOption.TopDirectoryOnly)
                          .ToDictionary(x => ReliablePath.GetFileName(x), x => (SizePair)GetFileSizeNoThrow(x));

                runtimeSize = GetDirectorySizeNoThrow(modulesDirectory) - Enumerable.Sum(modules, x => x.Value.uncompressed);
            }

            var unityResources = s_unityResourcesNames
                                 .Select(x => new { Relative = x, Actual = ReliablePath.Combine(dataDirectory, x) })
                                 .Where(x => !Directory.Exists(x.Actual))
                                 .Select(x => new { x.Relative, File = new FileInfo(x.Actual) })
                                 .Where(x => x.File.Exists)
                                 .ToDictionary(x => x.Relative, x => (SizePair)x.File.Length);

            return(new BuildArtifactsInfo()
            {
                totalSize = GetDirectorySizeNoThrow(totalSizeDir),
                streamingAssetsSize = GetDirectorySizeNoThrow(streamingAssetsDirectory),
                runtimeSize = runtimeSize,
                managedModules = modules,
                unityResources = unityResources,
                sceneSizes = CalculateScenesSizes(x =>
                {
                    var fileInfo = new FileInfo(ReliablePath.Combine(dataDirectory, x));
                    if (!fileInfo.Exists)
                    {
                        return null;
                    }
                    return fileInfo.Length;
                })
            });
        }
Пример #4
0
        private static BuildArtifactsInfo CreateForWebGL(string buildPath)
        {
            var compressedSize      = GetDirectorySizeNoThrow(buildPath);
            var totalSize           = compressedSize;
            var streamingAssetsSize = GetDirectorySizeNoThrow(ReliablePath.Combine(buildPath, "StreamingAssets"));

            var latestReport = UnityVersionAgnostic.GetLatestBuildReport();

            if (latestReport == null)
            {
                throw new System.InvalidOperationException("Unable to retreive native Unity report");
            }

            var prop = new SerializedObject(latestReport).FindPropertyOrThrow("m_Files");

            var scenes  = new List <SizePair>();
            var modules = new Dictionary <string, SizePair>();

            for (int propIdx = 0; propIdx < prop.arraySize; ++propIdx)
            {
                var elem = prop.GetArrayElementAtIndex(propIdx);
                var role = elem.FindPropertyRelativeOrThrow("role").stringValue;

                if (role == "Scene")
                {
                    var path      = elem.FindPropertyRelativeOrThrow("path").stringValue;
                    var prefix    = "level";
                    var lastIndex = path.LastIndexOf(prefix);
                    if (lastIndex < 0)
                    {
                        Log.Warning("Unexpected level path: " + path);
                        continue;
                    }

                    var levelNumberStr = path.Substring(lastIndex + prefix.Length);
                    var levelNumber    = int.Parse(levelNumberStr);

                    // pad with zeros
                    for (int i = scenes.Count; i <= levelNumber; ++i)
                    {
                        scenes.Add(0);
                    }

                    var s = elem.FindPropertyRelative("totalSize").longValue;
                    scenes[levelNumber] = new SizePair(s, s);
                }
                else if (role == "DependentManagedLibrary" || role == "ManagedLibrary")
                {
                    var path      = elem.FindPropertyRelativeOrThrow("path").stringValue;
                    var prefix    = "/Managed/";
                    var lastIndex = path.LastIndexOf(prefix);
                    if (lastIndex < 0)
                    {
                        Log.Warning("Unexpected module path: " + path);
                        continue;
                    }

                    var moduleName = path.Substring(lastIndex + prefix.Length);
                    var s          = elem.FindPropertyRelative("totalSize").longValue;
                    modules.Add(moduleName, new SizePair(0, s));
                }
            }

            // try to run 7z to get actual data size
            var releaseDir = ReliablePath.Combine(buildPath, "Release");

            if (Directory.Exists(releaseDir))
            {
                var buildName        = buildPath.Split(new[] { "/", "\\" }, StringSplitOptions.RemoveEmptyEntries).Last();
                var zipPath          = ReliablePath.Combine(releaseDir, buildName + ".datagz");
                var uncompressedSize = Get7ZipArchiveUncompressedSize(zipPath);
                if (uncompressedSize >= 0)
                {
                    totalSize += uncompressedSize;
                    totalSize -= GetFileSizeNoThrow(zipPath);
                }
            }
            else
            {
                var buildDir = ReliablePath.Combine(buildPath, "Build");
                if (Directory.Exists(buildDir))
                {
                    foreach (var compressedFile in Directory.GetFiles(buildDir, "*.unityweb"))
                    {
                        var uncompressedSize = Get7ZipArchiveUncompressedSize(compressedFile);
                        if (uncompressedSize >= 0)
                        {
                            totalSize += uncompressedSize;
                            totalSize -= GetFileSizeNoThrow(compressedFile);
                        }
                    }
                }
            }

            return(new BuildArtifactsInfo()
            {
                totalSize = new SizePair(compressedSize, totalSize),
                streamingAssetsSize = streamingAssetsSize,
                sceneSizes = scenes,
                managedModules = modules,
            });
        }
Пример #5
0
 private static string DropExtension(string path)
 {
     return(ReliablePath.Combine(ReliablePath.GetDirectoryName(path), ReliablePath.GetFileNameWithoutExtension(path)));
 }
Пример #6
0
        private static BuildArtifactsInfo CreateForAndroid(string buildPath, bool hasObb)
        {
            Dictionary <string, SizePair> partialResults = new Dictionary <string, SizePair>();
            Dictionary <string, SizePair> managedModules = new Dictionary <string, SizePair>();
            Dictionary <string, SizePair> otherAssets    = new Dictionary <string, SizePair>();

            const string DataDirectory = "assets/bin/Data/";

            long compressedSize      = 0;
            long uncompressedSize    = 0;
            long streamingAssetsSize = 0;

            SizePair runtimeSize    = new SizePair();
            var      unityResources = new Dictionary <string, SizePair>();

            //var sources = Enumerable.Repeat(new { Source = "Apk", Files = GetFilesFromZipArchive(buildPath) }, 1);

            var sources = new List <KeyValuePair <string, string> >();

            sources.Add(new KeyValuePair <string, string>("Apk", buildPath));

            if (hasObb)
            {
                var obbPath = DropExtension(buildPath) + ".main.obb";
                sources.Add(new KeyValuePair <string, string>("Obb", obbPath));
            }

            foreach (var source in sources)
            {
                var path = source.Value;

                compressedSize += GetFileSizeNoThrow(path);

                //files = files.Concat(GetFilesFromZipArchive(obbPath));
                var files = GetFilesFromZipArchive(path);
                foreach (var entry in files)
                {
                    uncompressedSize += entry.size.uncompressed;

                    if (entry.path.StartsWith(DataDirectory))
                    {
                        var fileName = entry.path.Substring(DataDirectory.Length);
                        if (fileName.StartsWith("level") || fileName.StartsWith("mainData"))
                        {
                            partialResults.Add(fileName, entry.size);
                        }
                        else if (fileName.StartsWith("Managed/"))
                        {
                            // dlls are included as assets, so don't count them as a part of runtime
                            if (fileName.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
                            {
                                var actualFileName = ReliablePath.GetFileName(fileName);
                                managedModules.Add(actualFileName, entry.size);
                            }
                            else
                            {
                                runtimeSize.compressed   += entry.size.compressed;
                                runtimeSize.uncompressed += entry.size.uncompressed;
                            }
                        }
                        else if (s_unityResourcesNames.Contains(fileName))
                        {
                            unityResources.Add(fileName, entry.size);
                        }
                        else
                        {
                            // is it a guid?
                            var justFileName = Path.GetFileNameWithoutExtension(fileName);
                            if (justFileName.Length == 32 && justFileName.All(x => char.IsDigit(x) || x >= 'a' && x <= 'f' || x >= 'A' && x <= 'F'))
                            {
                                SizePair existingEntry;
                                if (otherAssets.TryGetValue(justFileName, out existingEntry))
                                {
                                    otherAssets[justFileName] = new SizePair(existingEntry.compressed + entry.size.compressed, existingEntry.uncompressed + entry.size.uncompressed);
                                }
                                else
                                {
                                    otherAssets.Add(justFileName, entry.size);
                                }
                            }
                        }
                    }
                    else if (entry.path.StartsWith("assets/"))
                    {
                        streamingAssetsSize += entry.size.uncompressed;
                    }
                    else if (entry.path.StartsWith("lib/"))
                    {
                        runtimeSize.compressed   += entry.size.compressed;
                        runtimeSize.uncompressed += entry.size.uncompressed;
                    }
                }
            }

            var scenes = CalculateScenesSizes(x =>
            {
                SizePair result;
                if (partialResults.TryGetValue(x, out result))
                {
                    return(result);
                }
                return(null);
            });

            return(new BuildArtifactsInfo()
            {
                managedModules = managedModules,
                sceneSizes = scenes,
                streamingAssetsSize = streamingAssetsSize,
                totalSize = new SizePair(compressedSize, uncompressedSize),
                runtimeSize = runtimeSize,
                unityResources = unityResources,
                otherAssets = otherAssets,
            });
        }
Пример #7
0
        private static BuildArtifactsInfo CreateForIOS(string buildPath)
        {
            var dataDirectory = ReliablePath.Combine(buildPath, "Data");

            return(CreateFromFileSystem(dataDirectory, dataDirectory, "Raw"));
        }
Пример #8
0
        public void Refresh(IEnumerable <string> allAssets, IEnumerable <AssetInfo> usedAssets)
        {
            foreach (var assetPath in allAssets)
            {
                string directoryPath;
                bool   isDirectory = true;

                if (Directory.Exists(assetPath))
                {
                    directoryPath = assetPath.TrimEnd('/', '\\');
                }
                else
                {
                    isDirectory   = false;
                    directoryPath = ReliablePath.GetDirectoryName(assetPath);
                }

                bool hadSpecialEditorDirectory = false;

                while (!string.IsNullOrEmpty(directoryPath) && IsInSpecialEditorDirectory(directoryPath))
                {
                    hadSpecialEditorDirectory = true;
                    directoryPath             = ReliablePath.GetDirectoryName(directoryPath);
                }

                // add directory entries
                while (!string.IsNullOrEmpty(directoryPath))
                {
                    var directoryIndex = paths.BinarySearch(directoryPath);
                    if (directoryIndex < 0)
                    {
                        directoryIndex = ~directoryIndex;
                        paths.Insert(directoryIndex, directoryPath);
                        entries.Insert(directoryIndex, new DirectoryEntry());
                    }

                    if (!isDirectory)
                    {
                        if (hadSpecialEditorDirectory)
                        {
                            ++entries[directoryIndex].editorAssetsCount;
                        }
                        else
                        {
                            ++entries[directoryIndex].assetsCount;
                        }
                    }

                    entries[directoryIndex].hadSpecialEditorDirectory |= hadSpecialEditorDirectory;
                    directoryPath = ReliablePath.GetDirectoryName(directoryPath);
                }
            }

            // now do the count
            foreach (var asset in usedAssets)
            {
                var guid = AssetDatabase.AssetPathToGUID(asset.path);

                if (IsInSpecialEditorDirectory(asset.path))
                {
                    continue;
                }

                if (string.IsNullOrEmpty(guid))
                {
                    continue;
                }

                var directoryPath = ReliablePath.GetDirectoryName(asset.path);

                while (!string.IsNullOrEmpty(directoryPath))
                {
                    var directoryIndex = paths.BinarySearch(directoryPath);
                    if (directoryIndex >= 0)
                    {
                        ++entries[directoryIndex].usedAssetsCount;
                        entries[directoryIndex].size += asset.size;
                        directoryPath = ReliablePath.GetDirectoryName(directoryPath);
                    }
                    else
                    {
                        break;
                    }
                }
            }
        }
Пример #9
0
        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);
        }