Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] use aapt2 option to generate proguard r…
Browse files Browse the repository at this point in the history
…ules

Context: https://developer.android.com/studio/build/shrink-code#configuration-files
Fixes: http://work.azdo.io/825420
Fixes: dotnet#3636

Let's say you used the following code fragment in an Android layout:

    <android.support.v7.widget.Toolbar />

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 `<Aapt2Link/>` 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.
  • Loading branch information
jonathanpeppers committed Nov 12, 2019
1 parent 1a61fa8 commit 78d95de
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
Condition="'$(_AndroidUseAapt2)' == 'True'">
<PropertyGroup>
<AndroidAapt2LinkExtraArgs Condition=" '$(_AndroidUseAapt2)' == 'True' And $(AndroidResgenExtraArgs.Contains('--no-version-vectors')) And !($(AndroidAapt2LinkExtraArgs.Contains('--no-version-vectors'))) ">--no-version-vectors $(AndroidAapt2LinkExtraArgs) </AndroidAapt2LinkExtraArgs>
<_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt</_Aapt2ProguardRules>
</PropertyGroup>
<Aapt2Link
Condition=" '$(_AndroidResourceDesignerFile)' != '' And '$(_AndroidUseAapt2)' == 'True' "
Expand All @@ -181,7 +182,12 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
ToolPath="$(Aapt2ToolPath)"
ToolExe="$(Aapt2ToolExe)"
UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"
ProguardRuleOutput="$(_Aapt2ProguardRules)"
/>
<ItemGroup Condition=" '$(_Aapt2ProguardRules)' != '' And Exists('$(_Aapt2ProguardRules)') ">
<ProguardConfiguration Include="$(_Aapt2ProguardRules)" />
<FileWrites Include="$(_Aapt2ProguardRules)" />
</ItemGroup>
<ItemGroup>
<FileWrites Include="$(IntermediateOutputPath)R.txt" Condition=" '$(_AndroidUseAapt2)' == 'True' And Exists ('$(IntermediateOutputPath)R.txt') " />
</ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions src/Xamarin.Android.Build.Tasks/Tasks/Aapt2Link.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public class Aapt2Link : Aapt2 {

public bool ProtobufFormat { get; set; }

public string ProguardRuleOutput { get; set; }

AssemblyIdentityMap assemblyMap = new AssemblyIdentityMap ();
List<string> tempFiles = new List<string> ();

Expand Down Expand Up @@ -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 ();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -1023,13 +1023,19 @@ 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"));
FileAssert.Exists (dexFile);
var classes = new [] {
"Lmono/MonoRuntimeProvider;",
"Landroid/runtime/UncaughtExceptionHandler;",
"Landroid/support/v7/widget/Toolbar;"
};
foreach (var className in classes) {
Assert.IsTrue (DexUtils.ContainsClassWithMethod (className, "<init>", "()V", dexFile, AndroidSdkPath), $"`{dexFile}` should include `{className}`!");
Expand Down

0 comments on commit 78d95de

Please sign in to comment.