From 78d95de470f14eb2f1edb0c0a0e142d468f63b13 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 11 Nov 2019 14:57:39 -0600 Subject: [PATCH] [Xamarin.Android.Build.Tasks] use aapt2 option to generate proguard rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: https://developer.android.com/studio/build/shrink-code#configuration-files Fixes: http://work.azdo.io/825420 Fixes: https://github.com/xamarin/xamarin-android/issues/3636 Let's say you used the following code fragment in an Android layout: Currently, you would need to add a ProGuard rule regardless if you were using ProGuard or R8. Fortunately, `aapt2` has a command-line switch: > aapt2 link -h ... --proguard arg Output file for generated Proguard rules. According to the Google docs for R8: AAPT2 generates keep rules based on references to classes in your app’s manifest, layouts, and other app resources. For example, AAPT2 includes a keep rule for each Activity that you register in your app’s manifest as an entry point. I added a new `ProguardRuleOutput` property for `` and set it in cases where `$(AndroidLinkTool)` is not blank. I added this file to `@(ProguardConfiguration)`, so it will get picked up by ProGuard or R8. I updated an existing test for this scenario, checking that a type is generated in the rules and exists in the final `classes.dex` file. --- .../MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets | 6 ++++++ src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs | 5 +++++ .../Tests/Xamarin.Android.Build.Tests/BuildTest.cs | 8 +++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets index 5cd02d6aca5..2e9bea69a7d 100644 --- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets +++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets @@ -156,6 +156,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. Condition="'$(_AndroidUseAapt2)' == 'True'"> --no-version-vectors $(AndroidAapt2LinkExtraArgs) + <_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs index e7d131ab517..a8b3fb6e9d0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs @@ -74,6 +74,8 @@ public class Aapt2Link : Aapt2 { public bool ProtobufFormat { get; set; } + public string ProguardRuleOutput { get; set; } + AssemblyIdentityMap assemblyMap = new AssemblyIdentityMap (); List tempFiles = new List (); @@ -185,6 +187,9 @@ string GenerateCommandLineCommands (string ManifestFile, string currentAbi, stri if (!string.IsNullOrWhiteSpace (assetDir) && Directory.Exists (assetDir)) cmd.AppendSwitchIfNotNull ("-A ", assetDir); } + if (!string.IsNullOrEmpty (ProguardRuleOutput)) { + cmd.AppendSwitchIfNotNull ("--proguard ", ProguardRuleOutput); + } cmd.AppendSwitchIfNotNull ("-o ", currentResourceOutputFile); return cmd.ToString (); } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 421f7ea1ec6..4ac2242ce89 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1011,7 +1011,7 @@ public void BuildAotApplicationAndBundleAndÜmläüts (string supportedAbis, boo [Test] public void BuildProguardEnabledProject ([Values (true, false)] bool isRelease, [Values ("dx", "d8")] string dexTool, [Values ("", "proguard", "r8")] string linkTool) { - var proj = new XamarinAndroidApplicationProject { + var proj = new XamarinFormsAndroidApplicationProject { IsRelease = isRelease, DexTool = dexTool, LinkTool = linkTool, @@ -1023,6 +1023,11 @@ public void BuildProguardEnabledProject ([Values (true, false)] bool isRelease, var proguardProjectPrimary = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "proguard", "proguard_project_primary.cfg"); FileAssert.Exists (proguardProjectPrimary); Assert.IsTrue (StringAssertEx.ContainsText (File.ReadAllLines (proguardProjectPrimary), $"-keep class {proj.JavaPackageName}.MainActivity"), $"`{proj.JavaPackageName}.MainActivity` should exist in `proguard_project_primary.cfg`!"); + + var toolbar_class = "android.support.v7.widget.Toolbar"; + var aapt_rules = b.Output.GetIntermediaryPath ("aapt_rules.txt"); + FileAssert.Exists (aapt_rules); + Assert.IsTrue (StringAssertEx.ContainsText (File.ReadAllLines (aapt_rules), $"-keep class {toolbar_class}"), $"`{toolbar_class}` should exist in `{aapt_rules}`!"); } var dexFile = b.Output.GetIntermediaryPath (Path.Combine ("android", "bin", "classes.dex")); @@ -1030,6 +1035,7 @@ public void BuildProguardEnabledProject ([Values (true, false)] bool isRelease, var classes = new [] { "Lmono/MonoRuntimeProvider;", "Landroid/runtime/UncaughtExceptionHandler;", + "Landroid/support/v7/widget/Toolbar;" }; foreach (var className in classes) { Assert.IsTrue (DexUtils.ContainsClassWithMethod (className, "", "()V", dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!");