Merge "Use R8 error handling utilities in DexSplitter."
diff --git a/src/main/java/com/android/tools/r8/DexSplitterHelper.java b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
index ab86367..1fcd70a 100644
--- a/src/main/java/com/android/tools/r8/DexSplitterHelper.java
+++ b/src/main/java/com/android/tools/r8/DexSplitterHelper.java
@@ -14,6 +14,7 @@
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FeatureClassMapping;
 import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
 import com.android.tools.r8.utils.InternalOptions;
@@ -33,14 +34,14 @@
 public class DexSplitterHelper {
 
   public static void run(
-      D8Command command,
-      FeatureClassMapping featureClassMapping,
-      String output,
-      String proguardMap)
-      throws IOException, CompilationException, ExecutionException {
+      D8Command command, FeatureClassMapping featureClassMapping, String output, String proguardMap)
+      throws CompilationFailedException {
     ExecutorService executor = ThreadUtils.getExecutorService(ThreadUtils.NOT_SPECIFIED);
     try {
-      run(command, featureClassMapping, output, proguardMap, executor);
+      ExceptionUtils.withCompilationHandler(
+          command.getReporter(),
+          () -> run(command, featureClassMapping, output, proguardMap, executor),
+          CompilationException::getMessage);
     } finally {
       executor.shutdown();
     }
@@ -52,7 +53,7 @@
       String output,
       String proguardMap,
       ExecutorService executor)
-      throws IOException, CompilationException, ExecutionException {
+      throws IOException, CompilationException {
     InternalOptions options = command.getInternalOptions();
     options.enableDesugaring = false;
     options.enableMainDexListCheck = false;
diff --git a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
index b2fe1bc..f452cf1 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -4,23 +4,25 @@
 
 package com.android.tools.r8.dexsplitter;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationFailedException;
 import com.android.tools.r8.D8Command;
 import com.android.tools.r8.DexIndexedConsumer;
 import com.android.tools.r8.DexSplitterHelper;
-import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.Diagnostic;
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.DefaultDiagnosticsHandler;
+import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FeatureClassMapping;
 import com.android.tools.r8.utils.FeatureClassMapping.FeatureMappingException;
 import com.android.tools.r8.utils.OptionsParsing;
 import com.android.tools.r8.utils.OptionsParsing.ParseContext;
+import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
 
 public class DexSplitter {
 
@@ -29,6 +31,37 @@
 
   private static final boolean PRINT_ARGS = false;
 
+  public static class Reporter implements DiagnosticsHandler {
+
+    private final DiagnosticsHandler handler = new DefaultDiagnosticsHandler();
+    private boolean reportedErrors = false;
+
+    public RuntimeException fatal(Diagnostic error) {
+      error(error);
+      throw new AbortException();
+    }
+
+    public boolean hasReportedErrors() {
+      return reportedErrors;
+    }
+
+    @Override
+    public void error(Diagnostic error) {
+      handler.error(error);
+      reportedErrors = true;
+    }
+
+    @Override
+    public void warning(Diagnostic warning) {
+      handler.warning(warning);
+    }
+
+    @Override
+    public void info(Diagnostic info) {
+      handler.info(info);
+    }
+  }
+
   public static class FeatureJar {
     private String jar;
     private String outputName;
@@ -62,6 +95,7 @@
   }
 
   public static class Options {
+    private final Reporter reporter = new Reporter();
     private List<String> inputArchives = new ArrayList<>();
     private List<FeatureJar> featureJars = new ArrayList<>();
     private String baseOutputName = DEFAULT_BASE_NAME;
@@ -69,6 +103,10 @@
     private String featureSplitMapping;
     private String proguardMap;
 
+    public Reporter getReporter() {
+      return reporter;
+    }
+
     public String getOutput() {
       return output;
     }
@@ -124,6 +162,11 @@
     public ImmutableList<FeatureJar> getFeatureJars() {
       return ImmutableList.copyOf(featureJars);
     }
+
+    // Shorthand error messages.
+    public void error(String msg) {
+      reporter.error(new StringDiagnostic(msg));
+    }
   }
 
   /**
@@ -144,7 +187,7 @@
     return new FeatureJar(argument);
   }
 
-  private static Options parseArguments(String[] args) throws IOException {
+  private static Options parseArguments(String[] args) {
     Options options = new Options();
     ParseContext context = new ParseContext(args);
     while (context.head() != null) {
@@ -185,32 +228,35 @@
   }
 
   private static FeatureClassMapping createFeatureClassMapping(Options options)
-      throws IOException, FeatureMappingException, ResourceException {
+      throws FeatureMappingException {
     if (options.getFeatureSplitMapping() != null) {
-      return FeatureClassMapping.fromSpecification(Paths.get(options.getFeatureSplitMapping()));
+      return FeatureClassMapping.fromSpecification(
+          Paths.get(options.getFeatureSplitMapping()), options.getReporter());
     }
     assert !options.getFeatureJars().isEmpty();
-    return FeatureClassMapping.fromJarFiles(options.getFeatureJars(), options.getBaseOutputName());
+    return FeatureClassMapping.fromJarFiles(
+        options.getFeatureJars(), options.getBaseOutputName(), options.getReporter());
   }
 
   private static void run(String[] args)
-      throws CompilationFailedException, IOException, CompilationException, ExecutionException,
-          ResourceException, FeatureMappingException {
+      throws CompilationFailedException, FeatureMappingException {
     Options options = parseArguments(args);
     run(options);
   }
 
   public static void run(Options options)
-      throws IOException, FeatureMappingException, ResourceException, CompilationException,
-      ExecutionException, CompilationFailedException {
+      throws FeatureMappingException, CompilationFailedException {
     if (options.getInputArchives().isEmpty()) {
-      throw new RuntimeException("Need at least one --input");
+      options.error("Need at least one --input");
     }
     if (options.getFeatureSplitMapping() == null && options.getFeatureJars().isEmpty()) {
-      throw new RuntimeException("You must supply a feature split mapping or feature jars");
+      options.error("You must supply a feature split mapping or feature jars");
     }
     if (options.getFeatureSplitMapping() != null && !options.getFeatureJars().isEmpty()) {
-      throw new RuntimeException("You can't supply both a feature split mapping and feature jars");
+      options.error("You can't supply both a feature split mapping and feature jars");
+    }
+    if (options.getReporter().hasReportedErrors()) {
+      throw new AbortException();
     }
 
     D8Command.Builder builder = D8Command.builder();
@@ -228,20 +274,19 @@
   }
 
   public static void main(String[] args) {
-    try {
-      if (PRINT_ARGS) {
-        printArgs(args);
-      }
-      run(args);
-    } catch (CompilationFailedException
-        | IOException
-        | CompilationException
-        | ExecutionException
-        | ResourceException
-        | FeatureMappingException e) {
-      System.err.println("Splitting failed: " + e.getMessage());
-      System.exit(1);
+    if (PRINT_ARGS) {
+      printArgs(args);
     }
+    ExceptionUtils.withMainProgramHandler(
+        () -> {
+          try {
+            run(args);
+          } catch (FeatureMappingException e) {
+            // TODO(ricow): Report feature mapping errors via the reporter.
+            System.err.println("Splitting failed: " + e.getMessage());
+            System.exit(1);
+          }
+        });
   }
 
   private static void printArgs(String[] args) {
diff --git a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
index 1c969f6..e8c6df9 100644
--- a/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
+++ b/src/main/java/com/android/tools/r8/utils/FeatureClassMapping.java
@@ -4,7 +4,9 @@
 package com.android.tools.r8.utils;
 
 import com.android.tools.r8.ArchiveClassFileProvider;
+import com.android.tools.r8.dexsplitter.DexSplitter;
 import com.android.tools.r8.dexsplitter.DexSplitter.FeatureJar;
+import com.android.tools.r8.origin.PathOrigin;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -51,11 +53,39 @@
   static final String COMMENT = "#";
   static final String SEPARATOR = ":";
 
+  private static class SpecificationOrigin extends PathOrigin {
 
-  public static FeatureClassMapping fromSpecification(Path file)
-      throws FeatureMappingException, IOException {
+    public SpecificationOrigin(Path path) {
+      super(path);
+    }
+
+    @Override
+    public String part() {
+      return "specification file '" + super.part() + "'";
+    }
+  }
+
+  private static class JarFileOrigin extends PathOrigin {
+
+    public JarFileOrigin(Path path) {
+      super(path);
+    }
+
+    @Override
+    public String part() {
+      return "jar file '" + super.part() + "'";
+    }
+  }
+
+  public static FeatureClassMapping fromSpecification(Path file, DexSplitter.Reporter reporter)
+      throws FeatureMappingException {
     FeatureClassMapping mapping = new FeatureClassMapping();
-    List<String> lines = FileUtils.readAllLines(file);
+    List<String> lines = null;
+    try {
+      lines = FileUtils.readAllLines(file);
+    } catch (IOException e) {
+      throw reporter.fatal(new ExceptionDiagnostic(e, new SpecificationOrigin(file)));
+    }
     for (int i = 0; i < lines.size(); i++) {
       String line = lines.get(i);
       mapping.parseAndAdd(line, i);
@@ -63,15 +93,21 @@
     return mapping;
   }
 
-  public static FeatureClassMapping fromJarFiles(List<FeatureJar> featureJars, String baseName)
-      throws FeatureMappingException, IOException {
+  public static FeatureClassMapping fromJarFiles(
+      List<FeatureJar> featureJars, String baseName, DexSplitter.Reporter reporter)
+      throws FeatureMappingException {
     FeatureClassMapping mapping = new FeatureClassMapping();
     if (mapping.baseName != null) {
       mapping.baseName = baseName;
     }
     for (FeatureJar featureJar : featureJars) {
       Path jarPath = Paths.get(featureJar.getJar());
-      ArchiveClassFileProvider provider = new ArchiveClassFileProvider(jarPath);
+      ArchiveClassFileProvider provider = null;
+      try {
+        provider = new ArchiveClassFileProvider(jarPath);
+      } catch (IOException e) {
+        throw reporter.fatal(new ExceptionDiagnostic(e, new JarFileOrigin(jarPath)));
+      }
       for (String javaDescriptor : provider.getClassDescriptors()) {
           String javaType = DescriptorUtils.descriptorToJavaType(javaDescriptor);
           mapping.addMapping(javaType, featureJar.getOutputName());
@@ -94,7 +130,7 @@
     }
   }
 
-  public String featureForClass(String clazz) throws FeatureMappingException {
+  public String featureForClass(String clazz) {
     if (usesOnlyExactMappings) {
       return parsedRules.getOrDefault(clazz, baseName);
     } else {