Created
May 20, 2022 05:53
-
-
Save Artein/d91e9280838ea5a464fd861081645490 to your computer and use it in GitHub Desktop.
AndroidSymbolsShrinkerPostProcessor updates Android debug symbols to decrease the size via 7z
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if UNITY_EDITOR && UNITY_ANDROID | |
using System; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using UnityEditor; | |
using UnityEditor.Build; | |
using UnityEditor.Build.Reporting; | |
using UnityEngine; | |
using UnityEngine.Assertions; | |
using Debug = UnityEngine.Debug; | |
namespace Evolver.Editor.Tools.Build | |
{ | |
public class AndroidSymbolsShrinkerPostProcessor : IPostprocessBuildWithReport | |
{ | |
private static string LastSymbolToShrinkLocationSaveKey => nameof(LastSymbolToShrinkLocationSaveKey); | |
private static string DataPathNoAssets => Application.dataPath.Substring(0, Application.dataPath.Length - 7); // 7=/Assets | |
private static string BaseToolFullPath => Path.GetFullPath(Path.Combine(DataPathNoAssets, "Tools", "7z", "7zz")); | |
private static string App7ZFullPath => Application.platform == RuntimePlatform.WindowsEditor ? BaseToolFullPath + ".exe" : BaseToolFullPath; | |
private class ProcessResult | |
{ | |
private int ExitCode { get; } | |
private string StdOut { get; } | |
private string StdErr { get; } | |
internal bool Failure => ExitCode != 0; | |
internal ProcessResult(int exitCode, string stdOut, string stdErr) | |
{ | |
ExitCode = exitCode; | |
StdOut = stdOut; | |
StdErr = stdErr; | |
} | |
public override string ToString() | |
{ | |
return $"Exit Code: {ExitCode}\nStdOut:\n{StdOut}\nStdErr:\n{StdErr}"; | |
} | |
} | |
private static ProcessResult RunProcess(string workingDirectory, string fileName, string args) | |
{ | |
Log($"Executing {fileName} {args} (Working Directory: {workingDirectory}"); | |
var process = new Process(); | |
process.StartInfo.FileName = fileName; | |
process.StartInfo.Arguments = args; | |
process.StartInfo.UseShellExecute = false; | |
process.StartInfo.RedirectStandardOutput = true; | |
process.StartInfo.RedirectStandardError = true; | |
process.StartInfo.WorkingDirectory = workingDirectory; | |
process.StartInfo.CreateNoWindow = true; | |
var output = new StringBuilder(); | |
process.OutputDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
output.AppendLine(e.Data); | |
} | |
}; | |
var error = new StringBuilder(); | |
process.ErrorDataReceived += (sender, e) => | |
{ | |
if (!string.IsNullOrEmpty(e.Data)) | |
{ | |
error.AppendLine(e.Data); | |
} | |
}; | |
process.Start(); | |
process.BeginOutputReadLine(); | |
process.BeginErrorReadLine(); | |
process.WaitForExit(); | |
Log($"{fileName} exited with {process.ExitCode}"); | |
return new ProcessResult(process.ExitCode, output.ToString(), error.ToString()); | |
} | |
private static void Cleanup(string path) | |
{ | |
if (Directory.Exists(path)) | |
{ | |
Log($"Delete {path}"); | |
Directory.Delete(path, true); | |
} | |
if (File.Exists(path)) | |
{ | |
Log($"Delete {path}"); | |
File.Delete(path); | |
} | |
} | |
[MenuItem("Tools/Build/Shrink Android Symbols")] | |
public static void ShrinkSymbols() | |
{ | |
var location = EditorPrefs.GetString(LastSymbolToShrinkLocationSaveKey, Path.Combine(Application.dataPath, "..")); | |
location = EditorUtility.OpenFilePanel("Open Android Symbol Package to shrink", location, "*.zip"); | |
ShrinkSymbols(location); | |
} | |
private static void ShrinkSymbols(string location) | |
{ | |
if (string.IsNullOrEmpty(location)) | |
{ | |
Log($"{nameof(AndroidSymbolsShrinkerPostProcessor)}: Selected location is null"); | |
return; | |
} | |
var targetDirectory = Path.GetDirectoryName(location); | |
Assert.IsNotNull(targetDirectory, "targetDirectory != null"); | |
var intermediatePath = Path.Combine(targetDirectory, "TempShrink"); | |
var newZipFilePath = Path.Combine(targetDirectory, Path.GetFileNameWithoutExtension(location) + ".shrank.zip"); | |
EditorPrefs.SetString(LastSymbolToShrinkLocationSaveKey, targetDirectory); | |
if (!File.Exists(App7ZFullPath)) | |
{ | |
throw new BuildFailedException($"Failed to locate {App7ZFullPath}"); | |
} | |
Cleanup(intermediatePath); | |
Cleanup(newZipFilePath); | |
var result = RunProcess(targetDirectory, App7ZFullPath, $"x -o\"{intermediatePath}\" \"{location}\""); | |
if (result.Failure) | |
{ | |
throw new BuildFailedException(result.ToString()); | |
} | |
EditorUtility.DisplayProgressBar("Shrinking Android debug symbols", "Deleting/Renaming/Compressing symbol files", 0.5f); | |
var files = Directory.GetFiles(intermediatePath, "*.*", SearchOption.AllDirectories); | |
const string symSo = ".sym.so"; | |
foreach (var file in files) | |
{ | |
if (file.EndsWith(".dbg.so")) | |
{ | |
Cleanup(file); | |
} | |
if (file.EndsWith(symSo)) | |
{ | |
var fileSO = file.Substring(0, file.Length - symSo.Length) + ".so"; | |
Log($"Rename {file} --> {fileSO}"); | |
File.Move(file, fileSO); | |
} | |
} | |
const Compression7Zip compressionType = Compression7Zip.Normal; | |
const int passNumber = 1; // [1; 15] - bigger is more compression, but slower | |
// for other arguments check Tools/7z/MANUAL/start.htm | |
var compressionArgs = $"a -tzip -mm=Deflate -mx={(int)compressionType} -mfb=257 -mpass={passNumber} -mmt -r \"{newZipFilePath}\""; | |
// ReSharper disable once StringLiteralTypo | |
result = RunProcess(intermediatePath, App7ZFullPath, compressionArgs); | |
EditorUtility.ClearProgressBar(); | |
if (result.Failure) | |
{ | |
throw new BuildFailedException(result.ToString()); | |
} | |
Cleanup(intermediatePath); | |
// Check results size | |
var resultZipFileInfo = new FileInfo(newZipFilePath); | |
var resultZipFileMB = (double)resultZipFileInfo.Length / 1024 / 1024; | |
if (resultZipFileMB > 299.9999d) | |
{ | |
throw new BuildFailedException("Android debug symbols too big to be uploaded to GooglePlay (must be up to 300 MB). " + | |
"Try remove unused code or increase compression ratio (will increase build time)"); | |
} | |
if (resultZipFileMB > 290) | |
{ | |
LogWarning($"Android debug symbols were shrank to {resultZipFileMB} MB. It is too close to limit of 300 MB for GooglePlay"); | |
} | |
Log($"The new shrank symbols: {newZipFilePath} ({resultZipFileMB} MB)"); | |
} | |
int IOrderedCallback.callbackOrder => (int)PostprocessorOrder.Android_ShrinkDebugSymbols; | |
void IPostprocessBuildWithReport.OnPostprocessBuild(BuildReport report) | |
{ | |
if (!EditorUserBuildSettings.androidCreateSymbolsZip) | |
{ | |
return; | |
} | |
var outputFilePath = report.files.FirstOrDefault(file => | |
file.path.Substring(Math.Max(0, file.path.Length - 4)) == | |
(EditorUserBuildSettings.buildAppBundle ? ".aab" : ".apk")).path; | |
if (string.IsNullOrWhiteSpace(outputFilePath)) | |
{ | |
return; | |
} | |
var outputFileNameWithoutExtension = Path.GetFileNameWithoutExtension(outputFilePath); | |
var outputFileParentDirectoryPath = Path.GetDirectoryName(outputFilePath); | |
var symbolFileExistRegex = new Regex($"^{outputFileNameWithoutExtension}.+\\.symbols\\.zip$"); | |
var symbolFilePath = Directory | |
.GetFiles(outputFileParentDirectoryPath ?? string.Empty, "*zip", SearchOption.TopDirectoryOnly) | |
.FirstOrDefault(path => symbolFileExistRegex.IsMatch(Path.GetFileName(path))); | |
if (string.IsNullOrWhiteSpace(symbolFilePath)) | |
{ | |
LogError("Symbol file not found with given format!"); | |
return; | |
} | |
ShrinkSymbols(symbolFilePath); | |
File.Delete(symbolFilePath); | |
} | |
private static void Log(string message) | |
{ | |
Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, message); | |
} | |
private static void LogWarning(string message) | |
{ | |
Debug.LogFormat(LogType.Warning, LogOption.NoStacktrace, null, message); | |
} | |
private static void LogError(string message) | |
{ | |
Debug.LogFormat(LogType.Error, LogOption.NoStacktrace, null, message); | |
} | |
private enum Compression7Zip | |
{ | |
NoCompression = 0, | |
Fastest = 1, | |
Fast = 3, | |
Normal = 5, | |
Maximum = 7, | |
Ultra = 9 | |
} | |
} | |
} | |
#endif // UNITY_EDITOR && UNITY_ANDROID |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment