Merge "Fix performance of unneeded catch handler removal."
diff --git a/build.gradle b/build.gradle
index 8237781..ff22c66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -348,7 +348,6 @@
def x20Dependencies = [
"third_party": [
- "benchmarks/santa-tracker",
"gmail/gmail_android_170604.16",
"gmscore/v4",
"gmscore/v5",
@@ -358,7 +357,6 @@
"gmscore/gmscore_v9",
"gmscore/gmscore_v10",
"gmscore/latest",
- "gradle-plugin",
"photos/2017-06-06",
"youtube/youtube.android_12.10",
"youtube/youtube.android_12.17",
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index d28f93f..8573e0f 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -4,7 +4,7 @@
package com.android.tools.r8;
-import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.DexIndexedConsumer.DirectoryConsumer;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.ApplicationWriter;
import com.android.tools.r8.graph.AppInfo;
@@ -19,6 +19,8 @@
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@@ -31,7 +33,7 @@
public static void run(
D8Command command,
FeatureClassMapping featureClassMapping,
- String outputArchive,
+ String output,
String proguardMap)
throws IOException, CompilationException, ExecutionException {
InternalOptions options = command.getInternalOptions();
@@ -65,8 +67,12 @@
AppInfo appInfo = new AppInfo(featureApp);
featureApp = D8.optimize(featureApp, appInfo, options, timing, executor);
// We create a specific consumer for each split.
- DexIndexedConsumer consumer =
- new ArchiveConsumer(Paths.get(outputArchive + "." + entry.getKey() + ".zip"));
+ Path outputDir = Paths.get(output).resolve(entry.getKey());
+ if (!Files.exists(outputDir)) {
+ Files.createDirectory(outputDir);
+ }
+ DexIndexedConsumer consumer = new DirectoryConsumer(outputDir);
+
try {
new ApplicationWriter(
featureApp,
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index e3753a7..5e8d54d 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.utils.OptionsParsing.ParseContext;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
@@ -23,23 +24,55 @@
public class DexSplitter {
- private static final String DEFAULT_OUTPUT_ARCHIVE_FILENAME = "split";
+ private static final String DEFAULT_OUTPUT_DIR = "output";
private static final boolean PRINT_ARGS = false;
+ public static class FeatureJar {
+ private String jar;
+ private String outputName;
+
+ public FeatureJar(String jar, String outputName) {
+ this.jar = jar;
+ this.outputName = outputName;
+ }
+
+ public FeatureJar(String jar) {
+ this(jar, featureNameFromJar(jar));
+ }
+
+ public String getJar() {
+ return jar;
+ }
+
+ public String getOutputName() {
+ return outputName;
+ }
+
+ private static String featureNameFromJar(String jar) {
+ Path jarPath = Paths.get(jar);
+ String featureName = jarPath.getFileName().toString();
+ if (featureName.endsWith(".jar") || featureName.endsWith(".zip")) {
+ featureName = featureName.substring(0, featureName.length() - 4);
+ }
+ return featureName;
+ }
+
+ }
+
public static class Options {
private List<String> inputArchives = new ArrayList<>();
- private List<String> featureJars = new ArrayList<>();
- private String splitBaseName = DEFAULT_OUTPUT_ARCHIVE_FILENAME;
+ private List<FeatureJar> featureJars = new ArrayList<>();
+ private String output = DEFAULT_OUTPUT_DIR;
private String featureSplitMapping;
private String proguardMap;
- public String getSplitBaseName() {
- return splitBaseName;
+ public String getOutput() {
+ return output;
}
- public void setSplitBaseName(String splitBaseName) {
- this.splitBaseName = splitBaseName;
+ public void setOutput(String output) {
+ this.output = output;
}
public String getFeatureSplitMapping() {
@@ -62,19 +95,45 @@
inputArchives.add(inputArchive);
}
- public void addFeatureJar(String featureJar) {
+ protected void addFeatureJar(FeatureJar featureJar) {
featureJars.add(featureJar);
}
+ public void addFeatureJar(String jar) {
+ featureJars.add(new FeatureJar(jar));
+ }
+
+ public void addFeatureJar(String jar, String outputName) {
+ featureJars.add(new FeatureJar(jar, outputName));
+ }
+
public ImmutableList<String> getInputArchives() {
return ImmutableList.copyOf(inputArchives);
}
- public ImmutableList<String> getFeatureJars() {
+ public ImmutableList<FeatureJar> getFeatureJars() {
return ImmutableList.copyOf(featureJars);
}
}
+ /**
+ * Parse a feature jar argument and return the corresponding FeatureJar representation.
+ * Default to use the name of the jar file if the argument contains no ':', if the argument
+ * contains ':', then use the value after the ':' as the name.
+ * @param argument
+ * @return
+ */
+ private static FeatureJar parseFeatureJarArgument(String argument) {
+ if (argument.contains(":")) {
+ String[] parts = argument.split(":");
+ if (parts.length > 2) {
+ throw new RuntimeException("--feature-jar argument contains more than one :");
+ }
+ return new FeatureJar(parts[0], parts[1]);
+ }
+ return new FeatureJar(argument);
+ }
+
private static Options parseArguments(String[] args) throws IOException {
Options options = new Options();
ParseContext context = new ParseContext(args);
@@ -86,12 +145,13 @@
}
List<String> featureJars = OptionsParsing.tryParseMulti(context, "--feature-jar");
if (featureJars != null) {
- featureJars.stream().forEach(options::addFeatureJar);
+ featureJars.stream().forEach(
+ (feature) -> options.addFeatureJar(parseFeatureJarArgument(feature)));
continue;
}
String output = OptionsParsing.tryParseSingle(context, "--output", "-o");
if (output != null) {
- options.setSplitBaseName(output);
+ options.setOutput(output);
continue;
}
String proguardMap = OptionsParsing.tryParseSingle(context, "--proguard-map", null);
@@ -114,8 +174,8 @@
if (options.getFeatureSplitMapping() != null) {
return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
}
- assert !options.featureJars.isEmpty();
- return FeatureClassMapping.fromJarFiles(options.featureJars);
+ assert !options.getFeatureJars().isEmpty();
+ return FeatureClassMapping.fromJarFiles(options.getFeatureJars());
}
private static void run(String[] args)
@@ -149,7 +209,7 @@
FeatureClassMapping featureClassMapping = createFeatureClassMapping(options);
DexSplitterHelper.run(
- builder.build(), featureClassMapping, options.getSplitBaseName(), options.getProguardMap());
+ builder.build(), featureClassMapping, options.getOutput(), options.getProguardMap());
}
public static void main(String[] args) {
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index 8c1a6c5..55ae757 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -199,14 +199,8 @@
if (context != null) {
// The SecondVistor is in charge of setting the context to null.
DexProgramClass owner = context.owner;
- int flags = ClassReader.SKIP_FRAMES;
- if (application.options.isGeneratingClassFiles()) {
- // TODO(mathiasr): Keep frames in JarCode until IR->CF construction is complete.
- // When we throw Unimplemented in IR->CF construction, the original JarCode is output
- // instead. In this case we must output frames as well, or we will fail verification.
- flags = 0;
- }
- new ClassReader(context.classCache).accept(new SecondVisitor(context, application), flags);
+ new ClassReader(context.classCache).accept(new SecondVisitor(context, application),
+ ClassReader.SKIP_FRAMES);
assert verifyNoReparseContext(owner);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index 7f89bdc..b5dfdbb 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -49,7 +49,6 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
-import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.IincInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
@@ -1174,9 +1173,6 @@
case AbstractInsnNode.LINE:
updateState((LineNumberNode) insn);
break;
- case AbstractInsnNode.FRAME:
- updateState((FrameNode) insn);
- break;
default:
throw new Unreachable("Unexpected instruction " + insn);
}
@@ -1798,11 +1794,6 @@
// Intentionally empty.
}
- private void updateState(FrameNode insn) {
- assert application.options.isGeneratingClassFiles();
- // Intentionally empty.
- }
-
private void updateStateForConversion(Type from, Type to) {
state.pop();
state.push(to);
@@ -1857,9 +1848,6 @@
case AbstractInsnNode.LINE:
build((LineNumberNode) insn, builder);
break;
- case AbstractInsnNode.FRAME:
- build((FrameNode) insn, builder);
- break;
default:
throw new Unreachable("Unexpected instruction " + insn);
}
@@ -2839,11 +2827,6 @@
builder.addDebugPosition(currentPosition);
}
- private void build(FrameNode insn, IRBuilder builder) {
- assert application.options.isGeneratingClassFiles();
- // Intentionally empty.
- }
-
@Override
public Position getDebugPositionAtOffset(int offset) {
if (offset == EXCEPTIONAL_SYNC_EXIT_OFFSET) {
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 5b96201..6893eb1 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils;
import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -58,20 +59,15 @@
return mapping;
}
- public static FeatureClassMapping fromJarFiles(List<String> jarFiles)
+ public static FeatureClassMapping fromJarFiles(List<FeatureJar> featureJars)
throws FeatureMappingException, IOException {
FeatureClassMapping mapping = new FeatureClassMapping();
- for (String jar : jarFiles) {
- Path jarPath = Paths.get(jar);
- String featureName = jarPath.getFileName().toString();
- if (featureName.endsWith(".jar") || featureName.endsWith(".zip")) {
- featureName = featureName.substring(0, featureName.length() - 4);
- }
-
+ for (FeatureJar featureJar : featureJars) {
+ Path jarPath = Paths.get(featureJar.getJar());
ArchiveClassFileProvider provider = new ArchiveClassFileProvider(jarPath);
for (String javaDescriptor : provider.getClassDescriptors()) {
String javaType = DescriptorUtils.descriptorToJavaType(javaDescriptor);
- mapping.addMapping(javaType, featureName);
+ mapping.addMapping(javaType, featureJar.getOutputName());
}
}
return mapping;
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 dc0cd0f..42775d2 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.StringConsumer;
import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.InvalidDebugInfoException;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -100,6 +101,8 @@
public boolean verbose = false;
// Silencing output.
public boolean quiet = false;
+ // Throw exception if there is a warning about invalid debug info.
+ public boolean invalidDebugInfoFatal = false;
// Hidden marker for classes.dex
private boolean hasMarker = false;
@@ -268,6 +271,9 @@
public void warningInvalidDebugInfo(
DexEncodedMethod method, Origin origin, InvalidDebugInfoException e) {
+ if (invalidDebugInfoFatal) {
+ throw new CompilationError("Fatal warning: Invalid debug info", e);
+ }
synchronized (warningInvalidDebugInfo) {
warningInvalidDebugInfo.computeIfAbsent(
origin, k -> new ArrayList<>()).add(new Pair<>(method, e.getMessage()));
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTest.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTest.java
new file mode 100644
index 0000000..6e009e8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTest.java
@@ -0,0 +1,27 @@
+// 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.cf;
+
+public class DebugInfoTest {
+
+ public static void main(String[] args) {
+ if (args.length > 0) {
+ arg = args.length % 2 == 0;
+ DebugInfoTest.method();
+ }
+ }
+
+ private static boolean arg;
+
+ private static void method() {
+ int intVar;
+ if (arg) {
+ float floatVar1 = 0f;
+ intVar = (int) floatVar1;
+ } else {
+ float floatVar2 = 0f;
+ intVar = (int) floatVar2;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
new file mode 100644
index 0000000..fed7637
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
@@ -0,0 +1,68 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.errors.InvalidDebugInfoException;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import java.nio.file.Path;
+import org.junit.Test;
+
+public class DebugInfoTestRunner extends TestBase {
+ static final Class CLASS = DebugInfoTest.class;
+
+ @Test
+ public void test() throws Exception {
+ ProcessResult runInput =
+ ToolHelper.runJava(ToolHelper.getClassPathForTests(), CLASS.getCanonicalName());
+ assertEquals(0, runInput.exitCode);
+ Path out1 = temp.getRoot().toPath().resolve("out1.zip");
+ build(
+ builder -> builder.addClassProgramData(ToolHelper.getClassAsBytes(CLASS), Origin.unknown()),
+ new ClassFileConsumer.ArchiveConsumer(out1));
+ ProcessResult run1 = ToolHelper.runJava(out1, CLASS.getCanonicalName());
+ assertEquals(runInput.toString(), run1.toString());
+ Path out2 = temp.getRoot().toPath().resolve("out2.zip");
+ boolean invalidDebugInfo = false;
+ try {
+ build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
+ } catch (CompilationError e) {
+ invalidDebugInfo = e.getCause() instanceof InvalidDebugInfoException;
+ }
+ // TODO(b/77522100): Change to assertFalse when fixed.
+ assertTrue(invalidDebugInfo);
+ if (!invalidDebugInfo) {
+ ProcessResult run2 = ToolHelper.runJava(out2, CLASS.getCanonicalName());
+ assertEquals(runInput.toString(), run2.toString());
+ }
+ }
+
+ private void build(ThrowingConsumer<Builder, Exception> input, ProgramConsumer consumer)
+ throws Exception {
+ Builder builder =
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(consumer);
+ input.accept(builder);
+ ToolHelper.runR8(
+ builder.build(),
+ o -> {
+ o.invalidDebugInfoFatal = true;
+ o.enableInlining = false;
+ });
+ }
+}
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 cf87d46..2db8fe0 100644
--- a/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
+++ b/src/test/java/com/android/tools/r8/dexsplitter/DexSplitterTests.java
@@ -84,14 +84,15 @@
.addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
.build());
- Path output = temp.getRoot().toPath().resolve("output");
+ Path output = temp.newFolder().toPath().resolve("output");
+ Files.createDirectory(output);
Path splitSpec = createSplitSpec();
if (useOptions) {
Options options = new Options();
options.addInputArchive(inputZip.toString());
options.setFeatureSplitMapping(splitSpec.toString());
- options.setSplitBaseName(output.toString());
+ options.setOutput(output.toString());
DexSplitter.run(options);
} else {
DexSplitter.main(
@@ -102,8 +103,8 @@
});
}
- Path base = output.getParent().resolve("output.base.zip");
- Path feature = output.getParent().resolve("output.feature1.zip");
+ Path base = output.resolve("base").resolve("classes.dex");
+ Path feature = output.resolve("feature1").resolve("classes.dex");
validateUnobfuscatedOutput(base, feature);
}
@@ -201,7 +202,8 @@
.addProgramFiles(Paths.get(CLASS4_LAMBDA_INTERFACE))
.build());
- Path output = temp.getRoot().toPath().resolve("output");
+ Path output = temp.newFolder().toPath().resolve("output");
+ Files.createDirectory(output);
Path baseJar = temp.getRoot().toPath().resolve("base.jar");
Path featureJar = temp.getRoot().toPath().resolve("feature1.jar");
ZipOutputStream baseStream = new ZipOutputStream(Files.newOutputStream(baseJar));
@@ -233,14 +235,16 @@
featureStream.write(Files.readAllBytes(Paths.get(CLASS4_LAMBDA_INTERFACE)));
featureStream.closeEntry();
featureStream.close();
+ // Make sure that we can pass in a name for the output.
+ String specificOutputName = "renamed";
if (useOptions) {
Options options = new Options();
options.addInputArchive(inputZip.toString());
- options.setSplitBaseName(output.toString());
+ options.setOutput(output.toString());
if (explicitBase) {
options.addFeatureJar(baseJar.toString());
}
- options.addFeatureJar(featureJar.toString());
+ options.addFeatureJar(featureJar.toString(), specificOutputName);
DexSplitter.run(options);
} else {
List<String> args = Lists.newArrayList(
@@ -249,7 +253,7 @@
"--output",
output.toString(),
"--feature-jar",
- featureJar.toString());
+ featureJar.toString().concat(":").concat(specificOutputName));
if (explicitBase) {
args.add("--feature-jar");
args.add(baseJar.toString());
@@ -257,8 +261,8 @@
DexSplitter.main(args.toArray(new String[0]));
}
- Path base = output.getParent().resolve("output.base.zip");
- Path feature = output.getParent().resolve("output.feature1.zip");
+ Path base = output.resolve("base").resolve("classes.dex");
+ Path feature = output.resolve(specificOutputName).resolve("classes.dex");;
validateUnobfuscatedOutput(base, feature);
}
@@ -284,7 +288,8 @@
.addProguardConfiguration(getProguardConf(), null)
.build());
- Path outputDex = temp.getRoot().toPath().resolve("output");
+ Path outputDex = temp.newFolder().toPath().resolve("output");
+ Files.createDirectory(outputDex);
Path splitSpec = createSplitSpec();
DexSplitter.main(
@@ -295,8 +300,8 @@
"--proguard-map", proguardMap.toString()
});
- Path base = outputDex.getParent().resolve("output.base.zip");
- Path feature = outputDex.getParent().resolve("output.feature1.zip");
+ Path base = outputDex.resolve("base").resolve("classes.dex");
+ Path feature = outputDex.resolve("feature1").resolve("classes.dex");
String class3 = "dexsplitsample.Class3";
// We should still be able to run the Class3 which we kept, it has a call to the obfuscated
// class1 which is in base.
diff --git a/third_party/benchmarks/android-sdk.tar.gz.sha1 b/third_party/benchmarks/android-sdk.tar.gz.sha1
new file mode 100644
index 0000000..efc177e
--- /dev/null
+++ b/third_party/benchmarks/android-sdk.tar.gz.sha1
@@ -0,0 +1 @@
+d033ea956b548b5fb87a2b37c584e87cfca33717
\ No newline at end of file
diff --git a/third_party/benchmarks/antenna-pod.tar.gz.sha1 b/third_party/benchmarks/antenna-pod.tar.gz.sha1
new file mode 100644
index 0000000..c3703e0
--- /dev/null
+++ b/third_party/benchmarks/antenna-pod.tar.gz.sha1
@@ -0,0 +1 @@
+f6f15c4efb79772ce8af147e0e2639db1c585fc7
\ No newline at end of file
diff --git a/third_party/benchmarks/gradle-java-1.6.tar.gz.sha1 b/third_party/benchmarks/gradle-java-1.6.tar.gz.sha1
new file mode 100644
index 0000000..e30dbfc
--- /dev/null
+++ b/third_party/benchmarks/gradle-java-1.6.tar.gz.sha1
@@ -0,0 +1 @@
+e87ccb7a5cb95555f46a6c8adbbcec99ec14b578
\ No newline at end of file
diff --git a/third_party/benchmarks/init-script.gradle b/third_party/benchmarks/init-script.gradle
new file mode 100644
index 0000000..3434a4a
--- /dev/null
+++ b/third_party/benchmarks/init-script.gradle
@@ -0,0 +1,74 @@
+def r8RootDir = System.properties['r8.root.dir']
+
+allprojects {
+ buildscript {
+ repositories {
+ maven { url r8RootDir+ '/third_party/gradle-plugin' }
+ // We don't use 'google()' in order to support projects using gradle
+ // lower to 4.1 version.
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ }
+ dependencies {
+ classpath files(r8RootDir + '/build/libs/r8.jar')
+ classpath 'com.android.tools.build:gradle:3.2.0-dev'
+ }
+ }
+ repositories {
+ maven { url r8RootDir+ '/third_party/gradle-plugin' }
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ mavenCentral()
+ }
+}
+
+//
+// Dump detailed timings per subtask
+//
+import java.util.concurrent.TimeUnit;
+class TimingsListener implements TaskExecutionListener, BuildListener {
+ private long startTimeInNs;
+ private timings = []
+
+ @Override
+ void beforeExecute(Task task) {
+ startTimeInNs = System.nanoTime();
+ }
+
+ @Override
+ void afterExecute(Task task, TaskState taskState) {
+ def ms = TimeUnit.MILLISECONDS.convert(
+ System.nanoTime() - startTimeInNs, TimeUnit.NANOSECONDS);
+ timings.add([task.path,ms])
+ }
+
+ @Override
+ void buildStarted(Gradle gradle) {
+ }
+
+ @Override
+ void buildFinished(BuildResult result) {
+ def total=0
+ for (timing in timings) {
+ total += timing[1]
+ }
+
+ for (timing in timings) {
+ printf "BENCH,%s,%s\n", timing
+ }
+
+ printf "BENCH,totalGradleTasks,%s\n", total
+
+ }
+
+ @Override
+ void projectsEvaluated(Gradle gradle) {}
+
+ @Override
+ void projectsLoaded(Gradle gradle) {}
+
+ @Override
+ void settingsEvaluated(Settings settings) {}
+}
+
+gradle.addListener new TimingsListener()
\ No newline at end of file
diff --git a/third_party/benchmarks/santa-tracker.tar.gz.sha1 b/third_party/benchmarks/santa-tracker.tar.gz.sha1
index d37a000..93b21bd 100644
--- a/third_party/benchmarks/santa-tracker.tar.gz.sha1
+++ b/third_party/benchmarks/santa-tracker.tar.gz.sha1
@@ -1 +1 @@
-88150a9a2215f1f821ea9321e61b5fc8276dffd3
\ No newline at end of file
+c0985a5e801ca74dad932dcd734b297e9fabe374
\ No newline at end of file
diff --git a/third_party/benchmarks/tachiyomi.tar.gz.sha1 b/third_party/benchmarks/tachiyomi.tar.gz.sha1
new file mode 100644
index 0000000..b3cf670
--- /dev/null
+++ b/third_party/benchmarks/tachiyomi.tar.gz.sha1
@@ -0,0 +1 @@
+9101f02e39bb938d44794d22b9813628f63f87af
\ No newline at end of file
diff --git a/third_party/benchmarks/wordpress.tar.gz.sha1 b/third_party/benchmarks/wordpress.tar.gz.sha1
new file mode 100644
index 0000000..dea2249
--- /dev/null
+++ b/third_party/benchmarks/wordpress.tar.gz.sha1
@@ -0,0 +1 @@
+f06cd747d2e2880cd450e67a4a21c28cd24a8dd5
\ No newline at end of file
diff --git a/tools/gradle.py b/tools/gradle.py
index 2303bce..eff949a 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -58,30 +58,42 @@
EnsureGradle()
EnsureShadow()
-def RunGradle(args, throw_on_failure=True):
+def RunGradleIn(gradleCmd, args, cwd, throw_on_failure=True):
EnsureDeps()
- cmd = [GRADLE]
+ cmd = [gradleCmd]
cmd.extend(args)
utils.PrintCmd(cmd)
- with utils.ChangedWorkingDirectory(utils.REPO_ROOT):
+ with utils.ChangedWorkingDirectory(cwd):
return_value = subprocess.call(cmd)
if throw_on_failure and return_value != 0:
raise Exception('Failed to execute gradle')
return return_value
+def RunGradleWrapperIn(args, cwd, throw_on_failure=True):
+ return RunGradleIn('./gradlew', args, cwd, throw_on_failure)
+
+def RunGradle(args, throw_on_failure=True):
+ return RunGradleIn(GRADLE, args, utils.REPO_ROOT, throw_on_failure)
+
def RunGradleExcludeDeps(args, throw_on_failure=True):
EnsureDeps()
args.append('-Pexclude_deps')
- RunGradle(args, throw_on_failure)
+ return RunGradle(args, throw_on_failure)
-def RunGradleGetOutput(args):
+def RunGradleInGetOutput(gradleCmd, args, cwd):
EnsureDeps()
- cmd = [GRADLE]
+ cmd = [gradleCmd]
cmd.extend(args)
utils.PrintCmd(cmd)
- with utils.ChangedWorkingDirectory(utils.REPO_ROOT):
+ with utils.ChangedWorkingDirectory(cwd):
return subprocess.check_output(cmd)
+def RunGradleWrapperInGetOutput(args, cwd):
+ return RunGradleInGetOutput('./gradlew', args, cwd)
+
+def RunGradleGetOutput(args):
+ return RunGradleInGetOutput(GRADLE, args, utils.REPO_ROOT)
+
def Main():
RunGradle(sys.argv[1:])
diff --git a/tools/test_gradle_benchmarks.py b/tools/test_gradle_benchmarks.py
new file mode 100755
index 0000000..e843753
--- /dev/null
+++ b/tools/test_gradle_benchmarks.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+# 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.
+
+
+from __future__ import print_function
+import argparse
+import os
+import sys
+import utils
+import gradle
+from enum import Enum
+
+
+BENCHMARKS_ROOT_DIR = os.path.join(utils.REPO_ROOT, 'third_party', 'benchmarks')
+
+def parse_arguments():
+ parser = argparse.ArgumentParser(
+ description='Run D8 or DX on gradle apps located in'
+ ' third_party/benchmarks/.'
+ ' Report Golem-compatible RunTimeRaw values.')
+ parser.add_argument('--tool',
+ choices=['dx', 'd8'],
+ required=True,
+ help='Compiler tool to use.')
+ return parser.parse_args()
+
+
+class Benchmark:
+ class Tools(Enum):
+ D8 = 1
+ DX = 2
+
+ class DesugarMode(Enum):
+ D8_DESUGARING = 1
+ DESUGAR_TOOL = 2
+
+ displayName = ""
+ rootDirPath = ""
+ appPath = ""
+ moduleName = ""
+ buildCommand = ""
+ cleanCommand = ""
+
+ def __init__(self, displayName, benchmarkDir, moduleName, buildCommand, cleanCommand):
+ self.displayName = displayName
+ self.rootDirPath = os.path.join(BENCHMARKS_ROOT_DIR, benchmarkDir.split(os.sep)[0])
+ self.appPath = os.path.join(BENCHMARKS_ROOT_DIR, benchmarkDir)
+ self.moduleName = moduleName
+ self.buildCommand = buildCommand
+ self.cleanCommand = cleanCommand
+
+ def RunGradle(self, command, tool, desugarMode):
+
+ args = ['-Dr8.root.dir=' + utils.REPO_ROOT, '--init-script',
+ os.path.join(BENCHMARKS_ROOT_DIR, 'init-script.gradle')]
+
+ if tool == self.Tools.D8:
+ args.append('-Dandroid.enableD8=true')
+ elif tool == self.Tools.DX:
+ args.append('-Dandroid.enableD8=false')
+ else:
+ raise AssertionError("Unknown tool: " + repr(tool))
+
+ if desugarMode == self.DesugarMode.D8_DESUGARING:
+ args.append('-Dandroid.enableDesugar=false')
+ elif desugarMode == self.DesugarMode.DESUGAR_TOOL:
+ args.append('-Dandroid.enableDesugar=true')
+ else:
+ raise AssertionError("Unknown desugar mode: " + repr(desugarMode))
+
+ args.extend(command)
+
+ return gradle.RunGradleWrapperInGetOutput(args, self.appPath)
+
+ def Build(self, tool, desugarMode):
+ return self.RunGradle(self.buildCommand, tool, desugarMode)
+
+ def Clean(self):
+ # tools and desugar mode not relevant for clean
+ return self.RunGradle(self.cleanCommand, self.Tools.D8, self.DesugarMode.D8_DESUGARING)
+
+ def EnsurePresence(self):
+ EnsurePresence(self.rootDirPath, self.displayName)
+
+
+def EnsurePresence(dir, displayName):
+ if not os.path.exists(dir) or os.path.getmtime(dir + '.tar.gz')\
+ < os.path.getmtime(dir + '.tar.gz.sha1'):
+ utils.DownloadFromX20(dir + '.tar.gz.sha1')
+ # Update the mtime of the tar file to make sure we do not run again unless
+ # there is an update.
+ os.utime(dir + '.tar.gz', None)
+ else:
+ print('test_gradle_benchmarks.py: benchmark {} is present'.format(displayName))
+
+def TaskFilter(taskname):
+ acceptedGradleTasks = [
+ 'dex',
+ 'Dex',
+ 'proguard',
+ 'Proguard',
+ 'kotlin',
+ 'Kotlin',
+ ]
+
+ return any(namePattern in taskname for namePattern in acceptedGradleTasks)
+
+
+def PrintBuildTimeForGolem(benchmark, stdOut):
+ for line in stdOut.splitlines():
+ if 'BENCH' in line and benchmark.moduleName in line:
+ commaSplit = line.split(',')
+ assert len(commaSplit) == 3
+
+ # Keep only module that have been configured to use R8
+ if benchmark.moduleName + ':' not in commaSplit[1]:
+ continue
+
+ # remove <module-name> + ':'
+ taskName = commaSplit[1][(len(benchmark.moduleName) + 1):]
+
+ # Just a temporary assumption.
+ # This means we have submodules, so we'll need to check their configuration
+ # so that the right r8/d8 is taken. For now it shouldn't be the case.
+ assert taskName.find(':') == -1, taskName
+
+ # Output example:
+ # SantaTracker-transformClassesWithDexBuilderForDevelopmentDebug(RunTimeRaw): 748 ms
+
+ golemBenchmarkValue = benchmark.displayName + '-' + taskName + '(RunTimeRaw): '
+ if TaskFilter(taskName):
+ print('{}(RunTimeRaw): {} ms'
+ .format(benchmark.displayName + '-' + taskName, commaSplit[2]))
+
+
+def Main():
+ args = parse_arguments()
+
+ if args.tool == 'd8':
+ tool = Benchmark.Tools.D8
+ desugarMode = Benchmark.DesugarMode.D8_DESUGARING
+ else:
+ tool = Benchmark.Tools.DX
+ desugarMode = Benchmark.DesugarMode.DESUGAR_TOOL
+
+ buildTimeBenchmarks = [
+ Benchmark('AntennaPod',
+ os.path.join('antenna-pod', 'AntennaPod'),
+ ':app',
+ [':app:assembleDebug'],
+ ['clean']),
+ Benchmark('Maps',
+ 'gradle-java-1.6',
+ ':maps',
+ [':maps:assembleDebug', '--settings-file', 'settings.gradle.maps'],
+ ['clean']),
+ Benchmark('Music2',
+ 'gradle-java-1.6',
+ ':music2Old',
+ [':music2Old:assembleDebug', '--settings-file', 'settings.gradle.music2Old'],
+ ['clean']),
+ Benchmark('Velvet',
+ 'gradle-java-1.6',
+ ':velvet',
+ [':velvet:assembleDebug', '--settings-file', 'settings.gradle.velvet'],
+ ['clean']),
+ Benchmark('SantaTracker',
+ 'santa-tracker',
+ ':santa-tracker',
+ [':santa-tracker:assembleDebug'],
+ ['clean']),
+
+ # disabled for now, apparently because of b/74227571
+ # Benchmark('Tachiyomi',
+ # 'tachiyomi',
+ # ':app',
+ # ['assembleStandardDebug'],
+ # ['clean']),
+
+ Benchmark('WordPress',
+ 'wordpress',
+ ':WordPress',
+ ['assembleVanillaDebug'],
+ ['clean']),
+
+ ]
+
+ EnsurePresence(os.path.join('third_party', 'benchmarks', 'android-sdk'), 'android SDK')
+ EnsurePresence(os.path.join('third_party', 'gradle-plugin'), 'Android Gradle plugin')
+
+ for benchmark in buildTimeBenchmarks:
+ benchmark.EnsurePresence()
+ benchmark.Clean()
+ stdOut = benchmark.Build(tool, desugarMode)
+ PrintBuildTimeForGolem(benchmark, stdOut)
+
+
+if __name__ == '__main__':
+ sys.exit(Main())
diff --git a/tools/utils.py b/tools/utils.py
index 8716f65..a138461 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -48,6 +48,12 @@
def IsWindows():
return os.name == 'nt'
+def DownloadFromX20(sha1_file):
+ download_script = os.path.join(REPO_ROOT, 'tools', 'download_from_x20.py')
+ cmd = [download_script, sha1_file]
+ PrintCmd(cmd)
+ subprocess.check_call(cmd)
+
def DownloadFromGoogleCloudStorage(sha1_file, bucket='r8-deps'):
suffix = '.bat' if IsWindows() else ''
download_script = 'download_from_google_storage%s' % suffix