Version 1.0.9
Merge: c25649a8b187dbcabfa8973be5f96fdc01008c1c
CL: Remove warnings from main dex calculation
Merge: 5429584751948942d30dd352388e387370df439e
CL: Mark test as requiring Art VM
Bug: 72312389
Change-Id: I76f1f63830fc0247bd8d684e22a249e2f6a81bd7
diff --git a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
index 5755c04..9fbca07 100644
--- a/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
+++ b/src/main/java/com/android/tools/r8/CompatProguardCommandBuilder.java
@@ -37,6 +37,13 @@
this(true);
}
+ public CompatProguardCommandBuilder(
+ boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
+ super(forceProguardCompatibility, diagnosticsHandler);
+ setIgnoreDexInArchive(true);
+ addProguardConfiguration(REFLECTIONS, EmbeddedOrigin.INSTANCE);
+ }
+
public CompatProguardCommandBuilder(boolean forceProguardCompatibility) {
super(forceProguardCompatibility);
setIgnoreDexInArchive(true);
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
index b5e1a49..5e41085 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexListCommand.java
@@ -27,6 +27,7 @@
private final ImmutableList<ProguardConfigurationRule> mainDexKeepRules;
private final StringConsumer mainDexListConsumer;
private final DexItemFactory factory;
+ private final Reporter reporter;
/** Get the output path for the main-dex list. Null if not set. */
@Deprecated
@@ -42,6 +43,14 @@
private final List<ProguardConfigurationSource> mainDexRules = new ArrayList<>();
private Path mainDexListOutput = null;
+ private Builder() {
+ }
+
+ private Builder(DiagnosticsHandler diagnosticsHandler) {
+ super(diagnosticsHandler);
+ }
+
+
@Override
GenerateMainDexListCommand.Builder self() {
return this;
@@ -119,7 +128,7 @@
mainDexListOutput != null ? new StringConsumer.FileConsumer(mainDexListOutput) : null;
return new GenerateMainDexListCommand(
- factory, getAppBuilder().build(), mainDexKeepRules, mainDexListConsumer);
+ factory, getAppBuilder().build(), mainDexKeepRules, mainDexListConsumer, getReporter());
}
}
@@ -139,6 +148,10 @@
return new GenerateMainDexListCommand.Builder();
}
+ public static GenerateMainDexListCommand.Builder builder(DiagnosticsHandler diagnosticsHandler) {
+ return new GenerateMainDexListCommand.Builder(diagnosticsHandler);
+ }
+
public static GenerateMainDexListCommand.Builder parse(String[] args) {
GenerateMainDexListCommand.Builder builder = builder();
parse(args, builder);
@@ -174,11 +187,13 @@
DexItemFactory factory,
AndroidApp inputApp,
ImmutableList<ProguardConfigurationRule> mainDexKeepRules,
- StringConsumer mainDexListConsumer) {
+ StringConsumer mainDexListConsumer,
+ Reporter reporter) {
super(inputApp);
this.factory = factory;
this.mainDexKeepRules = mainDexKeepRules;
this.mainDexListConsumer = mainDexListConsumer;
+ this.reporter = reporter;
}
private GenerateMainDexListCommand(boolean printHelp, boolean printVersion) {
@@ -186,18 +201,17 @@
this.factory = new DexItemFactory();
this.mainDexKeepRules = ImmutableList.of();
this.mainDexListConsumer = null;
+ this.reporter = new Reporter(new DefaultDiagnosticsHandler());
}
@Override
InternalOptions getInternalOptions() {
- InternalOptions internal =
- new InternalOptions(factory, new Reporter(new DefaultDiagnosticsHandler()));
+ InternalOptions internal = new InternalOptions(factory, reporter);
internal.mainDexKeepRules = mainDexKeepRules;
internal.mainDexListConsumer = mainDexListConsumer;
internal.minimalMainDex = internal.debug;
internal.removeSwitchMaps = false;
internal.inlineAccessors = false;
- internal.allowLibraryClassesToExtendProgramClasses = true;
return internal;
}
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 71c82a8..e6e8019 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -75,6 +75,12 @@
this.forceProguardCompatibility = forceProguardCompatibility;
}
+ Builder(boolean forceProguardCompatibility, DiagnosticsHandler diagnosticsHandler) {
+ super(diagnosticsHandler);
+ setMode(CompilationMode.RELEASE);
+ this.forceProguardCompatibility = forceProguardCompatibility;
+ }
+
private Builder(DiagnosticsHandler diagnosticsHandler) {
super(diagnosticsHandler);
setMode(CompilationMode.DEBUG);
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index eba3f9e..94585ce 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
- public static final String LABEL = "v1.0.8";
+ public static final String LABEL = "v1.0.9";
private Version() {
}
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index fe2f063..0bd3068 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -78,6 +78,7 @@
* field descriptions for details.
*/
public class Enqueuer {
+ private boolean tracingMainDex = false;
private final AppInfoWithSubtyping appInfo;
private final InternalOptions options;
@@ -499,13 +500,19 @@
}
private void ensureFromLibraryOrThrow(DexType type, DexType context) {
+ if (tracingMainDex) {
+ // b/72312389: android.jar contains parts of JUnit and most developers include JUnit in
+ // their programs. This leads to library classes extending program classes. When tracing
+ // main dex lists we allow this.
+ return;
+ }
+
DexClass holder = appInfo.definitionFor(type);
if (holder != null && !holder.isLibraryClass()) {
Diagnostic message = new StringDiagnostic("Library class " + context.toSourceString()
+ (holder.isInterface() ? " implements " : " extends ")
+ "program class " + type.toSourceString());
- if (options.forceProguardCompatibility
- || options.allowLibraryClassesToExtendProgramClasses) {
+ if (options.forceProguardCompatibility) {
options.reporter.warning(message);
} else {
options.reporter.error(message);
@@ -944,6 +951,7 @@
}
public Set<DexType> traceMainDex(RootSet rootSet, Timing timing) {
+ this.tracingMainDex = true;
this.rootSet = rootSet;
// Translate the result of root-set computation into enqueuer actions.
enqueueRootItems(rootSet.noShrinking);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index fa32b89..7bb23d4 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -169,10 +169,6 @@
public boolean disableAssertions = true;
public boolean debugKeepRules = false;
- // TODO(72312389): android.jar contains parts of JUnit and most developers include JUnit in
- // their programs, which can lead to library classes extending program classes.
- public boolean allowLibraryClassesToExtendProgramClasses = false;
-
public boolean debug = false;
public final TestingOptions testing = new TestingOptions();
diff --git a/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java b/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java
new file mode 100644
index 0000000..5541ccd
--- /dev/null
+++ b/src/test/examplesAndroidApi/instrumentationtest/InstrumentationTest.java
@@ -0,0 +1,21 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package instrumentationtest;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+
+public class InstrumentationTest extends InstrumentationTestCase {
+ Context context;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ context = getInstrumentation().getContext();
+ assertNotNull(context);
+ }
+
+ public void testSomething() {
+ assertEquals(false, true);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index fffdc48..6cc9bb9 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -513,6 +513,13 @@
}
}
+ /**
+ * Get the junit jar bundled with the framework.
+ */
+ public static Path getFrameworkJunitJarPath(DexVm version) {
+ return Paths.get(getArtDir(version), "framework", "junit.jar");
+ }
+
static class RetainedTemporaryFolder extends TemporaryFolder {
RetainedTemporaryFolder(java.io.File parentFolder) {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
new file mode 100644
index 0000000..1d2a942
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/maindexlist/b72312389/B72312389.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.maindexlist.b72312389;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.BaseCommand;
+import com.android.tools.r8.CompatProguardCommandBuilder;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.GenerateMainDexList;
+import com.android.tools.r8.GenerateMainDexListCommand;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.VmTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.DexInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(VmTestRunner.class)
+public class B72312389 extends TestBase {
+ // Build a app with a class extending InstrumentationTestCase and including both the junit
+ // and the Android library.
+ private void buildInstrumentationTestCaseApplication(BaseCommand.Builder builder) {
+ builder
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O.getLevel()))
+ .addProgramFiles(
+ Paths.get("build", "test", "examplesAndroidApi",
+ "classes", "instrumentationtest", "InstrumentationTest.class"))
+ .addProgramFiles(ToolHelper.getFrameworkJunitJarPath(DexVm.ART_7_0_0_HOST));
+ }
+
+ private List<String> keepInstrumentationTestCaseRules = ImmutableList.of(
+ "-keep class instrumentationtest.InstrumentationTest {",
+ " *;",
+ "}");
+
+ @Test
+ public void testGenerateMainDexList() throws Exception {
+ CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+ GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.builder(diagnostics);
+ buildInstrumentationTestCaseApplication(builder);
+ GenerateMainDexListCommand command = builder
+ .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+ .build();
+ List<String> mainDexList = GenerateMainDexList.run(command);
+ assertTrue(mainDexList.contains("junit/framework/TestCase.class"));
+ diagnostics.assertEmpty();
+ }
+
+ private static class StringBox {
+ String content;
+ }
+
+ @Test
+ public void testR8ForceProguardCompatibility() throws Exception {
+ StringBox mainDexList = new StringBox();
+ // Build a app with a class extending InstrumentationTestCase and including both the junit
+ // and the Android library.
+ CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+ R8Command.Builder builder = new CompatProguardCommandBuilder(true, diagnostics);
+ buildInstrumentationTestCaseApplication(builder);
+ R8Command command = builder
+ .setMinApiLevel(AndroidApiLevel.K.getLevel())
+ // TODO(72793900): This should not be required.
+ .addProguardConfiguration(ImmutableList.of("-keep class ** { *; }"), Origin.unknown())
+ .addProguardConfiguration(ImmutableList.of("-dontobfuscate"), Origin.unknown())
+ .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .setMainDexListConsumer(
+ (string, handler) -> mainDexList.content = string)
+ .build();
+ DexInspector inspector = new DexInspector(ToolHelper.runR8(command));
+ assertTrue(inspector.clazz("instrumentationtest.InstrumentationTest").isPresent());
+ assertTrue(mainDexList.content.contains("junit/framework/TestCase.class"));
+ // TODO(72794301): Two copies of this message is a bit over the top.
+ assertEquals(2,
+ diagnostics.countLibraryClassExtensdProgramClassWarnings(
+ "android.test.InstrumentationTestCase", "junit.framework.TestCase"));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ CollectingDiagnosticHandler diagnostics = new CollectingDiagnosticHandler();
+ R8Command.Builder builder = R8Command.builder(diagnostics);
+ buildInstrumentationTestCaseApplication(builder);
+ R8Command command = builder
+ .addMainDexRules(keepInstrumentationTestCaseRules, Origin.unknown())
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build();
+ try {
+ R8.run(command);
+ fail();
+ } catch (CompilationFailedException e) {
+ // Expected, as library class extending program class is an error for R8.
+ }
+ }
+
+ private static class CollectingDiagnosticHandler implements DiagnosticsHandler {
+ private final List<Diagnostic> infos = new ArrayList<>();
+ private final List<Diagnostic> warnings = new ArrayList<>();
+ private final List<Diagnostic> errors = new ArrayList<>();
+
+ @Override
+ public void info(Diagnostic info) {
+ infos.add(info);
+ }
+
+ @Override
+ public void warning(Diagnostic warning) {
+ warnings.add(warning);
+ }
+
+ @Override
+ public void error(Diagnostic error) {
+ errors.add(error);
+ }
+
+ public void assertEmpty() {
+ assertEquals(0, errors.size());
+ assertEquals(0, warnings.size());
+ assertEquals(0, infos.size());
+ }
+
+ private boolean isLibraryClassExtensdProgramClassWarnings(
+ String libraryClass, String programClass, Diagnostic diagnostic) {
+ return diagnostic.getDiagnosticMessage().equals(
+ "Library class "+ libraryClass + " extends program class " + programClass);
+ }
+
+ public long countLibraryClassExtensdProgramClassWarnings(
+ String libraryClass, String programClass) {
+ return warnings.stream()
+ .filter(diagnostics ->
+ isLibraryClassExtensdProgramClassWarnings(libraryClass, programClass, diagnostics))
+ .count();
+ }
+ }
+}