Merge "Add easier control of whether data resources are processed or not by R8"
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 8828248..a537d7f 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -172,7 +172,7 @@
try {
app.addProgramFile(path);
programFiles.add(path);
- } catch (IOException | CompilationError e) {
+ } catch (CompilationError e) {
error(new ProgramInputOrigin(path), e);
}
});
diff --git a/src/main/java/com/android/tools/r8/CompatDxHelper.java b/src/main/java/com/android/tools/r8/CompatDxHelper.java
index f540ed0..a84bab5 100644
--- a/src/main/java/com/android/tools/r8/CompatDxHelper.java
+++ b/src/main/java/com/android/tools/r8/CompatDxHelper.java
@@ -6,10 +6,10 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
-import java.io.IOException;
public class CompatDxHelper {
- public static void run(D8Command command, Boolean minimalMainDex) throws IOException {
+ public static void run(D8Command command, Boolean minimalMainDex)
+ throws CompilationFailedException {
AndroidApp app = command.getInputApp();
InternalOptions options = command.getInternalOptions();
// DX does not desugar.
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 76e7277..68d9095 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -110,7 +110,7 @@
}
InternalOptions options = command.getInternalOptions();
AndroidApp app = command.getInputApp();
- ExceptionUtils.withD8CompilationHandler(options.reporter, () -> runForTesting(app, options));
+ runForTesting(app, options);
}
/**
@@ -126,13 +126,18 @@
ExceptionUtils.withMainProgramHandler(() -> run(args));
}
- static void runForTesting(AndroidApp inputApp, InternalOptions options) throws IOException {
+ static void runForTesting(AndroidApp inputApp, InternalOptions options)
+ throws CompilationFailedException {
ExecutorService executor = ThreadUtils.getExecutorService(options);
- try {
- run(inputApp, options, executor);
- } finally {
- executor.shutdown();
- }
+ ExceptionUtils.withD8CompilationHandler(
+ options.reporter,
+ () -> {
+ try {
+ run(inputApp, options, executor);
+ } finally {
+ executor.shutdown();
+ }
+ });
}
// Compute the marker to be placed in the main dex file.
diff --git a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
index d7183c8..b6bb4e0 100644
--- a/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
+++ b/src/main/java/com/android/tools/r8/DexFileMergerHelper.java
@@ -115,7 +115,7 @@
}
public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
- throws IOException {
+ throws CompilationFailedException {
InternalOptions options = command.getInternalOptions();
options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/DexRoundTrip.java b/src/main/java/com/android/tools/r8/DexRoundTrip.java
index b6bebbb..fcf308b 100644
--- a/src/main/java/com/android/tools/r8/DexRoundTrip.java
+++ b/src/main/java/com/android/tools/r8/DexRoundTrip.java
@@ -5,10 +5,8 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
-import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -20,11 +18,10 @@
InternalOptions options = command.getInternalOptions();
AndroidApp app = command.getInputApp();
options.passthroughDexCode = false;
- ExceptionUtils.withD8CompilationHandler(options.reporter, () -> D8.runForTesting(app, options));
+ D8.runForTesting(app, options);
}
- public static void main(String[] args)
- throws CompilationFailedException, IOException, ResourceException {
+ public static void main(String[] args) throws CompilationFailedException {
D8Command.Builder builder = D8Command.builder();
for (String arg : args) {
Path file = Paths.get(arg);
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index 163c46f..51424d5 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -136,7 +136,7 @@
}
public static void runD8ForTesting(D8Command command, boolean dontCreateMarkerInD8)
- throws IOException {
+ throws CompilationFailedException {
InternalOptions options = command.getInternalOptions();
options.testing.dontCreateMarkerInD8 = dontCreateMarkerInD8;
D8.runForTesting(command.getInputApp(), options);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6de196e..5d3f795 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -126,16 +126,7 @@
public static void run(R8Command command) throws CompilationFailedException {
AndroidApp app = command.getInputApp();
InternalOptions options = command.getInternalOptions();
- ExecutorService executor = ThreadUtils.getExecutorService(options);
- ExceptionUtils.withR8CompilationHandler(
- command.getReporter(),
- () -> {
- try {
- run(app, options, executor);
- } finally {
- executor.shutdown();
- }
- });
+ runForTesting(app, options);
}
/**
@@ -218,13 +209,18 @@
return result;
}
- static void runForTesting(AndroidApp app, InternalOptions options) throws IOException {
+ static void runForTesting(AndroidApp app, InternalOptions options)
+ throws CompilationFailedException {
ExecutorService executor = ThreadUtils.getExecutorService(options);
- try {
- run(app, options, executor);
- } finally {
- executor.shutdown();
- }
+ ExceptionUtils.withR8CompilationHandler(
+ options.reporter,
+ () -> {
+ try {
+ run(app, options, executor);
+ } finally {
+ executor.shutdown();
+ }
+ });
}
private static void run(AndroidApp app, InternalOptions options, ExecutorService executor)
diff --git a/src/main/java/com/android/tools/r8/naming/NamingState.java b/src/main/java/com/android/tools/r8/naming/NamingState.java
index fc00d91..6b64017 100644
--- a/src/main/java/com/android/tools/r8/naming/NamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/NamingState.java
@@ -22,7 +22,7 @@
class NamingState<ProtoType extends CachedHashValueDexItem, KeyType> {
private final NamingState<ProtoType, KeyType> parent;
- private final Map<KeyType, InternalState<ProtoType>> usedNames = new HashMap<>();
+ private final Map<KeyType, InternalState> usedNames = new HashMap<>();
private final DexItemFactory itemFactory;
private final ImmutableList<String> dictionary;
private final Function<ProtoType, KeyType> keyTransform;
@@ -53,25 +53,25 @@
return new NamingState<>(this, itemFactory, dictionary, keyTransform, useUniqueMemberNames);
}
- private InternalState<ProtoType> findInternalStateFor(ProtoType proto) {
+ private InternalState findInternalStateFor(ProtoType proto) {
KeyType key = keyTransform.apply(proto);
- InternalState<ProtoType> result = usedNames.get(key);
+ InternalState result = usedNames.get(key);
if (result == null && parent != null) {
result = parent.findInternalStateFor(proto);
}
return result;
}
- private InternalState<ProtoType> getOrCreateInternalStateFor(ProtoType proto) {
+ private InternalState getOrCreateInternalStateFor(ProtoType proto) {
// TODO(herhut): Maybe allocate these sparsely and search via state chain.
KeyType key = keyTransform.apply(proto);
- InternalState<ProtoType> result = usedNames.get(key);
+ InternalState result = usedNames.get(key);
if (result == null) {
if (parent != null) {
- InternalState<ProtoType> parentState = parent.getOrCreateInternalStateFor(proto);
+ InternalState parentState = parent.getOrCreateInternalStateFor(proto);
result = parentState.createChild();
} else {
- result = new InternalState<>(itemFactory, null, dictionary);
+ result = new InternalState(itemFactory, null, dictionary);
}
usedNames.put(key, result);
}
@@ -79,7 +79,7 @@
}
private DexString getAssignedNameFor(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return null;
}
@@ -89,19 +89,19 @@
public DexString assignNewNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
DexString result = getAssignedNameFor(original, proto);
if (result == null) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
result = state.getNameFor(original, proto, markAsUsed);
}
return result;
}
public void reserveName(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
state.reserveName(name);
}
public boolean isReserved(DexString name, ProtoType proto) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return false;
}
@@ -109,7 +109,7 @@
}
public boolean isAvailable(DexString original, ProtoType proto, DexString candidate) {
- InternalState<ProtoType> state = findInternalStateFor(proto);
+ InternalState state = findInternalStateFor(proto);
if (state == null) {
return true;
}
@@ -118,25 +118,25 @@
}
public void addRenaming(DexString original, ProtoType proto, DexString newName) {
- InternalState<ProtoType> state = getOrCreateInternalStateFor(proto);
+ InternalState state = getOrCreateInternalStateFor(proto);
state.addRenaming(original, proto, newName);
}
- private class InternalState<InternalProtoType extends CachedHashValueDexItem> {
+ private class InternalState {
private static final int INITIAL_NAME_COUNT = 1;
private final char[] EMPTY_CHAR_ARRARY = new char[0];
protected final DexItemFactory itemFactory;
- private final InternalState<InternalProtoType> parentInternalState;
+ private final InternalState parentInternalState;
private Set<DexString> reservedNames = null;
- private Table<DexString, InternalProtoType, DexString> renamings = null;
+ private Table<DexString, ProtoType, DexString> renamings = null;
private int nameCount;
private final Iterator<String> dictionaryIterator;
private InternalState(
DexItemFactory itemFactory,
- InternalState<InternalProtoType> parentInternalState,
+ InternalState parentInternalState,
Iterator<String> dictionaryIterator) {
this.itemFactory = itemFactory;
this.parentInternalState = parentInternalState;
@@ -146,9 +146,7 @@
}
private InternalState(
- DexItemFactory itemFactory,
- InternalState<InternalProtoType> parentInternalState,
- List<String> dictionary) {
+ DexItemFactory itemFactory, InternalState parentInternalState, List<String> dictionary) {
this(itemFactory, parentInternalState, dictionary.iterator());
}
@@ -163,8 +161,8 @@
&& (parentInternalState == null || parentInternalState.isAvailable(name));
}
- InternalState<InternalProtoType> createChild() {
- return new InternalState<>(itemFactory, this, dictionaryIterator);
+ InternalState createChild() {
+ return new InternalState(itemFactory, this, dictionaryIterator);
}
void reserveName(DexString name) {
@@ -174,11 +172,11 @@
reservedNames.add(name);
}
- DexString getAssignedNameFor(DexString original, InternalProtoType proto) {
+ DexString getAssignedNameFor(DexString original, ProtoType proto) {
DexString result = null;
if (renamings != null) {
if (useUniqueMemberNames) {
- Map<InternalProtoType, DexString> row = renamings.row(original);
+ Map<ProtoType, DexString> row = renamings.row(original);
if (row != null) {
// Either not renamed yet (0) or renamed (1). If renamed, return the same renamed name
// so that other members with the same name can be renamed to the same renamed name.
@@ -196,7 +194,7 @@
return result;
}
- DexString getNameFor(DexString original, InternalProtoType proto, boolean markAsUsed) {
+ DexString getNameFor(DexString original, ProtoType proto, boolean markAsUsed) {
DexString name = getAssignedNameFor(original, proto);
if (name != null) {
return name;
@@ -210,7 +208,7 @@
return name;
}
- void addRenaming(DexString original, InternalProtoType proto, DexString newName) {
+ void addRenaming(DexString original, ProtoType proto, DexString newName) {
if (renamings == null) {
renamings = HashBasedTable.create();
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
index 3b3063d..1eb43f5 100644
--- a/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
+++ b/src/main/java/com/android/tools/r8/shaking/ReasonPrinter.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexType;
import com.google.common.collect.Sets;
+import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collections;
@@ -136,6 +137,8 @@
private int indentation = -1;
+ private PrintStream output = System.out;
+
void pushIsLivePrefix() {
prefixes.push("is live because ");
}
@@ -159,27 +162,27 @@
void startItem(DexItem item) {
indentation++;
indent();
- System.out.println(item.toSourceString());
+ output.println(item.toSourceString());
}
private void indent() {
for (int i = 0; i < indentation; i++) {
- System.out.print(" ");
+ output.print(" ");
}
}
void addReason(String thing) {
indent();
- System.out.print("|- ");
+ output.print("|- ");
String prefix = prefixes.peek();
- System.out.print(prefix);
- System.out.println(thing);
+ output.print(prefix);
+ output.println(thing);
}
void addMessage(String thing) {
indent();
- System.out.print("| ");
- System.out.println(thing);
+ output.print("| ");
+ output.println(thing);
}
void endItem() {
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 4dff93c..383b9c3 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.ClassFileResourceProvider;
import com.android.tools.r8.DataEntryResource;
import com.android.tools.r8.DataResourceProvider;
-import com.android.tools.r8.DataResourceProvider.Visitor;
import com.android.tools.r8.DexFilePerClassFileConsumer;
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.DirectoryClassFileProvider;
@@ -303,12 +302,12 @@
}
/** Add program file resources. */
- public Builder addProgramFiles(Path... files) throws NoSuchFileException {
+ public Builder addProgramFiles(Path... files) {
return addProgramFiles(Arrays.asList(files));
}
/** Add program file resources. */
- public Builder addProgramFiles(Collection<Path> files) throws NoSuchFileException {
+ public Builder addProgramFiles(Collection<Path> files) {
for (Path file : files) {
addProgramFile(file);
}
@@ -366,12 +365,12 @@
}
/** Add library file resources. */
- public Builder addLibraryFiles(Path... files) throws IOException {
+ public Builder addLibraryFiles(Path... files) {
return addLibraryFiles(Arrays.asList(files));
}
/** Add library file resources. */
- public Builder addLibraryFiles(Collection<Path> files) throws IOException {
+ public Builder addLibraryFiles(Collection<Path> files) {
for (Path file : files) {
addClasspathOrLibraryProvider(file, libraryResourceProviders);
}
@@ -578,9 +577,11 @@
mainDexListClasses);
}
- public void addProgramFile(Path file) throws NoSuchFileException {
+ public void addProgramFile(Path file) {
if (!Files.exists(file)) {
- throw new NoSuchFileException(file.toString());
+ PathOrigin pathOrigin = new PathOrigin(file);
+ NoSuchFileException noSuchFileException = new NoSuchFileException(file.toString());
+ reporter.error(new ExceptionDiagnostic(noSuchFileException, pathOrigin));
}
if (isDexFile(file)) {
addProgramResources(ProgramResource.fromFile(Kind.DEX, file));
@@ -610,12 +611,18 @@
}
private void addClasspathOrLibraryProvider(
- Path file, List<ClassFileResourceProvider> providerList) throws IOException {
+ Path file, List<ClassFileResourceProvider> providerList) {
if (!Files.exists(file)) {
- throw new NoSuchFileException(file.toString());
+ reporter.error(
+ new ExceptionDiagnostic(
+ new NoSuchFileException(file.toString()), new PathOrigin(file)));
}
if (isArchive(file)) {
- providerList.add(new ArchiveClassFileProvider(file));
+ try {
+ providerList.add(new ArchiveClassFileProvider(file));
+ } catch (IOException e) {
+ reporter.error(new ExceptionDiagnostic(e, new PathOrigin(file)));
+ }
} else if (Files.isDirectory(file) ) {
providerList.add(DirectoryClassFileProvider.fromDirectory(file));
} else {
diff --git a/src/test/java/com/android/tools/r8/AsmTestBase.java b/src/test/java/com/android/tools/r8/AsmTestBase.java
index 35767fb..2c4cb6b 100644
--- a/src/test/java/com/android/tools/r8/AsmTestBase.java
+++ b/src/test/java/com/android/tools/r8/AsmTestBase.java
@@ -8,9 +8,7 @@
import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.InternalOptions;
@@ -94,17 +92,17 @@
throws Exception {
// TODO(zerny): Port this to use diagnostics handler.
AndroidApp app = buildAndroidApp(classes);
- CompilationError r8Error = null;
- CompilationError r8ShakenError = null;
+ CompilationFailedException r8Error = null;
+ CompilationFailedException r8ShakenError = null;
try {
runOnArtRaw(compileWithR8(app), main);
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
r8Error = e;
}
try {
runOnArtRaw(compileWithR8(app, keepMainProguardConfiguration(main) + "-dontobfuscate\n"),
main);
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
r8ShakenError = e;
}
Assert.assertNotNull(r8Error);
@@ -112,7 +110,7 @@
}
protected void ensureSameOutputAfterMerging(String main, byte[]... classes)
- throws IOException, CompilationFailedException, ProguardRuleParserException {
+ throws IOException, CompilationFailedException {
AndroidApp app = buildAndroidApp(classes);
// Compile to dex files with D8.
AndroidApp dexApp = compileWithD8(app);
diff --git a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
index e01373c..6ccf086 100644
--- a/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8RunExamplesAndroidOTest.java
@@ -4,20 +4,15 @@
package com.android.tools.r8;
-import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.InternalCompilerError;
-import com.android.tools.r8.errors.Unimplemented;
+import static org.junit.Assert.assertTrue;
+
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.OffOrAuto;
import java.nio.file.Path;
import java.util.function.UnaryOperator;
-import org.hamcrest.core.CombinableMatcher;
-import org.hamcrest.core.IsInstanceOf;
-import org.hamcrest.core.StringContains;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
-import org.junit.internal.matchers.ThrowableMessageMatcher;
public class D8RunExamplesAndroidOTest extends RunExamplesAndroidOTest<D8Command.Builder> {
@@ -37,7 +32,7 @@
}
@Override
- void build(Path inputFile, Path out, OutputMode mode) throws Throwable {
+ void build(Path inputFile, Path out, OutputMode mode) throws CompilationFailedException {
D8Command.Builder builder = D8Command.builder().setOutput(out, mode);
for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
builder = transformation.apply(builder);
@@ -46,13 +41,7 @@
ToolHelper.getAndroidJar(
androidJarVersion == null ? builder.getMinApiLevel() : androidJarVersion.getLevel()));
builder.addProgramFiles(inputFile);
- try {
- ToolHelper.runD8(builder, this::combinedOptionConsumer);
- } catch (Unimplemented | CompilationError | InternalCompilerError re) {
- throw re;
- } catch (RuntimeException re) {
- throw re.getCause() == null ? re : re.getCause();
- }
+ ToolHelper.runD8(builder, this::combinedOptionConsumer);
}
D8TestRunner withIntermediate(boolean intermediate) {
@@ -77,7 +66,7 @@
// compilation should have failed on CompilationError since A is declaring a default method.
Assert.fail();
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
// Expected.
}
}
@@ -363,13 +352,14 @@
.withClasspath(lib1.getInputJar())
.withClasspath(lib2.getInputJar())
.withMinApiLevel(minApi);
- thrown.expect(
- new CombinableMatcher<CompilationError>(new IsInstanceOf(CompilationError.class))
- .and(new ThrowableMessageMatcher<CompilationError>(
- new StringContains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault")))
- .and(new ThrowableMessageMatcher<CompilationError>(
- new StringContains("desugaringwithmissingclasslib3.C"))));
- test.build();
+ try {
+ test.build();
+ Assert.fail("Expected build to fail with CompilationFailedException");
+ } catch (CompilationFailedException e) {
+ String message = e.getCause().getMessage();
+ assertTrue(message.contains("desugaringwithmissingclasstest2.ImplementMethodsWithDefault"));
+ assertTrue(message.contains("desugaringwithmissingclasslib3.C"));
+ }
}
@Test
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 37515d2..3ef4037 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -1408,8 +1408,7 @@
boolean disableInlining,
boolean disableClassInlining,
boolean hasMissingClasses)
- throws IOException, ProguardRuleParserException, ExecutionException,
- CompilationFailedException {
+ throws CompilationFailedException {
executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
disableInlining, disableClassInlining, hasMissingClasses);
}
@@ -1423,8 +1422,7 @@
boolean disableInlining,
boolean disableClassInlining,
boolean hasMissingClasses)
- throws IOException, ProguardRuleParserException, ExecutionException,
- CompilationFailedException {
+ throws CompilationFailedException {
assert mode != null;
switch (compilerUnderTest) {
case D8_AFTER_R8CF:
@@ -1956,8 +1954,6 @@
specification.hasMissingClasses);
} catch (CompilationFailedException e) {
throw new CompilationError(e.getMessage(), e);
- } catch (ExecutionException e) {
- throw e.getCause();
}
System.err.println("Should have failed R8/D8 compilation with a CompilationError.");
return;
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
index 008c6b9..428c611 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesCommon.java
@@ -12,7 +12,6 @@
import com.android.tools.r8.R8RunArtTestsTest.DexTool;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.errors.Unreachable;
-import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.TestDescriptionWatcher;
import java.io.IOException;
@@ -170,19 +169,16 @@
.setOutput(getOutputFile(), outputMode)
.setMode(mode)
.build();
- ExceptionUtils.withR8CompilationHandler(
- command.getReporter(),
- () ->
- ToolHelper.runR8(
- command,
- options -> {
- options.lineNumberOptimization = LineNumberOptimization.OFF;
- options.enableCfFrontend = frontend == Frontend.CF;
- if (output == Output.CF) {
- // Class inliner is not supported with CF backend yet.
- options.enableClassInlining = false;
- }
- }));
+ ToolHelper.runR8(
+ command,
+ options -> {
+ options.lineNumberOptimization = LineNumberOptimization.OFF;
+ options.enableCfFrontend = frontend == Frontend.CF;
+ if (output == Output.CF) {
+ // Class inliner is not supported with CF backend yet.
+ options.enableClassInlining = false;
+ }
+ });
break;
}
default:
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
index 83df845..5b4482e 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidNTest.java
@@ -129,9 +129,8 @@
.run();
}
- @Test
+ @Test(expected = CompilationFailedException.class)
public void staticInterfaceMethodsErrorDueToMinSdk() throws Throwable {
- thrown.expect(ApiLevelException.class);
test("staticinterfacemethods-error-due-to-min-sdk", "interfacemethods",
"StaticInterfaceMethods")
.withInterfaceMethodDesugaring(OffOrAuto.Off)
@@ -146,9 +145,8 @@
.run();
}
- @Test
+ @Test(expected = CompilationFailedException.class)
public void defaultMethodsErrorDueToMinSdk() throws Throwable {
- thrown.expect(ApiLevelException.class);
test("defaultmethods-error-due-to-min-sdk", "interfacemethods",
"DefaultMethods")
.withInterfaceMethodDesugaring(OffOrAuto.Off)
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index 1f0c464..68aec3f 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -149,7 +149,7 @@
void run() throws Throwable {
if (minSdkErrorExpected(testName)) {
- thrown.expect(ApiLevelException.class);
+ thrown.expect(CompilationFailedException.class);
}
String qualifiedMainClass = packageName + "." + mainClass;
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
index 7f15eca..177fe89 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidPTest.java
@@ -136,7 +136,7 @@
void run() throws Throwable {
if (minSdkErrorExpected(testName)) {
- thrown.expect(ApiLevelException.class);
+ thrown.expect(CompilationFailedException.class);
}
String qualifiedMainClass = packageName + "." + mainClass;
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 48a14f3..99ac1f7 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -139,7 +139,7 @@
void run() throws Throwable {
if (minSdkErrorExpected(testName)) {
- thrown.expect(ApiLevelException.class);
+ thrown.expect(CompilationFailedException.class);
}
String qualifiedMainClass = packageName + "." + mainClass;
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 48e7d1a..22bb5e5 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.code.Instruction;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexCode;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.SmaliWriter;
@@ -106,10 +107,8 @@
return file;
}
- /**
- * Build an AndroidApp with the specified test classes as byte array.
- */
- protected AndroidApp buildAndroidApp(byte[]... classes) throws IOException {
+ /** Build an AndroidApp with the specified test classes as byte array. */
+ protected AndroidApp buildAndroidApp(byte[]... classes) {
AndroidApp.Builder builder = AndroidApp.builder();
for (byte[] clazz : classes) {
builder.addClassProgramData(clazz, Origin.unknown());
@@ -342,7 +341,8 @@
}
/** Compile an application with R8. */
- protected AndroidApp compileWithR8(Class... classes) throws IOException {
+ protected AndroidApp compileWithR8(Class... classes)
+ throws IOException, CompilationFailedException {
return ToolHelper.runR8(readClasses(classes));
}
@@ -582,13 +582,8 @@
*/
protected String runOnJava(Class mainClass) throws Exception {
ProcessResult result = ToolHelper.runJava(mainClass);
- if (result.exitCode != 0) {
- System.out.println("Std out:");
- System.out.println(result.stdout);
- System.out.println("Std err:");
- System.out.println(result.stderr);
- assertEquals(0, result.exitCode);
- }
+ ToolHelper.failOnProcessFailure(result);
+ ToolHelper.failOnVerificationErrors(result);
return result.stdout;
}
@@ -604,9 +599,17 @@
}
/** Run application on Java with the specified main class and provided arguments. */
+ protected String runOnJava(AndroidApp app, String mainClass, String... args) throws IOException {
+ return runOnJava(app, mainClass, Arrays.asList(args));
+ }
+
+ /** Run application on Java with the specified main class and provided arguments. */
protected String runOnJava(AndroidApp app, String mainClass, List<String> args)
throws IOException {
- return runOnJavaRaw(app, mainClass, args).stdout;
+ ProcessResult result = runOnJavaRaw(app, mainClass, args);
+ ToolHelper.failOnProcessFailure(result);
+ ToolHelper.failOnVerificationErrors(result);
+ return result.stdout;
}
protected ProcessResult runOnJavaRawNoVerify(String main, byte[]... classes) throws IOException {
@@ -648,6 +651,18 @@
return ToolHelper.runJavaNoVerify(out, mainClass, args.toArray(new String[0]));
}
+ /** Run application on Art or Java with the specified main class. */
+ protected String runOnVM(AndroidApp app, String mainClass, Backend backend) throws IOException {
+ switch (backend) {
+ case CF:
+ return runOnJava(app, mainClass);
+ case DEX:
+ return runOnArt(app, mainClass);
+ default:
+ throw new Unreachable("Unexpected backend: " + backend);
+ }
+ }
+
private String extractClassName(byte[] ccc) {
class ClassNameExtractor extends ClassVisitor {
private String className;
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 530518a..e34282d 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -840,39 +840,32 @@
.setProguardMapConsumer(StringConsumer.emptyConsumer());
}
- public static AndroidApp runR8(AndroidApp app) throws IOException {
+ public static AndroidApp runR8(AndroidApp app) throws CompilationFailedException {
return runR8WithProgramConsumer(app, DexIndexedConsumer.emptyConsumer());
}
public static AndroidApp runR8WithProgramConsumer(AndroidApp app, ProgramConsumer programConsumer)
- throws IOException {
- try {
- return runR8(prepareR8CommandBuilder(app, programConsumer).build());
- } catch (CompilationFailedException e) {
- throw new RuntimeException(e);
- }
+ throws CompilationFailedException {
+ return runR8(prepareR8CommandBuilder(app, programConsumer).build());
}
public static AndroidApp runR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
- throws IOException {
- try {
- return runR8(prepareR8CommandBuilder(app).build(), optionsConsumer);
- } catch (CompilationFailedException e) {
- throw new RuntimeException(e);
- }
+ throws CompilationFailedException {
+ return runR8(prepareR8CommandBuilder(app).build(), optionsConsumer);
}
- public static AndroidApp runR8(R8Command command) throws IOException {
+ public static AndroidApp runR8(R8Command command) throws CompilationFailedException {
return runR8(command, null);
}
public static AndroidApp runR8(R8Command command, Consumer<InternalOptions> optionsConsumer)
- throws IOException {
+ throws CompilationFailedException {
return runR8WithFullResult(command, optionsConsumer);
}
public static AndroidApp runR8WithFullResult(
- R8Command command, Consumer<InternalOptions> optionsConsumer) throws IOException {
+ R8Command command, Consumer<InternalOptions> optionsConsumer)
+ throws CompilationFailedException {
// TODO(zerny): Should we really be adding the android library in ToolHelper?
AndroidApp app = command.getInputApp();
if (app.getLibraryResourceProviders().isEmpty()) {
@@ -891,39 +884,32 @@
return compatSink.build();
}
- public static void addFilteredAndroidJar(BaseCommand.Builder builder, AndroidApiLevel apiLevel)
- throws IOException {
+ public static void addFilteredAndroidJar(BaseCommand.Builder builder, AndroidApiLevel apiLevel) {
addFilteredAndroidJar(getAppBuilder(builder), apiLevel);
}
- public static void addFilteredAndroidJar(AndroidApp.Builder builder, AndroidApiLevel apiLevel)
- throws IOException {
+ public static void addFilteredAndroidJar(AndroidApp.Builder builder, AndroidApiLevel apiLevel) {
builder.addFilteredLibraryArchives(Collections.singletonList(
new FilteredClassPath(getAndroidJar(apiLevel),
ImmutableList.of("!junit/**", "!android/test/**"))));
}
- public static AndroidApp runD8(AndroidApp app) throws IOException {
+ public static AndroidApp runD8(AndroidApp app) throws CompilationFailedException {
return runD8(app, null);
}
public static AndroidApp runD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
- throws IOException {
- try {
- return runD8(D8Command.builder(app), optionsConsumer);
- } catch (CompilationFailedException e) {
- throw new RuntimeException(e);
- }
+ throws CompilationFailedException {
+ return runD8(D8Command.builder(app), optionsConsumer);
}
- public static AndroidApp runD8(D8Command.Builder builder)
- throws IOException, CompilationFailedException {
+ public static AndroidApp runD8(D8Command.Builder builder) throws CompilationFailedException {
return runD8(builder, null);
}
public static AndroidApp runD8(
D8Command.Builder builder, Consumer<InternalOptions> optionsConsumer)
- throws IOException, CompilationFailedException {
+ throws CompilationFailedException {
AndroidAppConsumers compatSink = new AndroidAppConsumers(builder);
D8Command command = builder.build();
InternalOptions options = command.getInternalOptions();
@@ -1310,13 +1296,13 @@
return runArtNoVerificationErrorsRaw(files, mainClass, extras, version).stdout;
}
- private static void failOnProcessFailure(ProcessResult result) {
+ protected static void failOnProcessFailure(ProcessResult result) {
if (result.exitCode != 0) {
- fail("Unexpected art failure: '" + result.stderr + "'\n" + result.stdout);
+ fail("Unexpected failure: '" + result.stderr + "'\n" + result.stdout);
}
}
- private static void failOnVerificationErrors(ProcessResult result) {
+ protected static void failOnVerificationErrors(ProcessResult result) {
if (result.stderr.contains("Verification error")) {
fail("Verification error: \n" + result.stderr);
}
diff --git a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
index ad6a72b..3514a00 100644
--- a/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
+++ b/src/test/java/com/android/tools/r8/cf/DebugInfoTestRunner.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.ClassFileConsumer;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.ProgramConsumer;
import com.android.tools.r8.R8Command;
@@ -14,8 +15,6 @@
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;
@@ -39,8 +38,8 @@
boolean invalidDebugInfo = false;
try {
build(builder -> builder.addProgramFiles(out1), new ClassFileConsumer.ArchiveConsumer(out2));
- } catch (CompilationError e) {
- invalidDebugInfo = e.getCause() instanceof InvalidDebugInfoException;
+ } catch (CompilationFailedException e) {
+ invalidDebugInfo = e.getCause().getMessage().contains("Invalid debug info");
}
// TODO(b/77522100): Change to assertFalse when fixed.
assertTrue(invalidDebugInfo);
diff --git a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
index 1938c46..a1042b8 100644
--- a/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
+++ b/src/test/java/com/android/tools/r8/checkdiscarded/CheckDiscardedTest.java
@@ -3,13 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.checkdiscarded;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.checkdiscarded.testclasses.Main;
import com.android.tools.r8.checkdiscarded.testclasses.UnusedClass;
import com.android.tools.r8.checkdiscarded.testclasses.UsedClass;
import com.android.tools.r8.checkdiscarded.testclasses.WillBeGone;
import com.android.tools.r8.checkdiscarded.testclasses.WillStay;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.List;
@@ -28,7 +28,7 @@
+ checkDiscardRule(checkMembers, annotation);
try {
compileWithR8(classes, proguardConfig, this::noInlining);
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
Assert.assertTrue(shouldFail);
return;
}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
index 2b73eaa..3674108 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/InterfaceMethodDesugaringTests.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.AsmTestBase;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.VmTestRunner;
@@ -20,7 +21,6 @@
import com.android.tools.r8.desugaring.interfacemethods.default2.TestMainDefault2;
import com.android.tools.r8.desugaring.interfacemethods.static0.TestMainStatic0;
import com.android.tools.r8.desugaring.interfacemethods.static1.TestMainStatic1;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.AndroidApiLevel;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -139,7 +139,7 @@
ToolHelper.getClassAsBytes(TestMainDefault0.class));
}
- @Test(expected = CompilationError.class)
+ @Test(expected = CompilationFailedException.class)
@IgnoreForRangeOfVmVersions(from = Version.V7_0_0, to = Version.DEFAULT) // No desugaring
public void testInvokeDefault1() throws Exception {
ensureSameOutput(
diff --git a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
index 39b5ff8..7e0bfec 100644
--- a/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
+++ b/src/test/java/com/android/tools/r8/graph/TargetLookupTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.ToolHelper.ProcessResult;
@@ -175,7 +176,7 @@
}
@Test
- public void lookupFieldWithDefaultInInterface() {
+ public void lookupFieldWithDefaultInInterface() throws CompilationFailedException {
SmaliBuilder builder = new SmaliBuilder();
builder.addInterface("Interface");
diff --git a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
index 49516d7..3d0aaff 100644
--- a/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/jasmin/MemberResolutionTest.java
@@ -5,12 +5,12 @@
import static java.util.Collections.emptyList;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.VmTestRunner;
import com.android.tools.r8.VmTestRunner.IgnoreForRangeOfVmVersions;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
import com.android.tools.r8.jasmin.JasminBuilder.ClassFileVersion;
import com.android.tools.r8.utils.ThrowingBiFunction;
@@ -572,7 +572,7 @@
ProcessResult result = process.get();
Assert.assertFalse(compiler != null && predicate.test(compiler));
Assert.assertTrue(result.stderr.contains(name));
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
Assert.assertTrue(compiler == null || predicate.test(compiler));
} catch (Exception e) {
Assert.fail();
diff --git a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
index 1104e54..6589e06 100644
--- a/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
+++ b/src/test/java/com/android/tools/r8/jasmin/NameTestBase.java
@@ -7,8 +7,8 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.utils.StringUtils;
import java.nio.file.InvalidPathException;
import java.util.Arrays;
@@ -112,8 +112,8 @@
try {
runOnArtD8(jasminBuilder, mainClassName);
fail("D8 should have rejected this case.");
- } catch (CompilationError t) {
- assertTrue(t.getMessage().contains(expectedNameInFailingD8Message));
+ } catch (CompilationFailedException t) {
+ assertTrue(t.getCause().getMessage().contains(expectedNameInFailingD8Message));
}
// Make sure ART also fail, if D8 rejects it.
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index e608a20..4a76bde 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -185,7 +185,7 @@
try {
verifyMainDexContains(TWO_LARGE_CLASSES, getTwoLargeClassesAppPath(), false);
fail("Expect to fail, for there are too many classes for the main-dex list.");
- } catch (AbortException e) {
+ } catch (CompilationFailedException e) {
assertEquals(1, errors.size());
String message = errors.get(0).getDiagnosticMessage();
// Make sure {@link MonoDexDistributor} was _not_ used.
@@ -229,7 +229,7 @@
try {
verifyMainDexContains(MANY_CLASSES, getManyClassesMultiDexAppPath(), false);
fail("Expect to fail, for there are too many classes for the main-dex list.");
- } catch (AbortException e) {
+ } catch (CompilationFailedException e) {
assertEquals(1, errors.size());
String message = errors.get(0).getDiagnosticMessage();
// Make sure {@link MonoDexDistributor} was _not_ used.
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index 1e2e46e..7ce8a05 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.StringConsumer;
@@ -17,7 +18,6 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -287,8 +287,8 @@
try {
runR8(getCommandForApps(out, flag, MINIFICATION_JAR).build());
fail("Expect to detect renaming conflict");
- } catch (ProguardMapError e) {
- assertTrue(e.getMessage().contains("functionFromIntToInt"));
+ } catch (CompilationFailedException e) {
+ assertTrue(e.getCause().getMessage().contains("functionFromIntToInt"));
}
}
@@ -310,8 +310,7 @@
.addProguardConfigurationFiles(flag);
}
- private static AndroidApp runR8(R8Command command)
- throws ProguardRuleParserException, ExecutionException, IOException {
+ private static AndroidApp runR8(R8Command command) throws CompilationFailedException {
return ToolHelper.runR8(
command,
options -> {
diff --git a/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
new file mode 100644
index 0000000..93f6215
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/CovariantReturnTypeTest.java
@@ -0,0 +1,113 @@
+// 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.naming;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isRenamed;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.io.File;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class CovariantReturnTypeTest extends TestBase {
+
+ @Ignore("b/115613850")
+ @Test
+ public void test() throws Exception {
+ for (int i = 0; i < 3; i++) {
+ JasminBuilder appBuilder = new JasminBuilder();
+ ClassBuilder classBuilder = appBuilder.addClass("package.TestClass");
+
+ classBuilder.addVirtualMethod(
+ "method1",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+ classBuilder.addVirtualMethod(
+ "method1", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method1", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+ classBuilder.addVirtualMethod(
+ "method2", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method2",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+ classBuilder.addVirtualMethod(
+ "method2", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+
+ classBuilder.addVirtualMethod(
+ "method3", "Lpackage/A;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method3", "Lpackage/B;", ".limit stack 1", ".limit locals 1", "aconst_null", "areturn");
+ classBuilder.addVirtualMethod(
+ "method3",
+ "Ljava/lang/Object;",
+ ".limit stack 1",
+ ".limit locals 1",
+ "aconst_null",
+ "areturn");
+
+ appBuilder.addInterface("package.A");
+ appBuilder.addInterface("package.B");
+
+ Path proguardMapPath = File.createTempFile("mapping", ".txt", temp.getRoot()).toPath();
+ AndroidApp output =
+ compileWithR8(
+ appBuilder.build(),
+ keepMainProguardConfiguration("package.TestClass"),
+ options -> {
+ options.enableTreeShaking = false;
+ options.proguardMapConsumer = new FileConsumer(proguardMapPath);
+ });
+
+ CodeInspector inspector = new CodeInspector(output, proguardMapPath);
+ ClassSubject clazz = inspector.clazz("package.TestClass");
+ assertThat(clazz, isPresent());
+
+ Map<String, Set<MethodSubject>> methodSubjectsByName = new HashMap<>();
+ for (String name : ImmutableList.of("method1", "method2", "method3")) {
+ methodSubjectsByName.put(
+ name,
+ ImmutableSet.of(
+ clazz.method("java.lang.Object", name, ImmutableList.of()),
+ clazz.method("package.A", name, ImmutableList.of()),
+ clazz.method("package.B", name, ImmutableList.of())));
+ }
+
+ Set<String> minifiedMethodNames = new HashSet<>();
+ for (Set<MethodSubject> methodSubjects : methodSubjectsByName.values()) {
+ for (MethodSubject methodSubject : methodSubjects) {
+ assertThat(methodSubject, isRenamed());
+ minifiedMethodNames.add(methodSubject.getFinalName());
+ }
+ }
+ assertEquals(3, minifiedMethodNames.size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
new file mode 100644
index 0000000..edf77b0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/b114554345/B114554345.java
@@ -0,0 +1,136 @@
+// 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.naming.b114554345;
+
+import static junit.framework.TestCase.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Ignore;
+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 B114554345 extends TestBase {
+
+ private final Backend backend;
+
+ @Parameters(name = "mode:{0}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (Backend backend : Backend.values()) {
+ parameters.add(new Object[] {backend});
+ }
+ return parameters;
+ }
+
+ public B114554345(Backend backend) {
+ this.backend = backend;
+ }
+
+ @Ignore("b/114554345")
+ @Test
+ public void test() throws Exception {
+ AndroidApp input =
+ AndroidApp.builder()
+ .addProgramFiles(
+ ToolHelper.getClassFilesForTestDirectory(
+ ToolHelper.getPackageDirectoryForTestPackage(this.getClass().getPackage()),
+ path -> !path.toString().contains("B114554345")))
+ .build();
+ AndroidApp output =
+ compileWithR8(
+ input,
+ keepMainProguardConfiguration(TestDriver.class),
+ options -> options.enableInlining = false,
+ backend);
+ String mainClass = TestDriver.class.getName();
+ assertEquals(runOnJava(TestDriver.class), runOnVM(output, mainClass, backend));
+ }
+}
+
+// Interface and two implementations.
+
+interface Interface {
+ Interface method();
+}
+
+class InterfaceImpl implements Interface {
+
+ @Override
+ public InterfaceImpl method() {
+ System.out.println("In InterfaceImpl.method()");
+ return this;
+ }
+}
+
+class OtherInterfaceImpl extends InterfaceImpl {
+
+ @Override
+ public OtherInterfaceImpl method() {
+ System.out.println("In OtherInterfaceImpl.method()");
+ return this;
+ }
+}
+
+// Sub-interface and three implementations.
+
+interface SubInterface extends Interface {
+ SubInterface method();
+}
+
+class SubInterfaceImpl implements SubInterface {
+
+ @Override
+ public SubInterfaceImpl method() {
+ System.out.println("In SubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class OtherSubInterfaceImpl implements SubInterface {
+
+ @Override
+ public OtherSubInterfaceImpl method() {
+ System.out.println("In OtherSubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class YetAnotherSubInterfaceImpl extends InterfaceImpl implements SubInterface {
+
+ @Override
+ public YetAnotherSubInterfaceImpl method() {
+ System.out.println("In YetAnotherSubInterfaceImpl.method()");
+ return this;
+ }
+}
+
+class TestDriver {
+
+ public static void main(String[] args) {
+ foo(new InterfaceImpl());
+ foo(new OtherInterfaceImpl());
+ foo(new SubInterfaceImpl());
+ foo(new YetAnotherSubInterfaceImpl());
+ bar(new SubInterfaceImpl());
+ bar(new OtherSubInterfaceImpl());
+ bar(new YetAnotherSubInterfaceImpl());
+ }
+
+ private static void foo(Interface obj) {
+ obj.method();
+ }
+
+ private static void bar(SubInterface obj) {
+ obj.method();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
index 2073ab9..354f3c2 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switches/SwitchRewritingTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.code.Const;
import com.android.tools.r8.code.Const4;
import com.android.tools.r8.code.ConstHigh16;
@@ -39,7 +40,8 @@
|| instruction instanceof ConstHigh16
|| instruction instanceof Const;
}
- private void runSingleCaseDexTest(boolean packed, int key) {
+
+ private void runSingleCaseDexTest(boolean packed, int key) throws CompilationFailedException {
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
String switchInstruction;
String switchData;
@@ -100,7 +102,7 @@
}
@Test
- public void singleCaseDex() {
+ public void singleCaseDex() throws CompilationFailedException {
for (boolean packed : new boolean[]{true, false}) {
runSingleCaseDexTest(packed, Integer.MIN_VALUE);
runSingleCaseDexTest(packed, -1);
@@ -110,7 +112,8 @@
}
}
- private void runTwoCaseSparseToPackedOrIfsDexTest(int key1, int key2) {
+ private void runTwoCaseSparseToPackedOrIfsDexTest(int key1, int key2)
+ throws CompilationFailedException {
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
MethodSignature signature = builder.addStaticMethod(
@@ -162,7 +165,7 @@
}
@Test
- public void twoCaseSparseToPackedOrIfsDex() {
+ public void twoCaseSparseToPackedOrIfsDex() throws CompilationFailedException {
for (int delta = 1; delta <= 3; delta++) {
runTwoCaseSparseToPackedOrIfsDexTest(0, delta);
runTwoCaseSparseToPackedOrIfsDexTest(-delta, 0);
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryExtendsProgramTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryExtendsProgramTest.java
index 09faa8b..834a486 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryExtendsProgramTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryExtendsProgramTest.java
@@ -3,40 +3,29 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.shaking.libraryextendsprogram.Interface;
import com.android.tools.r8.shaking.libraryextendsprogram.Main;
import com.android.tools.r8.shaking.libraryextendsprogram.SubClass;
import com.android.tools.r8.shaking.libraryextendsprogram.SuperClass;
-import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApp;
import com.google.common.collect.ImmutableList;
-import org.junit.Assert;
import org.junit.Test;
public class LibraryExtendsProgramTest extends TestBase {
- @Test
+ @Test(expected = CompilationFailedException.class)
public void libraryClassExtendsProgramClass() throws Exception {
AndroidApp theApp = readClasses(ImmutableList.of(Main.class, SuperClass.class),
ImmutableList.of(SubClass.class, Interface.class));
- try {
- compileWithR8(theApp, o -> o.ignoreMissingClasses = true);
- } catch (AbortException e) {
- return;
- }
- Assert.fail();
+ compileWithR8(theApp, o -> o.ignoreMissingClasses = true);
}
- @Test
+ @Test(expected = CompilationFailedException.class)
public void libraryClassImplementsProgramInterface() throws Exception {
AndroidApp theApp = readClasses(ImmutableList.of(Main.class, Interface.class),
ImmutableList.of(SubClass.class, SuperClass.class));
- try {
- compileWithR8(theApp, o -> o.ignoreMissingClasses = true);
- } catch (AbortException e) {
- return;
- }
- Assert.fail();
+ compileWithR8(theApp, o -> o.ignoreMissingClasses = true);
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index ff762b7..30de112 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -9,10 +9,10 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.CompatProguardCommandBuilder;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.invokesuper.Consumer;
@@ -588,7 +588,7 @@
builder.setProgramConsumer(emptyConsumer(backend)).addLibraryFiles(runtimeJar(backend));
try {
app = ToolHelper.runR8(builder.build(), o -> o.enableClassInlining = false);
- } catch (CompilationError e) {
+ } catch (CompilationFailedException e) {
assertTrue(!forceProguardCompatibility && (!innerClasses || !enclosingMethod));
return;
}
diff --git a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
index de8b832..9cfd7fa 100644
--- a/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
+++ b/src/test/java/com/android/tools/r8/smali/ComputeBlockTryRangeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.smali;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.smali.SmaliBuilder.MethodSignature;
import com.android.tools.r8.utils.AndroidApp;
@@ -13,7 +14,7 @@
public class ComputeBlockTryRangeTest extends SmaliTestBase {
@Test
- public void jumpIntoTryRange() {
+ public void jumpIntoTryRange() throws CompilationFailedException {
SmaliBuilder builder = new SmaliBuilder("Test");
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
index 8d780b4..912915b 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -6,6 +6,7 @@
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.origin.EmbeddedOrigin;
import com.android.tools.r8.utils.AndroidApp;
@@ -28,7 +29,7 @@
}
@Test
- public void buildWithoutLibrary() {
+ public void buildWithoutLibrary() throws CompilationFailedException {
// Build simple "Hello, world!" application.
SmaliBuilder builder = new SmaliBuilder(DEFAULT_CLASS_NAME);
builder.addMainMethod(
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index b12c0b9..0aa8cae 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -69,17 +69,15 @@
}
}
- protected AndroidApp processApplication(AndroidApp application) {
+ protected AndroidApp processApplication(AndroidApp application)
+ throws CompilationFailedException {
return processApplication(application, null);
}
- protected AndroidApp processApplication(AndroidApp application,
- Consumer<InternalOptions> optionsConsumer) {
- try {
- return ToolHelper.runR8(application, optionsConsumer);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ protected AndroidApp processApplication(
+ AndroidApp application, Consumer<InternalOptions> optionsConsumer)
+ throws CompilationFailedException {
+ return ToolHelper.runR8(application, optionsConsumer);
}
protected Path runR8(SmaliBuilder builder, List<String> proguardConfigurations) {
@@ -224,7 +222,12 @@
returnType, parameters, locals, instructions);
// Process the application with R8.
- AndroidApp processdApplication = processApplication(application);
+ AndroidApp processdApplication = null;
+ try {
+ processdApplication = processApplication(application);
+ } catch (CompilationFailedException e) {
+ throw new RuntimeException(e);
+ }
assertEquals(1, getNumberOfProgramClasses(processdApplication));
// Return the processed method for inspection.
diff --git a/tools/compare_apk_sizes.py b/tools/compare_apk_sizes.py
new file mode 100755
index 0000000..e477320
--- /dev/null
+++ b/tools/compare_apk_sizes.py
@@ -0,0 +1,143 @@
+#!/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.
+
+# Script for checking impact of a change by comparing the sizes of generated
+# classes in an apk.
+
+import glob
+import optparse
+import os
+import shutil
+import sys
+import toolhelper
+import utils
+import zipfile
+import StringIO
+
+USAGE = """%prog [options] app1 app2
+ NOTE: This only makes sense if minification is disabled"""
+
+def parse_options():
+ result = optparse.OptionParser(usage=USAGE)
+ result.add_option('--temp',
+ help='Temporary directory to store extracted classes in')
+ result.add_option('--report',
+ help='Print comparison to this location instead of stdout')
+ return result.parse_args()
+
+def extract_apk(apk, output):
+ if os.path.exists(output):
+ shutil.rmtree(output)
+ zipfile.ZipFile(apk).extractall(output)
+ with utils.ChangedWorkingDirectory(output):
+ dex = glob.glob('*.dex')
+ return [os.path.join(output, dexfile) for dexfile in dex]
+
+def ensure_exists(files):
+ for f in files:
+ if not os.path.exists(f):
+ raise Exception('%s does not exist')
+
+def extract_classes(input, output):
+ if os.path.exists(output):
+ shutil.rmtree(output)
+ os.makedirs(output)
+ args = ['--file-per-class',
+ '--output', output]
+ args.extend(input)
+ if toolhelper.run('d8', args) is not 0:
+ raise Exception('Failed running d8')
+
+class FileInfo:
+ def __init__(self, path, root):
+ self.path = path
+ self.full_path = os.path.join(root, path)
+ self.size = os.path.getsize(self.full_path)
+
+def generate_file_info(path):
+ file_info_map = {}
+ with utils.ChangedWorkingDirectory(path):
+ for root, dirs, files in os.walk('.'):
+ for f in files:
+ assert f.endswith('dex')
+ file_path = os.path.join(root, f)
+ entry = FileInfo(file_path, path)
+ file_info_map[file_path] = entry
+ return file_info_map
+
+def print_info(app, app_files, only_in_app, bigger_in_app, output):
+ output.write('Only in %s\n' % app)
+ only_app_sorted = sorted(only_in_app,
+ key=lambda a: app_files[a].size,
+ reverse=True)
+ output.write('\n'.join([' %s %s bytes' %
+ (x, app_files[x].size) for x in only_app_sorted]))
+ output.write('\n\n')
+ output.write('Bigger in %s\n' % app)
+ # Sort by the percentage diff compared to size
+ percent = lambda a: (0.0 + bigger_in_app.get(a))/app_files.get(a).size * 100
+ for bigger in sorted(bigger_in_app, key=percent, reverse=True):
+ output.write(' {0:.3f}% {1} bytes {2}\n'.format(percent(bigger),
+ bigger_in_app[bigger],
+ bigger))
+ output.write('\n\n')
+
+
+def compare(app1_classes_dir, app2_classes_dir, app1, app2, report):
+ app1_files = generate_file_info(app1_classes_dir)
+ app2_files = generate_file_info(app2_classes_dir)
+ only_in_app1 = [k for k in app1_files if k not in app2_files]
+ only_in_app2 = [k for k in app2_files if k not in app1_files]
+ in_both = [k for k in app2_files if k in app1_files]
+ assert len(app1_files) == len(only_in_app1) + len(in_both)
+ assert len(app2_files) == len(only_in_app2) + len(in_both)
+ bigger_in_app1 = {}
+ bigger_in_app2 = {}
+ same_size = []
+ for f in in_both:
+ app1_entry = app1_files[f]
+ app2_entry = app2_files[f]
+ if app1_entry.size > app2_entry.size:
+ bigger_in_app1[f] = app1_entry.size - app2_entry.size
+ elif app2_entry.size > app1_entry.size:
+ bigger_in_app2[f] = app2_entry.size - app1_entry.size
+ else:
+ same_size.append(f)
+ output = open(report, 'w') if report else sys.stdout
+ print_info(app1, app1_files, only_in_app1, bigger_in_app1, output)
+ print_info(app2, app2_files, only_in_app2, bigger_in_app2, output)
+ output.write('Same size\n')
+ output.write('\n'.join([' %s' % x for x in same_size]))
+ if report:
+ output.close()
+
+def Main():
+ (options, args) = parse_options()
+ if len(args) is not 2:
+ print args
+ print('Takes exactly two arguments, the two apps to compare')
+ return 1
+ app1 = args[0]
+ app2 = args[1]
+ ensure_exists([app1, app2])
+ with utils.TempDir() as temporary:
+ # If a temp dir is passed in, use that instead of the generated temporary
+ output = options.temp if options.temp else temporary
+ ensure_exists([output])
+ app1_input = [app1]
+ app2_input = [app2]
+ if app1.endswith('apk'):
+ app1_input = extract_apk(app1, os.path.join(output, 'app1'))
+ if app2.endswith('apk'):
+ app2_input = extract_apk(app2, os.path.join(output, 'app2'))
+ app1_classes_dir = os.path.join(output, 'app1_classes')
+ app2_classes_dir = os.path.join(output, 'app2_classes')
+
+ extract_classes(app1_input, app1_classes_dir)
+ extract_classes(app2_input, app2_classes_dir)
+ compare(app1_classes_dir, app2_classes_dir, app1, app2, options.report)
+
+if __name__ == '__main__':
+ sys.exit(Main())