Skip to content

Commit

Permalink
[r8] ignore custom multidex.keep files if API >= 21
Browse files Browse the repository at this point in the history
Context: https://r8.googlesource.com/r8/+/0e5c4339df0207a0e38f11438db84b29f328f777%5E%21/

In R8 1.4.93, they appear to have removed support for custom
`multidex.keep` files when `minSdkVersion` >= 21. R8 decides on its
own if multidex is needed, and splits up dex files appropriately.

Since `r8.jar` will now abort with an error if you specify
`multidex.keep` files and `minSdkVersion` >= 21. I feel all we can do
in this case is add a new warning if developers specify
`@(MultiDexMainDexList)` with R8--then allow R8 to calculate multidex
on its own.

I updated the `MultiDexCustomMainDexFileList` to check for these
scenarios. R8 does appear to create multiple dex files without any
multidex settings applied.
  • Loading branch information
jonathanpeppers committed May 28, 2019
1 parent 1b36677 commit 40712ea
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 11 deletions.
26 changes: 26 additions & 0 deletions Documentation/guides/messages/xa4306.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Xamarin.Android warning XA4306
description: XA4306 warning code
ms.date: 05/28/2019
---
# Xamarin.Android warning XA4306

## Issue

[d8/r8][r8_source] does not support using custom `multidex.keep` files
if `minSdkVersion` >= 21, e.g. devices where native multidex is
supported. In theses cases, d8/r8 calculates the contents of the main
`classes.dex` without rules specified by developers.

[This change][r8_commit] in d8/r8 appeared in version 1.4.93.

## Solution

Verify you are not declaring any `MultiDexMainDexList` build items.

Consider submitting a [bug][bug] if your application does not function
after removing any `MultiDexMainDexList` items.

[r8_source]: https://r8.googlesource.com/
[r8_commit]: https://r8.googlesource.com/r8/+/0e5c4339df0207a0e38f11438db84b29f328f777%5E%21/
[bug]: https://github.com/xamarin/xamarin-android/wiki/Submitting-Bugs,-Feature-Requests,-and-Pull-Requests
6 changes: 4 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/D8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ protected override string GenerateCommandLineCommands ()

protected virtual string MainClass => "com.android.tools.r8.D8";

protected int MinApiVersion { get; set; }

protected virtual CommandLineBuilder GetCommandLineBuilder ()
{
var cmd = new CommandLineBuilder ();
Expand All @@ -66,8 +68,8 @@ protected virtual CommandLineBuilder GetCommandLineBuilder ()
//NOTE: if this is blank, we can omit --min-api in this call
if (!string.IsNullOrEmpty (AndroidManifestFile)) {
var doc = AndroidAppManifest.Load (AndroidManifestFile, MonoAndroidHelper.SupportedVersions);
int minApiVersion = doc.MinSdkVersion == null ? 4 : (int)doc.MinSdkVersion;
cmd.AppendSwitchIfNotNull ("--min-api ", minApiVersion.ToString ());
MinApiVersion = doc.MinSdkVersion == null ? 4 : (int)doc.MinSdkVersion;
cmd.AppendSwitchIfNotNull ("--min-api ", MinApiVersion.ToString ());
}

if (!EnableDesugar)
Expand Down
4 changes: 3 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/R8.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ protected override CommandLineBuilder GetCommandLineBuilder ()
var cmd = base.GetCommandLineBuilder ();

if (EnableMultiDex) {
if (string.IsNullOrEmpty (MultiDexMainDexListFile)) {
if (MinApiVersion >= 21) {
Log.LogCodedWarning ("XA4306", "R8 does not support `MultiDexMainDexList` files when android:minSdkVersion >= 21");
} else if (string.IsNullOrEmpty (MultiDexMainDexListFile)) {
Log.LogCodedWarning ("XA4305", $"MultiDex is enabled, but '{nameof (MultiDexMainDexListFile)}' was not specified.");
} else {
var content = new List<string> ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -965,30 +965,34 @@ public void BuildAfterMultiDexIsNotRequired ([Values (true, false)] bool useD8)
}

[Test]
public void MultiDexCustomMainDexFileList ([Values (true, false)] bool useD8)
public void MultiDexCustomMainDexFileList ([Values ("dx", "d8")] string dexTool, [Values ("19", "21")] string minSdkVersion)
{
var expected = new [] {
"android/support/multidex/ZipUtil$CentralDirectory.class",
"android/support/multidex/MultiDexApplication.class",
"android/support/multidex/MultiDex$V19.class",
"android/support/multidex/MultiDex$V4.class",
"android/support/multidex/ZipUtil.class",
"android/support/multidex/MultiDexExtractor$1.class",
"android/support/multidex/MultiDexExtractor.class",
"android/support/multidex/MultiDex$V14.class",
"android/support/multidex/MultiDex.class",
"MyTest.class",
};
var proj = CreateMultiDexRequiredApplication ();
if (useD8) {
proj.DexTool = "d8";
}
proj.DexTool = dexTool;
proj.AndroidManifest = proj.AndroidManifest.Replace ("<uses-sdk />", $"<uses-sdk android:minSdkVersion=\"{minSdkVersion}\" />");
proj.SetProperty ("AndroidEnableMultiDex", "True");
proj.OtherBuildItems.Add (new BuildItem ("MultiDexMainDexList", "mymultidex.keep") { TextContent = () => "MyTest.class", Encoding = Encoding.ASCII });
proj.OtherBuildItems.Add (new BuildItem ("AndroidJavaSource", "MyTest.java") { TextContent = () => "public class MyTest {}", Encoding = Encoding.ASCII });
using (var b = CreateApkBuilder (Path.Combine ("temp", $"{nameof (MultiDexCustomMainDexFileList)}{useD8}"))) {
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
Assert.IsTrue (b.Build (proj), "build should succeed. Run will fail.");

string androidBinDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin");
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
FileAssert.Exists (Path.Combine (androidBinDir, "classes.dex"));
FileAssert.Exists (Path.Combine (androidBinDir, "classes2.dex"));
if (dexTool == "d8" && minSdkVersion == "21") {
//NOTE: d8/r8 does not support custom dex list files in this case
return;
}
//NOTE: d8 has the list in a different order, so we should do an unordered comparison
var actual = File.ReadAllLines (Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "multidex.keep"));
foreach (var item in expected) {
Expand Down

0 comments on commit 40712ea

Please sign in to comment.