Rewrite the main dex classes using the graph lens
Bug: 167939768
Change-Id: I5cbb7fe3c3d899e1745d464be6284ad9e4694bb0
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 9af31ab..6c6bab5 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -990,8 +990,7 @@
DexDefinitionSupplier definitionSupplier = application.getDefinitionsSupplier(committedItems);
return new AppInfoWithLiveness(
application,
- // TODO(b/167939768): Rewrite the main dex classes.
- getMainDexClasses(),
+ getMainDexClasses().rewrittenWithLens(lens),
committedItems,
deadProtoTypes,
missingTypes,
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
index 991b7cf..cfadae32 100644
--- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
+++ b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GraphLens;
import com.google.common.collect.Sets;
import java.util.Set;
import java.util.function.Consumer;
@@ -69,6 +70,15 @@
return mainDexClasses.isEmpty();
}
+ public MainDexClasses rewrittenWithLens(GraphLens lens) {
+ MainDexClasses rewrittenMainDexClasses = createEmptyMainDexClasses();
+ for (DexType mainDexClass : mainDexClasses) {
+ DexType rewrittenMainDexClass = lens.lookupType(mainDexClass);
+ rewrittenMainDexClasses.mainDexClasses.add(rewrittenMainDexClass);
+ }
+ return rewrittenMainDexClasses;
+ }
+
public int size() {
return mainDexClasses.size();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index 1b51a73..6b5604e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -51,6 +51,9 @@
private final Reporter reporter;
private final boolean allowTestOptions;
+ public static final String FLATTEN_PACKAGE_HIERARCHY = "flattenpackagehierarchy";
+ public static final String REPACKAGE_CLASSES = "repackageclasses";
+
private static final List<String> IGNORED_SINGLE_ARG_OPTIONS = ImmutableList.of(
"protomapping",
"target",
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 0d09fcd..342920c 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1603,6 +1603,10 @@
return AndroidApiLevel.O;
}
+ public static AndroidApiLevel apiLevelWithNativeMultiDexSupport() {
+ return AndroidApiLevel.L;
+ }
+
public Path compileToZip(
TestParameters parameters, Collection<Class<?>> classPath, Class<?>... compilationUnit)
throws Exception {
diff --git a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
index f503359..0ef3bdf 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -369,7 +369,7 @@
// Ensure that the Class1 is actually in the correct split. Note that Class2 would have been
// shaken away.
- CodeInspector inspector = new CodeInspector(base, proguardMap.toString());
+ CodeInspector inspector = new CodeInspector(base, proguardMap);
ClassSubject subject = inspector.clazz("dexsplitsample.Class1");
assertTrue(subject.isPresent());
assertTrue(subject.isRenamed());
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
index 97b4db0..bbddb47 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1419TreeShakeJarVerificationTest.java
@@ -69,8 +69,7 @@
new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
ProtoApplicationStats baseline =
new ProtoApplicationStats(
- dexItemFactory,
- new CodeInspector(getReleaseApk(), getReleaseProguardMap().toString()));
+ dexItemFactory, new CodeInspector(getReleaseApk(), getReleaseProguardMap()));
System.out.println(actual.getStats(baseline));
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
index b2498b5..0a304b1 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1444TreeShakeJarVerificationTest.java
@@ -78,8 +78,7 @@
new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
ProtoApplicationStats baseline =
new ProtoApplicationStats(
- dexItemFactory,
- new CodeInspector(getReleaseApk(), getReleaseProguardMap().toString()));
+ dexItemFactory, new CodeInspector(getReleaseApk(), getReleaseProguardMap()));
System.out.println(actual.getStats(baseline));
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
index b48bec3..79e7eb1 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1508TreeShakeJarVerificationTest.java
@@ -78,8 +78,7 @@
new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
ProtoApplicationStats baseline =
new ProtoApplicationStats(
- dexItemFactory,
- new CodeInspector(getReleaseApk(), getReleaseProguardMap().toString()));
+ dexItemFactory, new CodeInspector(getReleaseApk(), getReleaseProguardMap()));
System.out.println(actual.getStats(baseline));
}
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
index 632210e..09293ac 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeV1533TreeShakeJarVerificationTest.java
@@ -78,8 +78,7 @@
new ProtoApplicationStats(dexItemFactory, compileResult.inspector(), original);
ProtoApplicationStats baseline =
new ProtoApplicationStats(
- dexItemFactory,
- new CodeInspector(getReleaseApk(), getReleaseProguardMap().toString()));
+ dexItemFactory, new CodeInspector(getReleaseApk(), getReleaseProguardMap()));
System.out.println(actual.getStats(baseline));
}
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 08a4ae9..0d96a65 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -162,10 +162,10 @@
annotationElement[0].value.asDexValueString().value.toSourceString());
}
- private String getGeneratedProguardMap() throws IOException {
+ private Path getGeneratedProguardMap() throws IOException {
Path mapFile = Paths.get(tmpOutputDir.getRoot().getCanonicalPath(), DEFAULT_MAP_FILENAME);
if (Files.exists(mapFile)) {
- return mapFile.toAbsolutePath().toString();
+ return mapFile.toAbsolutePath();
}
return null;
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java
new file mode 100644
index 0000000..00dd079
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithMainDexListTest.java
@@ -0,0 +1,104 @@
+// Copyright (c) 2020, 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.repackage;
+
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.FLATTEN_PACKAGE_HIERARCHY;
+import static com.android.tools.r8.shaking.ProguardConfigurationParser.REPACKAGE_CLASSES;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RepackageWithMainDexListTest extends TestBase {
+
+ private static final String REPACKAGE_DIR = "foo";
+
+ private final String flattenPackageHierarchyOrRepackageClasses;
+ private final TestParameters parameters;
+
+ @Parameters(name = "{1}, kind: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ ImmutableList.of(FLATTEN_PACKAGE_HIERARCHY, REPACKAGE_CLASSES),
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevelsEndingAtExcluding(apiLevelWithNativeMultiDexSupport())
+ .build());
+ }
+
+ public RepackageWithMainDexListTest(
+ String flattenPackageHierarchyOrRepackageClasses, TestParameters parameters) {
+ this.flattenPackageHierarchyOrRepackageClasses = flattenPackageHierarchyOrRepackageClasses;
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ // -keep,allowobfuscation does not prohibit repackaging.
+ .addKeepClassRulesWithAllowObfuscation(TestClass.class, OtherTestClass.class)
+ .addKeepRules(
+ "-keepclassmembers class " + TestClass.class.getTypeName() + " { <methods>; }")
+ .addKeepRules(
+ "-" + flattenPackageHierarchyOrRepackageClasses + " \"" + REPACKAGE_DIR + "\"")
+ // Add a class that will be repackaged to the main dex list.
+ .addMainDexListClasses(TestClass.class)
+ .addOptionsModification(
+ options -> {
+ assertFalse(options.testing.enableExperimentalRepackaging);
+ options.testing.enableExperimentalRepackaging = true;
+ })
+ // Debug mode to enable minimal main dex.
+ .debug()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .apply(this::checkCompileResult)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("Hello world!");
+ }
+
+ private void checkCompileResult(R8TestCompileResult compileResult) throws Exception {
+ Path out = temp.newFolder().toPath();
+ compileResult.app.writeToDirectory(out, OutputMode.DexIndexed);
+ Path classes = out.resolve("classes.dex");
+ Path classes2 = out.resolve("classes2.dex");
+ inspectMainDex(new CodeInspector(classes, compileResult.getProguardMap()));
+ inspectSecondaryDex(new CodeInspector(classes2, compileResult.getProguardMap()));
+ }
+
+ private void inspectMainDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+ assertThat(inspector.clazz(OtherTestClass.class), not(isPresent()));
+ }
+
+ private void inspectSecondaryDex(CodeInspector inspector) {
+ assertThat(inspector.clazz(TestClass.class), not(isPresent()));
+ assertThat(inspector.clazz(OtherTestClass.class), isPresent());
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ System.out.println("Hello world!");
+ }
+ }
+
+ static class OtherTestClass {}
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 8365345..6f7c644 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -220,12 +220,7 @@
Assert.assertEquals(resultInput.toString(), resultOutput.toString());
}
if (inspection != null) {
- CodeInspector inspector =
- new CodeInspector(
- out,
- minify.isMinify()
- ? proguardMap.toString()
- : null);
+ CodeInspector inspector = new CodeInspector(out, minify.isMinify() ? proguardMap : null);
inspection.accept(inspector);
}
return;
@@ -252,9 +247,7 @@
if (dexComparator != null) {
CodeInspector ref = new CodeInspector(Paths.get(originalDex));
- CodeInspector inspector = new CodeInspector(out,
- minify.isMinify() ? proguardMap.toString()
- : null);
+ CodeInspector inspector = new CodeInspector(out, minify.isMinify() ? proguardMap : null);
dexComparator.accept(ref, inspector);
}
} else {
@@ -265,8 +258,7 @@
}
if (inspection != null) {
- CodeInspector inspector =
- new CodeInspector(out, minify.isMinify() ? proguardMap.toString() : null);
+ CodeInspector inspector = new CodeInspector(out, minify.isMinify() ? proguardMap : null);
inspection.accept(inspector);
}
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index 6d48d92..8672caa 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -77,8 +77,12 @@
this(Paths.get(path));
}
- public CodeInspector(Path file, String mappingFile) throws IOException {
- this(Collections.singletonList(file), mappingFile, null);
+ public CodeInspector(Path file, String proguardMapContent) throws IOException {
+ this(AndroidApp.builder().addProgramFile(file).build(), proguardMapContent);
+ }
+
+ public CodeInspector(Path file, Path mappingFile) throws IOException {
+ this(Collections.singletonList(file), mappingFile.toString(), null);
}
public CodeInspector(Path file) throws IOException {