Merge "Report errors as Diagnostics"
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index cb19bad..20b63df 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -13,10 +13,14 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.ir.conversion.IRConverter;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppOutputSink;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.CompilationFailedException;
+import com.android.tools.r8.utils.IOExceptionDiagnostic;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.VersionProperties;
@@ -65,13 +69,13 @@
    * @param command D8 command.
    * @return the compilation result.
    */
-  public static D8Output run(D8Command command) throws IOException, CompilationException {
-    InternalOptions options = command.getInternalOptions();
-    AndroidAppOutputSink compatSink = new AndroidAppOutputSink(command.getOutputSink());
-    CompilationResult result = runForTesting(command.getInputApp(), compatSink, options);
-    assert result != null;
-    D8Output output = new D8Output(compatSink.build(), command.getOutputMode());
-    return output;
+  public static D8Output run(D8Command command) throws CompilationFailedException {
+    ExecutorService executor = ThreadUtils.getExecutorService(command.getInternalOptions());
+    try {
+      return run(command, executor);
+    } finally {
+      executor.shutdown();
+    }
   }
 
   /**
@@ -85,15 +89,27 @@
    * @return the compilation result
    */
   public static D8Output run(D8Command command, ExecutorService executor)
-      throws IOException, CompilationException {
-    InternalOptions options = command.getInternalOptions();
-    AndroidAppOutputSink compatSink = new AndroidAppOutputSink(command.getOutputSink());
-    CompilationResult result = runForTesting(command.getInputApp(), compatSink, options, executor);
-    assert result != null;
-    return new D8Output(compatSink.build(), command.getOutputMode());
+      throws CompilationFailedException {
+    try {
+      InternalOptions options = command.getInternalOptions();
+      AndroidAppOutputSink compatSink = new AndroidAppOutputSink(command.getOutputSink());
+      CompilationResult result =
+          run(command.getInputApp(), compatSink, options, executor);
+      assert result != null;
+      return new D8Output(compatSink.build(), command.getOutputMode());
+    } catch (IOException io) {
+      command.getDiagnosticsHandler().error(new IOExceptionDiagnostic(io));
+      throw new CompilationFailedException(io);
+    } catch (CompilationException e) {
+      command.getDiagnosticsHandler().error(new StringDiagnostic(e.getMessage()));
+      throw new CompilationFailedException(e);
+    } catch (AbortException e) {
+      throw new CompilationFailedException(e);
+    }
   }
 
-  private static void run(String[] args) throws IOException, CompilationException {
+  private static void run(String[] args)
+      throws IOException, CompilationException, CompilationFailedException {
     D8Command.Builder builder = D8Command.parse(args);
     if (builder.getOutputPath() == null) {
       builder.setOutputPath(Paths.get("."));
@@ -127,6 +143,10 @@
     } catch (IOException e) {
       System.err.println("Failed to read or write application files: " + e.getMessage());
       System.exit(STATUS_ERROR);
+    } catch (CompilationFailedException | AbortException e) {
+      // Detail of the errors were already reported
+      System.err.println("Compilation failed");
+      System.exit(STATUS_ERROR);
     } catch (RuntimeException e) {
       System.err.println("Compilation failed with an internal error.");
       Throwable cause = e.getCause() == null ? e : e.getCause();
@@ -141,9 +161,9 @@
   static CompilationResult runForTesting(AndroidApp inputApp, OutputSink outputSink,
       InternalOptions options)
       throws IOException, CompilationException {
-    ExecutorService executor = ThreadUtils.getExecutorService(options);
+    ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
     try {
-      return runForTesting(inputApp, outputSink, options, executor);
+      return run(inputApp, outputSink, options, executor);
     } finally {
       executor.shutdown();
     }
@@ -163,7 +183,7 @@
     return marker;
   }
 
-  private static CompilationResult runForTesting(
+  private static CompilationResult run(
       AndroidApp inputApp, OutputSink outputSink, InternalOptions options,
       ExecutorService executor)
       throws IOException, CompilationException {
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
index 10f24f5..8212835 100644
--- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
+++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -13,6 +13,20 @@
 public interface DiagnosticsHandler {
 
   /**
+   * Handle error diagnostics.
+   *
+   * @param error Diagnostic containing error information.
+   */
+  default void error(Diagnostic error) {
+    if (error.getOrigin() != Origin.unknown()) {
+      System.err.print("Error in " + error.getOrigin() + ":\n  ");
+    } else {
+      System.err.print("Error: ");
+    }
+    System.err.println(error.getDiagnosticMessage());
+  }
+
+  /**
    * Handle warning diagnostics.
    *
    * @param warning Diagnostic containing warning information.
diff --git a/src/main/java/com/android/tools/r8/GenerateMainDexList.java b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
index d4b1ee5..e36a1a3 100644
--- a/src/main/java/com/android/tools/r8/GenerateMainDexList.java
+++ b/src/main/java/com/android/tools/r8/GenerateMainDexList.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder;
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -105,7 +106,7 @@
   }
 
   public static void main(String[] args)
-      throws IOException, CompilationException, ExecutionException {
+      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
     GenerateMainDexListCommand.Builder builder = GenerateMainDexListCommand.parse(args);
     GenerateMainDexListCommand command = builder.build();
     if (command.isPrintHelp()) {
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index c1b1096..fc05c0b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -40,10 +40,13 @@
 import com.android.tools.r8.shaking.SimpleClassMerger;
 import com.android.tools.r8.shaking.TreePruner;
 import com.android.tools.r8.shaking.protolite.ProtoLiteExtension;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppOutputSink;
 import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.CompilationFailedException;
+import com.android.tools.r8.utils.IOExceptionDiagnostic;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.LineNumberOptimizer;
@@ -123,13 +126,13 @@
       throws IOException, CompilationException {
     ExecutorService executor = ThreadUtils.getExecutorService(options);
     try {
-      runForTesting(app, outputSink, options, executor);
+      run(app, outputSink, options, executor);
     } finally {
       executor.shutdown();
     }
   }
 
-  private static void runForTesting(
+  private static void run(
       AndroidApp app,
       OutputSink outputSink,
       InternalOptions options,
@@ -393,7 +396,7 @@
    *
    * @param command R8 command.
    */
-  public static void run(R8Command command) throws IOException, CompilationException {
+  public static void run(R8Command command) throws CompilationFailedException {
     InternalOptions options = command.getInternalOptions();
     ExecutorService executorService = ThreadUtils.getExecutorService(options);
     try {
@@ -427,9 +430,16 @@
    * @param executor executor service from which to get threads for multi-threaded processing.
    */
   public static void run(R8Command command, ExecutorService executor)
-      throws IOException, CompilationException {
-    InternalOptions options = command.getInternalOptions();
-    runForTesting(command.getInputApp(), command.getOutputSink(), options, executor);
+      throws CompilationFailedException {
+    try {
+      InternalOptions options = command.getInternalOptions();
+      run(command.getInputApp(), command.getOutputSink(), options, executor);
+    } catch (IOException io) {
+      command.getDiagnosticsHandler().error(new IOExceptionDiagnostic(io));
+      throw new CompilationFailedException(io);
+    } catch (CompilationException e) {
+      throw new CompilationFailedException(e);
+    }
   }
 
   /**
@@ -439,11 +449,12 @@
       throws IOException, CompilationException {
     InternalOptions options = command.getInternalOptions();
     AndroidAppOutputSink compatSink = new AndroidAppOutputSink(command.getOutputSink());
-    runForTesting(command.getInputApp(), compatSink, options, executor);
+    run(command.getInputApp(), compatSink, options, executor);
     return compatSink.build();
   }
 
-  private static void run(String[] args) throws IOException, CompilationException {
+  private static void run(String[] args)
+      throws IOException, CompilationException, CompilationFailedException {
     R8Command.Builder builder = R8Command.parse(args);
     if (builder.getOutputPath() == null) {
       builder.setOutputPath(Paths.get("."));
@@ -457,7 +468,13 @@
       Version.printToolVersion("R8");
       return;
     }
-    run(command);
+    InternalOptions options = command.getInternalOptions();
+    ExecutorService executorService = ThreadUtils.getExecutorService(options);
+    try {
+      run(command.getInputApp(), command.getOutputSink(), options, executorService);
+    } finally {
+      executorService.shutdown();
+    }
   }
 
   public static void main(String[] args) {
@@ -471,6 +488,10 @@
     } catch (IOException e) {
       System.err.println("Failed to read or write Android app: " + e.getMessage());
       System.exit(1);
+    } catch (CompilationFailedException | AbortException e) {
+      // Detail of the errors were already reported
+      System.err.println("Compilation failed");
+      System.exit(1);
     } catch (RuntimeException e) {
       System.err.println("Compilation failed with an internal error.");
       Throwable cause = e.getCause() == null ? e : e.getCause();
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index 948178a..df3903b 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.OutputMode;
@@ -102,7 +103,7 @@
       boolean desugar,
       Map<String, Resource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     long start = System.nanoTime();
     D8Output out =
         D8.run(
@@ -134,7 +135,7 @@
       boolean desugar,
       Map<String, Resource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     descriptors.sort(String::compareTo);
     int increment = descriptors.size() / ITERATIONS;
     long start = System.nanoTime();
@@ -172,7 +173,7 @@
       boolean desugar,
       Map<String, Resource> outputs,
       ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     List<byte[]> bytes = new ArrayList<>(outputs.size());
     for (Resource input : outputs.values()) {
       try (InputStream inputStream = input.getStream()) {
@@ -195,7 +196,8 @@
     return out;
   }
 
-  public static void main(String[] args) throws IOException, CompilationException {
+  public static void main(String[] args)
+      throws IOException, CompilationException, CompilationFailedException {
     boolean desugar = Arrays.asList(args).contains("--desugar");
     Path input = desugar ? JAR_NOT_DESUGARED : JAR_DESUGARED;
     InMemoryClassPathProvider provider = new InMemoryClassPathProvider(input);
diff --git a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
index 257f814..97336ce 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/IncrementalDexingBenchmark.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.D8Output;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.ThreadUtils;
 import java.io.IOException;
 import java.nio.file.Paths;
@@ -16,7 +17,8 @@
 public class IncrementalDexingBenchmark {
   private static final int ITERATIONS = 1000;
 
-  public static void compile(ExecutorService executor) throws IOException, CompilationException {
+  public static void compile(ExecutorService executor)
+      throws IOException, CompilationException, CompilationFailedException {
     D8Output output =
         D8.run(
             D8Command.builder()
@@ -29,7 +31,8 @@
     }
   }
 
-  public static void main(String[] args) throws IOException, CompilationException {
+  public static void main(String[] args)
+      throws IOException, CompilationException, CompilationFailedException {
     int threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
     ExecutorService executor = ThreadUtils.getExecutorService(threads);
     try {
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index 407147b..8972a96 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Output;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.io.ByteStreams;
 import java.io.IOException;
@@ -130,7 +131,7 @@
   }
 
   private D8Output dexEntry(ZipFile zipFile, ZipEntry classEntry, ExecutorService executor)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     try (InputStream stream = zipFile.getInputStream(classEntry)) {
       CompatDexBuilderCommandBuilder builder = new CompatDexBuilderCommandBuilder();
       builder
diff --git a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
index 500cff3..261cef0 100644
--- a/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
+++ b/src/main/java/com/android/tools/r8/compatdx/CompatDx.java
@@ -23,6 +23,7 @@
 import com.android.tools.r8.errors.Unimplemented;
 import com.android.tools.r8.logging.Log;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.ImmutableList;
@@ -317,10 +318,13 @@
       System.err.println(USAGE_HEADER);
       e.printHelpOn(System.err);
       System.exit(1);
+    } catch (CompilationFailedException e) {
+      System.exit(1);
     }
   }
 
-  private static void run(String[] args) throws DxUsageMessage, IOException, CompilationException {
+  private static void run(String[] args)
+      throws DxUsageMessage, IOException, CompilationException, CompilationFailedException {
     DxCompatOptions dexArgs = DxCompatOptions.parse(args);
     if (dexArgs.help) {
       printHelpOn(System.out);
diff --git a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
index b6d9fc0..335ded4 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -9,7 +9,9 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.Version;
 import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.utils.AbortException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Paths;
@@ -119,7 +121,8 @@
     }
   }
 
-  private static void run(String[] args) throws IOException, CompilationException {
+  private static void run(String[] args)
+      throws IOException, CompilationException, CompilationFailedException {
     if (args.length == 0) {
       Version.printToolVersion("CompatProguard");
       return;
@@ -153,6 +156,10 @@
     } catch (CompilationException e) {
       System.err.println(e.getMessage());
       System.exit(1);
+    } catch (CompilationFailedException | AbortException e) {
+      // Detail of the errors were already reported
+      System.err.println("Compilation failed");
+      System.exit(1);
     }
   }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/AbortException.java b/src/main/java/com/android/tools/r8/utils/AbortException.java
new file mode 100644
index 0000000..897e022
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/AbortException.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2017, 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.utils;
+
+/**
+ * Exception thrown to interrupt processing after a fatal error. The exception doesn't carry
+ * directly information about the fatal error: the problem was already reported to the
+ * {@link com.android.tools.r8.DiagnosticsHandler}.
+ */
+public class AbortException extends RuntimeException {
+
+}
diff --git a/src/main/java/com/android/tools/r8/utils/CompilationFailedException.java b/src/main/java/com/android/tools/r8/utils/CompilationFailedException.java
new file mode 100644
index 0000000..b21e02f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/CompilationFailedException.java
@@ -0,0 +1,20 @@
+// Copyright (c) 2017, 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.utils;
+
+/**
+ * Exception thrown when compilation failed to complete because of errors previously reported
+ * through {@link com.android.tools.r8.DiagnosticsHandler}.
+ */
+public class CompilationFailedException extends Exception {
+
+  public CompilationFailedException() {
+    super("Compilation failed to complete");
+  }
+
+  public CompilationFailedException(Throwable cause) {
+    super("Compilation failed to complete", cause);
+  }
+
+}
diff --git a/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
new file mode 100644
index 0000000..da439fe
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DiagnosticWithThrowable.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2017, 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.utils;
+
+import com.android.tools.r8.Diagnostic;
+
+public abstract class DiagnosticWithThrowable implements Diagnostic {
+
+  private final Throwable throwable;
+
+  protected DiagnosticWithThrowable(Throwable throwable) {
+    assert throwable != null;
+    this.throwable = throwable;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java b/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
new file mode 100644
index 0000000..8738c89
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IOExceptionDiagnostic.java
@@ -0,0 +1,56 @@
+// Copyright (c) 2017, 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.utils;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.origin.PathOrigin;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.FileSystemException;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Paths;
+
+public class IOExceptionDiagnostic extends DiagnosticWithThrowable {
+
+  private final Origin origin;
+  private final String message;
+
+  public IOExceptionDiagnostic(IOException e) {
+    super(e);
+    Origin origin = Origin.unknown();
+
+    if (e instanceof FileSystemException) {
+      FileSystemException fse = (FileSystemException) e;
+      if (fse.getFile() != null && !fse.getFile().isEmpty()) {
+        origin = new PathOrigin(Paths.get(fse.getFile()), Origin.root());
+      }
+    }
+
+    String message = e.getMessage();
+    if (message == null || message.isEmpty()) {
+      if (e instanceof NoSuchFileException || e instanceof FileNotFoundException) {
+        message = "File not found";
+      } else if (e instanceof FileAlreadyExistsException) {
+        message = "File already exists";
+      }
+    }
+
+    this.origin = origin;
+    this.message = message;
+
+  }
+
+  @Override
+  public Origin getOrigin() {
+    return origin;
+  }
+
+  @Override
+  public String getDiagnosticMessage() {
+    return message;
+  }
+
+}
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 24df0e4..408776f 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -54,8 +54,6 @@
     itemFactory = proguardConfiguration.getDexItemFactory();
   }
 
-  public final int NOT_SPECIFIED = -1;
-
   public boolean printTimes = false;
 
   public boolean outputClassFiles = false;
@@ -69,7 +67,7 @@
   public boolean propagateMemberValue = true;
 
   // Number of threads to use while processing the dex files.
-  public int numberOfThreads = NOT_SPECIFIED;
+  public int numberOfThreads = ThreadUtils.NOT_SPECIFIED;
   // Print smali disassembly.
   public boolean useSmaliSyntax = false;
   // Verbose output.
diff --git a/src/main/java/com/android/tools/r8/utils/Reporter.java b/src/main/java/com/android/tools/r8/utils/Reporter.java
new file mode 100644
index 0000000..a6c5367
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/Reporter.java
@@ -0,0 +1,69 @@
+// Copyright (c) 2017, 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.utils;
+
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class Reporter implements DiagnosticsHandler {
+
+  private final DiagnosticsHandler clientHandler;
+  private int errorCount = 0;
+  private final Collection<Throwable> suppressedExceptions = new ArrayList<>();
+
+  public Reporter(DiagnosticsHandler clientHandler) {
+    this.clientHandler = clientHandler;
+  }
+
+  @Override
+  public void info(Diagnostic info) {
+    clientHandler.info(info);
+  }
+
+  @Override
+  public void warning(Diagnostic warning) {
+    clientHandler.warning(warning);
+  }
+
+  @Override
+  public void error(Diagnostic error) {
+    clientHandler.error(error);
+    synchronized (this) {
+      errorCount++;
+    }
+  }
+  public void error(String message) {
+    error(new StringDiagnostic(message));
+  }
+
+  public void error(Diagnostic error, Throwable suppressedException) {
+    clientHandler.error(error);
+    synchronized (this) {
+      errorCount++;
+      suppressedExceptions.add(suppressedException);
+    }
+  }
+
+  public void fatalError(Diagnostic error) throws AbortException {
+    error(error);
+    failIfPendingErrors();
+  }
+
+  public void fatalError(Diagnostic error, Throwable suppressedException) throws AbortException {
+    error(error, suppressedException);
+    failIfPendingErrors();
+  }
+
+  public void failIfPendingErrors() throws AbortException {
+    synchronized (this) {
+      if (errorCount != 0) {
+        AbortException abort = new AbortException();
+        suppressedExceptions.stream().forEach(throwable -> abort.addSuppressed(throwable));
+        throw abort;
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index e5172e9..01eb3f6 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -11,6 +11,8 @@
 
 public class ThreadUtils {
 
+  public static final int NOT_SPECIFIED = -1;
+
   public static void awaitFutures(Iterable<? extends Future<?>> futures)
       throws ExecutionException {
     Iterator<? extends Future<?>> it = futures.iterator();
@@ -35,16 +37,16 @@
   }
 
   public static ExecutorService getExecutorService(int threads) {
+    if (threads == NOT_SPECIFIED) {
+      // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
+      threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
+    }
+
     // Don't use Executors.newSingleThreadExecutor() when threads == 1, see b/67338394.
     return Executors.newWorkStealingPool(threads);
   }
 
   public static ExecutorService getExecutorService(InternalOptions options) {
-    int threads = options.numberOfThreads;
-    if (threads == options.NOT_SPECIFIED) {
-      // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
-      threads = Integer.min(Runtime.getRuntime().availableProcessors(), 16) / 2;
-    }
-    return getExecutorService(threads);
+    return getExecutorService(options.numberOfThreads);
   }
 }
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
index ff61567..5d19bc8 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.OutputMode;
 import java.io.IOException;
 import java.nio.file.FileVisitResult;
@@ -109,7 +110,7 @@
   }
 
   private void merge(Path outputDir, Path mainDexClasses,
-      List<Path> toMerge) throws IOException, CompilationException {
+      List<Path> toMerge) throws IOException, CompilationException, CompilationFailedException {
     D8Command.Builder merger = D8Command.builder();
     merger.setEnableDesugaring(false);
 
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 45939db..1c68d75 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -11,6 +11,7 @@
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DirectoryClassFileProvider;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -122,7 +123,7 @@
   }
 
   private AndroidApp mergeDexResources(int minAPILevel, List<Resource> individalDexes)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     D8Command.Builder builder = D8Command.builder()
         .setMinApiLevel(minAPILevel);
     for (Resource resource : individalDexes) {
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index 5ad82de..60be850 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -5,6 +5,7 @@
 package com.android.tools.r8;
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -131,7 +132,8 @@
     Assert.assertTrue(Files.isRegularFile(testFlags.getParent().resolve(SEEDS)));
   }
 
-  private R8Command getCommand(Path out) throws CompilationException, IOException {
+  private R8Command getCommand(Path out)
+      throws CompilationException, IOException, CompilationFailedException {
     return R8Command.builder()
       .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
       .addProgramFiles(INPUT_JAR)
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index 1ce3a43..5eb7168 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.ArtErrorParser;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.ListUtils;
@@ -1311,7 +1312,8 @@
       String resultPath,
       CompilationMode compilationMode,
       boolean disableInlining)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultPath, compilationMode, null,
         disableInlining);
   }
@@ -1323,7 +1325,8 @@
       CompilationMode mode,
       String keepRulesFile,
       boolean disableInlining)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+        CompilationFailedException {
     assert mode != null;
     switch (compilerUnderTest) {
       case D8: {
@@ -1436,7 +1439,8 @@
 
   protected void runJctfTest(CompilerUnderTest compilerUnderTest, String classFilePath,
       String fullClassName)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      CompilationFailedException {
 
     DexVm dexVm = ToolHelper.getDexVm();
 
@@ -1558,7 +1562,8 @@
       CompilationMode mode,
       DexVm dexVm,
       File resultDir)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      CompilationFailedException {
     executeCompilerUnderTest(compilerUnderTest, fileNames, resultDir.getAbsolutePath(), mode,
         specification.disableInlining);
 
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index e0aeaeb..b03b6f4 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -238,8 +238,7 @@
   public ExpectedException thrown = ExpectedException.none();
 
   @Before
-  public void compile()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void compile() throws Exception {
     if (output == Output.CF && failingCompileCf.contains(mainClass)) {
       thrown.expect(Throwable.class);
     }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 60bcc5b..7cd4833 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.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
@@ -135,7 +136,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app)
-      throws CompilationException, ExecutionException, IOException {
+      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
     D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
     return ToolHelper.runD8(command);
   }
@@ -144,7 +145,7 @@
    * Compile an application with D8.
    */
   protected AndroidApp compileWithD8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ExecutionException, IOException {
+      throws CompilationException, ExecutionException, IOException, CompilationFailedException {
     D8Command command = ToolHelper.prepareD8CommandBuilder(app).build();
     return ToolHelper.runD8(command, optionsConsumer);
   }
@@ -153,7 +154,8 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(Class... classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command);
   }
@@ -162,7 +164,8 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command);
   }
@@ -171,7 +174,8 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(readClasses(classes)).build();
     return ToolHelper.runR8(command, optionsConsumer);
   }
@@ -180,7 +184,8 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command);
   }
@@ -189,7 +194,8 @@
    * Compile an application with R8.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command = ToolHelper.prepareR8CommandBuilder(app).build();
     return ToolHelper.runR8(command, optionsConsumer);
   }
@@ -198,7 +204,8 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
 
@@ -207,7 +214,8 @@
    */
   protected AndroidApp compileWithR8(
       List<Class> classes, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig, optionsConsumer);
   }
 
@@ -215,7 +223,8 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(List<Class> classes, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     return compileWithR8(readClasses(classes), proguardConfig);
   }
 
@@ -223,7 +232,8 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, Path proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
             .addProguardConfigurationFiles(proguardConfig)
@@ -235,7 +245,8 @@
    * Compile an application with R8 using the supplied proguard configuration.
    */
   protected AndroidApp compileWithR8(AndroidApp app, String proguardConfig)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     return compileWithR8(app, proguardConfig, null);
   }
 
@@ -244,7 +255,8 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, String proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
             .addProguardConfiguration(ImmutableList.of(proguardConfig))
@@ -257,7 +269,8 @@
    */
   protected AndroidApp compileWithR8(
       AndroidApp app, Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException {
+      throws CompilationException, ProguardRuleParserException, ExecutionException, IOException,
+      CompilationFailedException {
     R8Command command =
         ToolHelper.prepareR8CommandBuilder(app)
             .addProguardConfigurationFiles(proguardConfig)
diff --git a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
index 261fd71..e214f5d 100644
--- a/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/ClassMergingTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
@@ -40,7 +41,8 @@
   }
 
   private void runR8(Path proguardConfig, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException,
+      CompilationFailedException {
     ToolHelper.runR8(
         R8Command.builder()
             .setOutputPath(Paths.get(temp.getRoot().getCanonicalPath()))
@@ -63,8 +65,7 @@
   );
 
   @Test
-  public void testClassesHaveBeenMerged()
-      throws IOException, ExecutionException, CompilationException, ProguardRuleParserException {
+  public void testClassesHaveBeenMerged() throws Exception {
     runR8(EXAMPLE_KEEP, this::configure);
     // GenericInterface should be merged into GenericInterfaceImpl.
     for (String candidate : CAN_BE_MERGED) {
@@ -77,8 +78,7 @@
   }
 
   @Test
-  public void testClassesShouldNotMerged()
-      throws IOException, ExecutionException, CompilationException, ProguardRuleParserException {
+  public void testClassesShouldNotMerged() throws Exception {
     runR8(DONT_OPTIMIZE, null);
     for (String candidate : CAN_BE_MERGED) {
       assertTrue(inspector.clazz(candidate).isPresent());
@@ -86,16 +86,14 @@
   }
 
   @Test
-  public void testConflictWasDetected()
-      throws IOException, ExecutionException, CompilationException, ProguardRuleParserException {
+  public void testConflictWasDetected() throws Exception {
     runR8(EXAMPLE_KEEP, this::configure);
     assertTrue(inspector.clazz("classmerging.ConflictingInterface").isPresent());
     assertTrue(inspector.clazz("classmerging.ConflictingInterfaceImpl").isPresent());
   }
 
   @Test
-  public void testSuperCallWasDetected()
-      throws IOException, ExecutionException, CompilationException, ProguardRuleParserException {
+  public void testSuperCallWasDetected() throws Exception {
     runR8(EXAMPLE_KEEP, this::configure);
     assertTrue(inspector.clazz("classmerging.SuperClassWithReferencedMethod").isPresent());
     assertTrue(inspector.clazz("classmerging.SubClassThatReferencesSuperMethod").isPresent());
diff --git a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
index 9b538b0..c3a2743 100644
--- a/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/d8/D8FrameworkDexPassthroughMarkerTest.java
@@ -55,7 +55,7 @@
   }
 
   @Test
-  public void compile() throws CompilationException, IOException, ExecutionException {
+  public void compile() throws Exception {
     D8Command command = D8Command.builder()
         .setMinApiLevel(AndroidApiLevel.N.getLevel())
         .addProgramFiles(FRAMEWORK_JAR)
diff --git a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
index ebf75f2..9a4e28b 100644
--- a/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
+++ b/src/test/java/com/android/tools/r8/d8/DexVersionTests.java
@@ -3,15 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.d8;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.D8Output;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import org.junit.Before;
@@ -35,7 +32,7 @@
   @Rule public TemporaryFolder androidNApiFolder2 = ToolHelper.getTemporaryFolderForTest();
 
   @Before
-  public void compileVersions() throws CompilationException, IOException {
+  public void compileVersions() throws Exception {
     D8Command.Builder arithmeticBuilder = D8Command.builder().addProgramFiles(ARITHMETIC_JAR);
     D8Command.Builder arrayAccessBuilder = D8Command.builder().addProgramFiles(ARRAYACCESS_JAR);
     D8Output output = D8.run(arrayAccessBuilder.build());
@@ -77,7 +74,7 @@
   }
 
   @Test
-  public void mergeCompatibleVersions() throws CompilationException, IOException {
+  public void mergeCompatibleVersions() throws Exception {
     // Verify that we can merge between all versions when no explicit min sdk version is set.
     D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(default2()).build());
     D8.run(D8Command.builder().addProgramFiles(default1()).addProgramFiles(androidO2()).build());
@@ -154,7 +151,7 @@
   }
 
   @Test(expected = CompilationError.class)
-  public void mergeErrorVersionNWithVersionOInput() throws CompilationException, IOException {
+  public void mergeErrorVersionNWithVersionOInput() throws Exception {
     D8.run(
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.N.getLevel())
@@ -164,7 +161,7 @@
   }
 
   @Test(expected = CompilationError.class)
-  public void mergeErrorVersionKWithVersionOInput() throws CompilationException, IOException {
+  public void mergeErrorVersionKWithVersionOInput() throws Exception {
     D8.run(
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.K.getLevel())
@@ -174,7 +171,7 @@
   }
 
   @Test(expected = CompilationError.class)
-  public void mergeErrorVersionKWithVersionNInput() throws CompilationException, IOException {
+  public void mergeErrorVersionKWithVersionNInput() throws Exception {
     D8.run(
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.K.getLevel())
diff --git a/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java b/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java
index 8892cfc..d541e3e 100644
--- a/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java
+++ b/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java
@@ -3,11 +3,9 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.d8;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
-import java.io.IOException;
 import java.nio.file.Paths;
 import org.junit.Rule;
 import org.junit.Test;
@@ -22,7 +20,7 @@
   @Rule public TemporaryFolder jarFolder = ToolHelper.getTemporaryFolderForTest();
 
   @Test
-  public void writeToZip() throws IOException, CompilationException {
+  public void writeToZip() throws Exception {
     D8Command command =
         D8Command.builder()
             .addProgramFiles(Paths.get(input))
@@ -32,7 +30,7 @@
   }
 
   @Test
-  public void writeToJar() throws IOException, CompilationException {
+  public void writeToJar() throws Exception {
     D8Command command =
         D8Command.builder()
             .addProgramFiles(Paths.get(input))
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 44c9dd4..3376cb8 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -3,10 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import java.io.IOException;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -22,7 +20,7 @@
   private static DebuggeePath debuggeePath;
 
   @BeforeClass
-  public static void initDebuggeePath() throws IOException, CompilationException {
+  public static void initDebuggeePath() throws Exception {
     // Force inversion of all conditionals to reliably construct a regression test for incorrect
     // line information when reordering blocks.
     debuggeePath =
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 8db3bf8..3a332b1 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.shaking.ProguardConfiguration;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -157,7 +158,8 @@
     debuggeeKotlinDexD8 = null;
   }
 
-  protected static synchronized Path getDebuggeeDexD8() throws IOException, CompilationException {
+  protected static synchronized Path getDebuggeeDexD8()
+      throws IOException, CompilationException, CompilationFailedException {
     if (debuggeeDexD8 == null) {
       debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, null);
     }
@@ -165,7 +167,7 @@
   }
 
   private static synchronized Path getDebuggeeJava8DexD8()
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     if (debuggeeJava8DexD8 == null) {
       debuggeeJava8DexD8 =
           compileToDex(
@@ -179,7 +181,7 @@
   }
 
   private static synchronized Path getDebuggeeKotlinDexD8()
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     if (debuggeeKotlinDexD8 == null) {
       debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, null);
     }
@@ -187,25 +189,25 @@
   }
 
   protected static DebuggeePath getDebuggeeDexD8OrCf(boolean cf)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     return cf ? DebuggeePath.makeClassFile(DEBUGGEE_JAR) : DebuggeePath.makeDex(getDebuggeeDexD8());
   }
 
   protected static DebuggeePath getDebuggeeJava8DexD8OrCf(boolean cf)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     return cf
         ? DebuggeePath.makeClassFile(DEBUGGEE_JAVA8_JAR)
         : DebuggeePath.makeDex(getDebuggeeJava8DexD8());
   }
 
   protected static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     return compileToDex(DEX_COMPILER_KIND, jarToCompile, optionsConsumer);
   }
 
   static Path compileToDex(
       DexCompilerKind compiler, Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
     Path dexOutputDir = temp.newFolder().toPath();
@@ -248,7 +250,8 @@
       List<String> proguardConfigurations,
       boolean writeProguardMap,
       CompilationMode compilationMode)
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
+      CompilationFailedException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
     Path dexOutputDir = temp.newFolder().toPath();
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 1419c7f..1e439c7 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.TestBase.MinifyMode;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
@@ -55,7 +56,8 @@
   private final Config config;
 
   private synchronized DebuggeePath getDebuggeePath()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
+      CompilationFailedException {
     Path path = debuggeePathMap.get(config);
     if (path == null) {
       List<String> proguardConfigurations = Collections.<String>emptyList();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
index e80a301..36f1b29 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.OutputMode;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -29,7 +30,8 @@
   @Rule
   public TemporaryFolder temp = new TemporaryFolder();
 
-  static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException {
+  static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException,
+      CompilationFailedException {
     D8Command.Builder builder = D8Command.builder();
     for (Class clazz : classes) {
       builder.addProgramFiles(ToolHelper.getClassFileForTestClass(clazz));
diff --git a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
index 59da13f..1f99cf0 100644
--- a/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/BasicTestDependenciesDesugaringTest.java
@@ -3,10 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.desugar;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.OffOrAuto;
@@ -79,7 +77,7 @@
   }
 
   @Test
-  public void testCompile() throws IOException, CompilationException {
+  public void testCompile() throws Exception {
     if (knownIssues.contains(name)) {
       thrown.expect(CompilationError.class);
     }
@@ -93,7 +91,7 @@
   }
 
   @Test
-  public void testCompileDontDesugarDefault() throws IOException, CompilationException {
+  public void testCompileDontDesugarDefault() throws Exception {
     if (knownIssues.contains(name)) {
       thrown.expect(CompilationError.class);
     }
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
index 3e4d129..4b285ff 100644
--- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.utils.ArtErrorParser;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorInfo;
 import com.android.tools.r8.utils.ArtErrorParser.ArtErrorParserException;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
@@ -52,7 +53,8 @@
       String pgMap,
       String pgConf,
       String... inputs)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException{
     return runAndCheckVerification(
         compiler, mode, referenceApk, pgMap, pgConf, null, Arrays.asList(inputs));
   }
@@ -70,7 +72,8 @@
       String pgConf,
       Consumer<InternalOptions> optionsConsumer,
       List<String> inputs)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException {
     assertTrue(referenceApk == null || new File(referenceApk).exists());
     AndroidApp outputApp;
     if (compiler == CompilerUnderTest.R8) {
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java
index 04c1d2e..6a17351 100644
--- a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV10DeployJarVerificationTest.java
@@ -3,27 +3,21 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class D8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
 
   @Test
-  public void buildDebugFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromDeployJar() throws Exception {
     buildFromDeployJar(
         CompilerUnderTest.D8, CompilationMode.DEBUG,
         GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
   }
 
   @Test
-  public void buildReleaseFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromDeployJar() throws Exception {
     buildFromDeployJar(
         CompilerUnderTest.D8, CompilationMode.RELEASE,
         GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
diff --git a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
index 2e076d5..9a1d91d 100644
--- a/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8GMSCoreV9DeployJarVerificationTest.java
@@ -14,16 +14,14 @@
 public class D8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
 
   @Test
-  public void buildDebugFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromDeployJar() throws Exception {
     buildFromDeployJar(
         CompilerUnderTest.D8, CompilationMode.DEBUG,
         GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
   }
 
   @Test
-  public void buildReleaseFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromDeployJar() throws Exception {
     buildFromDeployJar(
         CompilerUnderTest.D8, CompilationMode.RELEASE,
         GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
diff --git a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
index de72fb2..6246470 100644
--- a/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8PhotosVerificationTest.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.CompilationFailedException;
 import java.io.IOException;
 import java.util.concurrent.ExecutionException;
 import org.junit.Test;
@@ -16,14 +17,14 @@
       "third_party/photos/2017-06-06/PhotosEnglishOnlyLegacy_proguard.jar";
 
   public void runD8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      CompilationFailedException {
     runAndCheckVerification(
         CompilerUnderTest.D8, mode, version, null, null, version);
   }
 
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runD8AndCheckVerification(CompilationMode.RELEASE, PHOTOS);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
index 5e226bb..99b49c4 100644
--- a/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/D8YouTubeDeployJarVerificationTest.java
@@ -14,15 +14,13 @@
 public class D8YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  public void buildDebugFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromDeployJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.D8, CompilationMode.DEBUG, BASE + APK, null, null, BASE + DEPLOY_JAR);
   }
 
   @Test
-  public void buildReleaseFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromDeployJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.D8, CompilationMode.RELEASE, BASE + APK, null, null, BASE + DEPLOY_JAR);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
index 1e91e0d..0db3100 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreCompilationTestBase.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
+import com.android.tools.r8.utils.CompilationFailedException;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.concurrent.ExecutionException;
@@ -32,13 +33,15 @@
   static final String REFERENCE_APK = "noshrink_x86_GmsCore_prod_alldpi_release_unsigned.apk";
 
   public void runR8AndCheckVerification(CompilationMode mode, String version)
-      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException {
+      throws ProguardRuleParserException, ExecutionException, IOException, CompilationException,
+      CompilationFailedException {
     runAndCheckVerification(CompilerUnderTest.R8, mode, version);
   }
 
   public void runAndCheckVerification(
       CompilerUnderTest compiler, CompilationMode mode, String version)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException {
     runAndCheckVerification(
         compiler, mode, version + GMSCORE_APK, null, null,
         Paths.get(version, GMSCORE_APK).toString());
diff --git a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
index 1f37499..ca8dc24 100644
--- a/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/GMSCoreDeployJarVerificationTest.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.InternalOptions;
 import java.io.IOException;
 import java.util.Collections;
@@ -18,7 +19,8 @@
 
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, hasReference ? base + REFERENCE_APK : null, null, null, base + DEPLOY_JAR);
   }
@@ -27,7 +29,8 @@
   public AndroidApp buildFromDeployJar(
       CompilerUnderTest compiler, CompilationMode mode, String base, boolean hasReference,
       Consumer<InternalOptions> optionsConsumer)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException {
     return runAndCheckVerification(
         compiler, mode, hasReference ? base + REFERENCE_APK : null, null, null,
         optionsConsumer, Collections.singletonList(base + DEPLOY_JAR));
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
index 02e301e..598d8d9 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreDeterministicTest.java
@@ -11,6 +11,7 @@
 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.CompilationFailedException;
 import com.android.tools.r8.utils.OutputMode;
 import com.beust.jcommander.internal.Lists;
 import java.io.IOException;
@@ -32,7 +33,8 @@
   }
 
   private AndroidApp doRun()
-      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException {
+      throws IOException, ProguardRuleParserException, CompilationException, ExecutionException,
+      CompilationFailedException {
     R8Command command =
         R8Command.builder().addProgramFiles(Paths.get(GMSCORE_V7_DIR, GMSCORE_APK)).build();
     return ToolHelper.runR8(
@@ -46,9 +48,7 @@
   }
 
   @Test
-  public void deterministic()
-      throws ExecutionException, IOException, ProguardRuleParserException, InterruptedException,
-          CompilationException {
+  public void deterministic() throws Exception {
 
     // Run two independent compilations.
     AndroidApp app1 = doRun();
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
index 8c72ddd..07afead 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreTreeShakeJarVerificationTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
 import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -21,7 +22,8 @@
   public AndroidApp buildAndTreeShakeFromDeployJar(
       CompilationMode mode, String base, boolean hasReference, int maxSize,
       Consumer<InternalOptions> optionsConsumer)
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException,
+      CompilationFailedException {
     AndroidApp app = runAndCheckVerification(
         CompilerUnderTest.R8,
         mode,
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
index ca713fd..81512e9 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV10DeployJarVerificationTest.java
@@ -16,9 +16,8 @@
 public class R8GMSCoreV10DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
 
   @Test
-  public void buildFromDeployJar()
-      // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildFromDeployJar() throws Exception {
+    // TODO(tamaskenez): set hasReference = true when we have the noshrink file for V10
     AndroidApp app1 = buildFromDeployJar(
         CompilerUnderTest.R8, CompilationMode.RELEASE,
         GMSCoreCompilationTestBase.GMSCORE_V10_DIR, false);
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
index 0405587..964d4de 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV4VerificationTest.java
@@ -12,8 +12,7 @@
 
 public class R8GMSCoreV4VerificationTest extends GMSCoreCompilationTestBase {
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V4_DIR);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
index 17aab5e..d999b07 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV5VerificationTest.java
@@ -12,8 +12,7 @@
 
 public class R8GMSCoreV5VerificationTest extends GMSCoreCompilationTestBase {
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V5_DIR);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
index d34ad90..9b8642d 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV6VerificationTest.java
@@ -12,8 +12,7 @@
 
 public class R8GMSCoreV6VerificationTest extends GMSCoreCompilationTestBase {
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V6_DIR);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
index 2f530f4..2cda255 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV7VerificationTest.java
@@ -3,18 +3,13 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class R8GMSCoreV7VerificationTest extends GMSCoreCompilationTestBase {
 
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V7_DIR);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
index 128bd17..1c7fb97 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV8VerificationTest.java
@@ -13,8 +13,7 @@
 public class R8GMSCoreV8VerificationTest extends GMSCoreCompilationTestBase {
 
   @Test
-  public void verify()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void verify() throws Exception {
     runR8AndCheckVerification(CompilationMode.RELEASE, GMSCORE_V8_DIR);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
index bc8c2b6..fb06e84 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9DeployJarVerificationTest.java
@@ -4,19 +4,14 @@
 
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class R8GMSCoreV9DeployJarVerificationTest extends GMSCoreDeployJarVerificationTest {
 
   @Test
-  public void buildFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildFromDeployJar() throws Exception {
     buildFromDeployJar(
         CompilerUnderTest.R8, CompilationMode.RELEASE,
         GMSCoreCompilationTestBase.GMSCORE_V9_DIR, true);
diff --git a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
index df7944f..59cf378 100644
--- a/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/R8GMSCoreV9TreeShakeJarVerificationTest.java
@@ -13,8 +13,7 @@
 public class R8GMSCoreV9TreeShakeJarVerificationTest extends R8GMSCoreTreeShakeJarVerificationTest {
 
   @Test
-  public void buildAndTreeShakeFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildAndTreeShakeFromDeployJar() throws Exception {
     buildAndTreeShakeFromDeployJar(
         CompilationMode.RELEASE, GMSCORE_V9_DIR, true, GMSCORE_V9_MAX_SIZE, null);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
index 7fbfb2e..90e81cc 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDeployJarVerificationTest.java
@@ -3,26 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class YouTubeDeployJarVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  public void buildDebugFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromDeployJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.DEBUG, BASE + APK, null, null, BASE + DEPLOY_JAR);
   }
 
   @Test
-  public void buildReleaseFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromDeployJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.RELEASE, BASE + APK, null, null, BASE + DEPLOY_JAR);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
index ba816f1..3f8fc41 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeDexVerificationTest.java
@@ -14,15 +14,13 @@
 public class YouTubeDexVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  public void buildDebugFromDex()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromDex() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.DEBUG, BASE + APK, BASE + PG_MAP, null, BASE + APK);
   }
 
   @Test
-  public void buildReleaseFromDex()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromDex() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.RELEASE, BASE + APK, BASE + PG_MAP, null, BASE + APK);
   }
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
index 4bbceeb..45b0617 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeProguardJarVerificationTest.java
@@ -3,26 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.internal;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.R8RunArtTestsTest.CompilerUnderTest;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
 import org.junit.Test;
 
 public class YouTubeProguardJarVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  public void buildDebugFromProguardJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildDebugFromProguardJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.DEBUG, BASE + APK, BASE + PG_MAP, null, BASE + PG_JAR);
   }
 
   @Test
-  public void buildReleaseFromProguardJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildReleaseFromProguardJar() throws Exception {
     runAndCheckVerification(
         CompilerUnderTest.R8, CompilationMode.RELEASE,
         BASE + APK, BASE + PG_MAP, null, BASE + PG_JAR);
diff --git a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
index 3b42d0e..94af915 100644
--- a/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
+++ b/src/test/java/com/android/tools/r8/internal/YouTubeTreeShakeJarVerificationTest.java
@@ -21,8 +21,7 @@
 public class YouTubeTreeShakeJarVerificationTest extends YouTubeCompilationBase {
 
   @Test
-  public void buildAndTreeShakeFromDeployJar()
-      throws ExecutionException, IOException, ProguardRuleParserException, CompilationException {
+  public void buildAndTreeShakeFromDeployJar() throws Exception {
     int maxSize = 20000000;
     AndroidApp app = runAndCheckVerification(
         CompilerUnderTest.R8,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
index 5b1c92a..9b8f688 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/MemberValuePropagationTest.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.code.SputObject;
 import com.android.tools.r8.graph.DexCode;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.FileUtils;
@@ -95,7 +96,8 @@
     });
   }
 
-  private Path runR8(Path proguardConfig) throws IOException, CompilationException {
+  private Path runR8(Path proguardConfig)
+      throws IOException, CompilationException, CompilationFailedException {
     Path dexOutputDir = temp.newFolder().toPath();
     ToolHelper.runR8(
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index d7c40d4..18bfcdd 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -15,6 +15,7 @@
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import java.io.File;
@@ -236,7 +237,7 @@
   public static TemporaryFolder temp = new TemporaryFolder();
 
   @BeforeClass
-  public static void compileLibraries() throws IOException, CompilationException {
+  public static void compileLibraries() throws Exception {
     // Selects appropriate jar according to min api level for the selected runtime.
     int minApi = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     Path jdwpTestsJar = ToolHelper.getJdwpTestsJarPath(minApi);
diff --git a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
index 6306f91..7610c19 100644
--- a/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
+++ b/src/test/java/com/android/tools/r8/jsr45/JSR45Tests.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexValue.DexValueString;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.AnnotationSubject;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
@@ -47,7 +48,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   private AndroidApp compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     return ToolHelper.runD8(
         D8Command.builder()
             .setMinApiLevel(AndroidApiLevel.O.getLevel())
@@ -57,7 +58,7 @@
   }
 
   private AndroidApp compileWithR8(Path inputPath, Path outputPath, Path keepRulesPath)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     return R8.runInternal(
         R8Command.builder()
             .addProgramFiles(inputPath)
@@ -83,8 +84,7 @@
   }
 
   @Test
-  public void testSourceDebugExtensionWithD8()
-      throws IOException, CompilationException, ExecutionException {
+  public void testSourceDebugExtensionWithD8() throws Exception {
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
     AndroidApp result = compileWithD8(INPUT_PATH, outputPath);
@@ -93,8 +93,7 @@
 
   /** Check that when dontshrink and dontobfuscate is used the annotation is transmitted. */
   @Test
-  public void testSourceDebugExtensionWithShrinking1()
-      throws IOException, CompilationException, ExecutionException {
+  public void testSourceDebugExtensionWithShrinking1() throws Exception {
     Path outputPath = tmpOutputDir.newFolder().toPath();
     AndroidApp result = compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_DONT_OBFUSCATE_CONFIG);
     checkAnnotationContent(INPUT_PATH, result);
@@ -104,8 +103,7 @@
    * Check that when dontshrink is used the annotation is not removed due to obfuscation.
    */
   @Test
-  public void testSourceDebugExtensionWithShrinking2()
-      throws IOException, CompilationException, ExecutionException {
+  public void testSourceDebugExtensionWithShrinking2() throws Exception {
     Path outputPath = tmpOutputDir.newFolder().toPath();
     AndroidApp result = compileWithR8(INPUT_PATH, outputPath, DONT_SHRINK_CONFIG);
     checkAnnotationContent(INPUT_PATH, result);
@@ -115,8 +113,7 @@
    * Check that the annotation is transmitted when shrinking is enabled with a keepattribute option.
    */
   @Test
-  public void testSourceDebugExtensionWithShrinking3()
-      throws IOException, CompilationException, ExecutionException {
+  public void testSourceDebugExtensionWithShrinking3() throws Exception {
     Path outputPath = tmpOutputDir.newFolder().toPath();
     AndroidApp result = compileWithR8(INPUT_PATH, outputPath, SHRINK_KEEP_CONFIG);
     checkAnnotationContent(INPUT_PATH, result);
@@ -127,8 +124,7 @@
    * keepattributes option.
    */
   @Test
-  public void testSourceDebugExtensionWithShrinking4()
-      throws IOException, CompilationException, ExecutionException {
+  public void testSourceDebugExtensionWithShrinking4() throws Exception {
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
     compileWithR8(INPUT_PATH, outputPath, SHRINK_NO_KEEP_CONFIG);
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 2929996..a2cdd12 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -50,6 +50,7 @@
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppOutputSink;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FoundClassSubject;
@@ -474,7 +475,8 @@
   private void doVerifyMainDexContains(
       List<String> mainDex, Path app, boolean singleDexApp, boolean minimalMainDex,
       MultiDexTestMode testMode)
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
+      CompilationFailedException {
     AndroidApp originalApp = AndroidApp.fromProgramFiles(app);
     DexInspector originalInspector = new DexInspector(originalApp);
     for (String clazz : mainDex) {
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
index 22876ff..f0a5a38 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingTest.java
@@ -6,12 +6,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.naming.MemberNaming.MethodSignature;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.FieldAccessInstructionSubject;
@@ -77,8 +74,7 @@
   }
 
   @Before
-  public void runR8()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void runR8() throws Exception {
     // Generate R8 processed version without library option.
     String out = temp.getRoot().getCanonicalPath();
     // NOTE: It is important to turn off inlining to ensure
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 dac440a..41370d4 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -58,8 +58,7 @@
   }
 
   @Test
-  public void test044_obfuscate_and_apply()
-      throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+  public void test044_obfuscate_and_apply() throws Exception {
     // keep rules that allow obfuscations while keeping everything.
     Path flagForObfuscation =
         Paths.get(ToolHelper.EXAMPLES_DIR, "naming044", "keep-rules-005.txt");
@@ -121,8 +120,7 @@
   }
 
   @Test
-  public void test044_apply()
-      throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+  public void test044_apply() throws Exception {
     Path flag =
         Paths.get(ToolHelper.EXAMPLES_DIR, "applymapping044", "keep-rules-apply-mapping.txt");
     AndroidApp outputApp = runR8(
@@ -165,8 +163,7 @@
   }
 
   @Test
-  public void test_naming001_rule105()
-      throws IOException, CompilationException, ProguardRuleParserException, ExecutionException {
+  public void test_naming001_rule105() throws Exception {
     // keep rules to reserve D and E, along with a proguard map.
     Path flag = Paths.get(ToolHelper.EXAMPLES_DIR, "naming001", "keep-rules-105.txt");
     Path proguardMap = out.resolve(MAPPING);
@@ -196,8 +193,7 @@
   }
 
   @Test
-  public void test_minification_conflict_mapping()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+  public void test_minification_conflict_mapping() throws Exception {
     Path flag = Paths.get(
         ToolHelper.EXAMPLES_DIR, "minification", "keep-rules-apply-conflict-mapping.txt");
     try {
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
index b8e2ccc..6e46183 100644
--- a/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierMinifierTest.java
@@ -6,7 +6,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.code.ConstString;
@@ -15,7 +14,6 @@
 import com.android.tools.r8.graph.Code;
 import com.android.tools.r8.graph.DexEncodedField;
 import com.android.tools.r8.graph.DexValue.DexValueString;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DexInspector;
@@ -23,7 +21,6 @@
 import com.android.tools.r8.utils.DexInspector.MethodSubject;
 import com.android.tools.r8.utils.ListUtils;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Arrays;
@@ -31,7 +28,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import org.junit.Before;
 import org.junit.Rule;
@@ -61,8 +57,7 @@
   }
 
   @Before
-  public void generateR8ProcessedApp()
-      throws IOException, ExecutionException, ProguardRuleParserException, CompilationException {
+  public void generateR8ProcessedApp() throws Exception {
     Path out = temp.getRoot().toPath();
     R8Command command =
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
index 75892cb..4f0f5b5 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -24,8 +24,7 @@
   private static DebuggeePath debuggeePath;
 
   @BeforeClass
-  public static void initDebuggeePath()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException {
+  public static void initDebuggeePath() throws Exception {
     debuggeePath =
         DebuggeePath.makeDex(
             compileToDexViaR8(
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
index d5642f8..d8fcc09 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/LongCompare.java
@@ -8,6 +8,7 @@
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
@@ -19,7 +20,6 @@
 import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.Iterator;
-import java.util.concurrent.ExecutionException;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -34,7 +34,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     D8.run(D8Command.builder().addProgramFiles(intputPath).setOutputPath(outputPath).build());
   }
 
@@ -53,8 +53,7 @@
   }
 
   @Test
-  public void testLongCompareIsRewritten()
-      throws IOException, CompilationException, ExecutionException {
+  public void testLongCompareIsRewritten() throws Exception {
     final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
index 4cbae16..5508d50 100644
--- a/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/longcompare/RequireNonNullRewriteTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.DexInspector;
 import com.android.tools.r8.utils.DexInspector.ClassSubject;
 import com.android.tools.r8.utils.DexInspector.InstructionSubject;
@@ -33,7 +34,7 @@
   public TemporaryFolder tmpOutputDir = ToolHelper.getTemporaryFolderForTest();
 
   void compileWithD8(Path intputPath, Path outputPath, CompilationMode mode)
-      throws IOException, CompilationException {
+      throws IOException, CompilationException, CompilationFailedException {
     D8.run(
         D8Command.builder()
             .setMode(mode)
@@ -58,19 +59,17 @@
   }
 
   @Test
-  public void testDebugRequireNonNullIsRewritten()
-      throws IOException, CompilationException, ExecutionException {
+  public void testDebugRequireNonNullIsRewritten() throws Exception {
     runTest(CompilationMode.DEBUG);
   }
 
   @Test
-  public void testReleaseRequireNonNullIsRewritten()
-      throws IOException, CompilationException, ExecutionException {
+  public void testReleaseRequireNonNullIsRewritten() throws Exception {
     runTest(CompilationMode.RELEASE);
   }
 
   private void runTest(CompilationMode mode)
-      throws IOException, CompilationException, ExecutionException {
+      throws IOException, CompilationException, ExecutionException, CompilationFailedException {
     final Path inputPath = Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "/rewrite.jar");
     Path outputPath = tmpOutputDir.newFolder().toPath();
 
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index 104ba43..87d3c7a 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -25,8 +25,7 @@
           "-dontobfuscate";
 
   @Test
-  public void checkSwitchMapsRemoved()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void checkSwitchMapsRemoved() throws Exception {
     AndroidApp.Builder builder = AndroidApp.builder();
     builder.addLibraryFiles(
         FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()));
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 4cb6736..4d0f341 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.shaking.PrintUsageTest.PrintUsageInspector.ClassSubject;
@@ -63,8 +62,7 @@
   }
 
   @Before
-  public void runR8andGetPrintUsage()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void runR8andGetPrintUsage() throws Exception {
     Path out = temp.getRoot().toPath();
     R8Command command =
         R8Command.builder()
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
index 081b083..ae3554b 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingTest.java
@@ -5,7 +5,6 @@
 
 import static com.android.tools.r8.utils.AndroidApp.DEFAULT_PROGUARD_MAP_FILE;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase.MinifyMode;
 import com.android.tools.r8.ToolHelper;
@@ -127,8 +126,7 @@
   }
 
   @Before
-  public void generateTreeShakedVersion()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void generateTreeShakedVersion() throws Exception {
     // Generate R8 processed version without library option.
     Path out = temp.getRoot().toPath();
     boolean inline = programFile.contains("inlining");
diff --git a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
index 941ba84..8f207c5 100644
--- a/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/D8CommandTest.java
@@ -48,7 +48,7 @@
     verifyEmptyCommand(parse("\t", "\t"));
   }
 
-  private void verifyEmptyCommand(D8Command command) throws IOException {
+  private void verifyEmptyCommand(D8Command command) throws Throwable {
     assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
     assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
     assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
@@ -59,7 +59,7 @@
   }
 
   @Test
-  public void defaultOutIsCwd() throws IOException, InterruptedException {
+  public void defaultOutIsCwd() throws Throwable {
     Path working = temp.getRoot().toPath();
     Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
     Path output = working.resolve("classes.dex");
@@ -246,7 +246,8 @@
     D8Command.builder().addProgramFiles(vdexFile).build();
   }
 
-  private D8Command parse(String... args) throws IOException, CompilationException {
+  private D8Command parse(String... args)
+      throws IOException, CompilationException, CompilationFailedException {
     return D8Command.parse(args).build();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
index 6dda185..2d55e9f 100644
--- a/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8CommandTest.java
@@ -50,7 +50,7 @@
     verifyEmptyCommand(parse("\t", "\t"));
   }
 
-  private void verifyEmptyCommand(R8Command command) throws IOException {
+  private void verifyEmptyCommand(R8Command command) throws Throwable {
     assertEquals(0, ToolHelper.getApp(command).getDexProgramResources().size());
     assertEquals(0, ToolHelper.getApp(command).getClassProgramResources().size());
     assertFalse(ToolHelper.getApp(command).hasMainDexListResources());
@@ -63,7 +63,7 @@
   }
 
   @Test
-  public void defaultOutIsCwd() throws IOException, InterruptedException {
+  public void defaultOutIsCwd() throws Throwable {
     Path working = temp.getRoot().toPath();
     Path input = Paths.get(EXAMPLES_BUILD_DIR, "arithmetic.jar").toAbsolutePath();
     Path output = working.resolve("classes.dex");
@@ -247,7 +247,8 @@
   }
 
   private R8Command parse(String... args)
-      throws CompilationException, ProguardRuleParserException, IOException {
+      throws CompilationException, ProguardRuleParserException, IOException,
+      CompilationFailedException {
     return R8Command.parse(args).build();
   }
 }
diff --git a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
index 7af5708..4e212c7 100644
--- a/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/utils/R8InliningTest.java
@@ -76,8 +76,7 @@
   public ExpectedException thrown = ExpectedException.none();
 
   @Before
-  public void generateR8Version()
-      throws IOException, ProguardRuleParserException, ExecutionException, CompilationException {
+  public void generateR8Version() throws Exception {
     Path out = temp.getRoot().toPath();
     R8Command command =
         R8Command.builder()
diff --git a/tests/api_usage_sample.jar b/tests/api_usage_sample.jar
index b8b9d66..c20643c 100644
--- a/tests/api_usage_sample.jar
+++ b/tests/api_usage_sample.jar
Binary files differ