From 570aea7b53b33fb717072bf515cc55402feb690a Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 14 May 2020 12:23:06 +0300 Subject: [PATCH 1/2] Shrink Android apk size --- eng/testing/tests.mobile.targets | 1 + .../AndroidAppBuilder/AndroidAppBuilder.cs | 3 ++ .../msbuild/AndroidAppBuilder/ApkBuilder.cs | 45 ++++++++++++++++--- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/eng/testing/tests.mobile.targets b/eng/testing/tests.mobile.targets index 4e6ceb20b1955e..da84cdf37a6d07 100644 --- a/eng/testing/tests.mobile.targets +++ b/eng/testing/tests.mobile.targets @@ -36,6 +36,7 @@ ProjectName="$(AssemblyName)" MonoRuntimeHeaders="$(RuntimePackNativeDir)include\mono-2.0" MainLibraryFileName="AndroidTestRunner.dll" + StripDebugSymbols="False" OutputDir="$(BundleDir)" SourceDir="$(PublishDir)"> diff --git a/src/mono/msbuild/AndroidAppBuilder/AndroidAppBuilder.cs b/src/mono/msbuild/AndroidAppBuilder/AndroidAppBuilder.cs index 57e1f90aba85d1..cc915aeaf69ef8 100644 --- a/src/mono/msbuild/AndroidAppBuilder/AndroidAppBuilder.cs +++ b/src/mono/msbuild/AndroidAppBuilder/AndroidAppBuilder.cs @@ -41,6 +41,8 @@ public class AndroidAppBuilderTask : Task public string? BuildToolsVersion { get; set; } + public bool StripDebugSymbols { get; set; } + [Output] public string ApkBundlePath { get; set; } = ""!; @@ -59,6 +61,7 @@ public override bool Execute() apkBuilder.MinApiLevel = MinApiLevel; apkBuilder.BuildApiLevel = BuildApiLevel; apkBuilder.BuildToolsVersion = BuildToolsVersion; + apkBuilder.StripDebugSymbols = StripDebugSymbols; (ApkBundlePath, ApkPackageId) = apkBuilder.BuildApk(SourceDir, Abi, MainLibraryFileName, MonoRuntimeHeaders); return true; diff --git a/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs b/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs index 715d4e70b10d41..bd01491f01de89 100644 --- a/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs +++ b/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs @@ -14,6 +14,7 @@ public class ApkBuilder public string? BuildApiLevel { get; set; } public string? BuildToolsVersion { get; set; } public string? OutputDir { get; set; } + public bool StripDebugSymbols { get; set; } public (string apk, string packageId) BuildApk( string sourceDir, string abi, string entryPointLib, string monoRuntimeHeaders) @@ -81,15 +82,28 @@ public class ApkBuilder Directory.CreateDirectory(Path.Combine(OutputDir, "obj")); Directory.CreateDirectory(Path.Combine(OutputDir, "assets")); + var extensionsToIgnore = new List { ".so", ".a", ".gz" }; + if (StripDebugSymbols) + { + extensionsToIgnore.Add(".pdb"); + extensionsToIgnore.Add(".dbg"); + } + // Copy AppDir to OutputDir/assets (ignore native files) Utils.DirectoryCopy(sourceDir, Path.Combine(OutputDir, "assets"), file => { string fileName = Path.GetFileName(file); string extension = Path.GetExtension(file); - // ignore native files, those go to lib/%abi% - if (extension == ".so" || extension == ".a") + + if (file.Any(s => s >= 128)) { - // ignore ".pdb" and ".dbg" to make APK smaller + // non-ascii files/folders are not allowed + return false; + } + if (extensionsToIgnore.Contains(extension)) + { + // ignore native files, those go to lib/%abi% + // also, aapt is not happy about zip files return false; } if (fileName.StartsWith(".")) @@ -129,9 +143,17 @@ public class ApkBuilder .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)); File.WriteAllText(Path.Combine(OutputDir, "runtime-android.c"), runtimeAndroidSrc); - Utils.RunProcess(cmake, workingDir: OutputDir, - args: $"-DCMAKE_TOOLCHAIN_FILE={androidToolchain} -DANDROID_ABI=\"{abi}\" -DANDROID_STL=none " + - $"-DANDROID_NATIVE_API_LEVEL={MinApiLevel} -B runtime-android"); + string cmakeArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchain} -DANDROID_ABI=\"{abi}\" -DANDROID_STL=none " + + $"-DANDROID_NATIVE_API_LEVEL={MinApiLevel} -B runtime-android"; + + if (StripDebugSymbols) + { + // Makefile prints a warrning "clang: warning: argument unused during compilation: '-s'" + // but it does use it and calls `android-%abi%-strip` from NDK + cmakeArgs+= " -DCMAKE_C_FLAGS=-s"; + } + + Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeArgs); Utils.RunProcess("make", workingDir: Path.Combine(OutputDir, "runtime-android")); // 2. Compile Java files @@ -168,7 +190,14 @@ public class ApkBuilder Directory.CreateDirectory(Path.Combine(OutputDir, "lib", abi)); foreach (var dynamicLib in dynamicLibs) { - string destRelative = Path.Combine("lib", abi, Path.GetFileName(dynamicLib)); + string dynamicLibName = Path.GetFileName(dynamicLib); + if (dynamicLibName == "libmonosgen-2.0.so") + { + // we link mono runtime statically into libruntime-android.so + continue; + } + + string destRelative = Path.Combine("lib", abi, dynamicLibName); File.Copy(dynamicLib, Path.Combine(OutputDir, destRelative), true); Utils.RunProcess(aapt, $"add {apkFile} {destRelative}", workingDir: OutputDir); } @@ -194,6 +223,8 @@ public class ApkBuilder Utils.RunProcess(apksigner, $"sign --min-sdk-version {MinApiLevel} --ks debug.keystore " + $"--ks-pass pass:android --key-pass pass:android {alignedApk}", workingDir: OutputDir); + Utils.LogInfo($"\nAPK size: {(new FileInfo(alignedApk).Length / 1000_000.0):0.#} Mb.\n"); + return (alignedApk, packageId); } From d4cd2e986f4a0da087a3d1c4190e1ddda6457e41 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 14 May 2020 16:44:27 +0300 Subject: [PATCH 2/2] bump xharness cli, use cmake config --- .config/dotnet-tools.json | 2 +- .../msbuild/AndroidAppBuilder/ApkBuilder.cs | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 49e475046f79c4..73baf5d1e3a84c 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -15,7 +15,7 @@ ] }, "microsoft.dotnet.xharness.cli": { - "version": "1.0.0-prerelease.20254.3", + "version": "1.0.0-prerelease.20264.2", "commands": [ "xharness" ] diff --git a/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs b/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs index bd01491f01de89..4330c7451f0279 100644 --- a/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs +++ b/src/mono/msbuild/AndroidAppBuilder/ApkBuilder.cs @@ -143,18 +143,25 @@ public class ApkBuilder .Replace("%EntryPointLibName%", Path.GetFileName(entryPointLib)); File.WriteAllText(Path.Combine(OutputDir, "runtime-android.c"), runtimeAndroidSrc); - string cmakeArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchain} -DANDROID_ABI=\"{abi}\" -DANDROID_STL=none " + + string cmakeGenArgs = $"-DCMAKE_TOOLCHAIN_FILE={androidToolchain} -DANDROID_ABI=\"{abi}\" -DANDROID_STL=none " + $"-DANDROID_NATIVE_API_LEVEL={MinApiLevel} -B runtime-android"; + string cmakeBuildArgs = "--build runtime-android"; + if (StripDebugSymbols) { - // Makefile prints a warrning "clang: warning: argument unused during compilation: '-s'" - // but it does use it and calls `android-%abi%-strip` from NDK - cmakeArgs+= " -DCMAKE_C_FLAGS=-s"; + // Use "-s" to strip debug symbols, it complains it's unused but it works + cmakeGenArgs+= " -DCMAKE_BUILD_TYPE=MinSizeRel -DCMAKE_C_FLAGS=\"-s -Wno-unused-command-line-argument\""; + cmakeBuildArgs += " --config MinSizeRel"; + } + else + { + cmakeGenArgs += " -DCMAKE_BUILD_TYPE=Debug"; + cmakeBuildArgs += " --config Debug"; } - Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeArgs); - Utils.RunProcess("make", workingDir: Path.Combine(OutputDir, "runtime-android")); + Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeGenArgs); + Utils.RunProcess(cmake, workingDir: OutputDir, args: cmakeBuildArgs); // 2. Compile Java files @@ -197,6 +204,8 @@ public class ApkBuilder continue; } + // NOTE: we can run android-strip tool from NDK to shrink native binaries here even more. + string destRelative = Path.Combine("lib", abi, dynamicLibName); File.Copy(dynamicLib, Path.Combine(OutputDir, destRelative), true); Utils.RunProcess(aapt, $"add {apkFile} {destRelative}", workingDir: OutputDir);