Remove uses of System.exit

Bug: 177180774
Change-Id: I0be80edf95b72e50594998e8bb55b2bdda0f918f
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index cae27a3..bc8d186 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -40,6 +40,7 @@
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.DesugarState;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -142,8 +143,7 @@
    */
   public static void main(String[] args) {
     if (args.length == 0) {
-      System.err.println(USAGE_MESSAGE);
-      System.exit(ExceptionUtils.STATUS_ERROR);
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index d705c8e..98a6582 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -103,9 +103,9 @@
     D8Command command = parse(args, Origin.root()).build();
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
-      System.exit(1);
+    } else {
+      D8.run(command);
     }
-    D8.run(command);
   }
 
   static final String USAGE_MESSAGE =
diff --git a/src/main/java/com/android/tools/r8/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
index da40e79..740984c 100644
--- a/src/main/java/com/android/tools/r8/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/GenerateLintFiles.java
@@ -46,6 +46,7 @@
 import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.Sets;
 import java.io.File;
@@ -794,9 +795,10 @@
       new GenerateLintFiles(args[1], args[2], args[3]).generateDesugaredLibraryApisDocumetation();
       return;
     }
-    System.out.println(
-        "Usage: GenerateLineFiles [--generate-api-docs] "
-            + "<desugar configuration> <desugar implementation> <output directory>");
-    System.exit(1);
+    throw new RuntimeException(
+        StringUtils.joinLines(
+            "Invalid invocation.",
+            "Usage: GenerateLineFiles [--generate-api-docs] "
+                + "<desugar configuration> <desugar implementation> <output directory>"));
   }
 }
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 40f48d2..f610d83 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -26,6 +26,7 @@
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.SelfRetraceTest;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
@@ -186,8 +187,7 @@
    */
   public static void main(String[] args) {
     if (args.length == 0) {
-      System.err.println(USAGE_MESSAGE);
-      System.exit(ExceptionUtils.STATUS_ERROR);
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
diff --git a/src/main/java/com/android/tools/r8/L8CommandParser.java b/src/main/java/com/android/tools/r8/L8CommandParser.java
index 0a57389..3dac9dc 100644
--- a/src/main/java/com/android/tools/r8/L8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/L8CommandParser.java
@@ -25,9 +25,9 @@
     L8Command command = parse(args, Origin.root()).build();
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
-      System.exit(1);
+    } else {
+      L8.run(command);
     }
-    L8.run(command);
   }
 
   static final String USAGE_MESSAGE =
diff --git a/src/main/java/com/android/tools/r8/PrintSeeds.java b/src/main/java/com/android/tools/r8/PrintSeeds.java
index b8a426c..212eb32 100644
--- a/src/main/java/com/android/tools/r8/PrintSeeds.java
+++ b/src/main/java/com/android/tools/r8/PrintSeeds.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.shaking.RootSetBuilder.RootSet;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import java.io.IOException;
@@ -39,21 +40,19 @@
 public class PrintSeeds {
 
   private static final String USAGE =
-      "Arguments: <rt.jar> <r8.jar> <pg-conf.txt>\n"
-          + "\n"
-          + "PrintSeeds prints the classes, interfaces, methods and fields selected by\n"
-          + "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.\n"
-          + "\n"
-          + "The output format is identical to what is printed when -printseeds is specified in\n"
-          + "<pg-conf.txt>, but running PrintSeeds can be faster than running R8 with \n"
-          + "-printseeds. See also the "
-          + PrintUses.class.getSimpleName()
-          + " program in R8.";
+      StringUtils.joinLines(
+          "Arguments: <rt.jar> <r8.jar> <pg-conf.txt>",
+          "",
+          "PrintSeeds prints the classes, interfaces, methods and fields selected by",
+          "<pg-conf.txt> when compiling <r8.jar> alongside <rt.jar>.",
+          "",
+          "The output format is identical to what is printed when -printseeds is specified in",
+          "<pg-conf.txt>, but running PrintSeeds can be faster than running R8 with",
+          "-printseeds. See also the " + PrintUses.class.getSimpleName() + " program in R8.");
 
   public static void main(String[] args) throws Exception {
     if (args.length != 3) {
-      System.out.println(USAGE.replace("\n", System.lineSeparator()));
-      System.exit(1);
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE));
     }
     Path rtJar = Paths.get(args[0]);
     Path r8Jar = Paths.get(args[1]);
diff --git a/src/main/java/com/android/tools/r8/PrintUses.java b/src/main/java/com/android/tools/r8/PrintUses.java
index df58bfa..ef8708f 100644
--- a/src/main/java/com/android/tools/r8/PrintUses.java
+++ b/src/main/java/com/android/tools/r8/PrintUses.java
@@ -61,17 +61,18 @@
 public class PrintUses {
 
   private static final String USAGE =
-      "Arguments: [--keeprules, --keeprules-allowobfuscation] <rt.jar> <r8.jar> <sample.jar>\n"
-          + "\n"
-          + "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,\n"
-          + "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.\n"
-          + "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.\n"
-          + "\n"
-          + "The output is in the same format as what is printed when specifying -printseeds in\n"
-          + "a ProGuard configuration file. Use --keeprules or --keeprules-allowobfuscation for "
-          + "outputting proguard keep rules. See also the "
-          + PrintSeeds.class.getSimpleName()
-          + " program in R8.";
+      StringUtils.joinLines(
+          "Arguments: [--keeprules, --keeprules-allowobfuscation] <rt.jar> <r8.jar> <sample.jar>",
+          "",
+          "PrintUses prints the classes, interfaces, methods and fields used by <sample.jar>,",
+          "restricted to classes and interfaces in <r8.jar> that are not in <sample.jar>.",
+          "<rt.jar> and <r8.jar> should point to libraries used by <sample.jar>.",
+          "",
+          "The output is in the same format as what is printed when specifying -printseeds in",
+          "a ProGuard configuration file. Use --keeprules or --keeprules-allowobfuscation for",
+          "outputting proguard keep rules. See also the "
+              + PrintSeeds.class.getSimpleName()
+              + " program in R8.");
 
   private final Set<String> descriptors;
   private final Printer printer;
@@ -306,8 +307,7 @@
 
   public static void main(String... args) throws Exception {
     if (args.length != 3 && args.length != 4 && args.length != 5) {
-      System.out.println(USAGE.replace("\n", System.lineSeparator()));
-      return;
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE));
     }
     int argumentIndex = 0;
     boolean printKeep = false;
@@ -319,9 +319,9 @@
       // Make sure there is only one argument that mentions --keeprules
       for (int i = 1; i < args.length; i++) {
         if (args[i].startsWith("-keeprules")) {
-          System.out.println("Use either --keeprules or --keeprules-allowobfuscation, not both.");
-          System.out.println(USAGE.replace("\n", System.lineSeparator()));
-          return;
+          throw new RuntimeException(
+              StringUtils.joinLines(
+                  "Use either --keeprules or --keeprules-allowobfuscation, not both.", USAGE));
         }
       }
     }
@@ -343,8 +343,7 @@
     printUses.analyze();
     printUses.print();
     if (printUses.errors > 0) {
-      System.err.println(printUses.errors + " errors");
-      System.exit(1);
+      throw new RuntimeException(printUses.errors + " errors");
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 1dc771e..9e7a967 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -117,6 +117,7 @@
 import com.android.tools.r8.utils.LineNumberOptimizer;
 import com.android.tools.r8.utils.SelfRetraceTest;
 import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
@@ -1130,8 +1131,7 @@
    */
   public static void main(String[] args) {
     if (args.length == 0) {
-      System.err.println(USAGE_MESSAGE);
-      System.exit(ExceptionUtils.STATUS_ERROR);
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index 3cb01e1..277bec8 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -42,9 +42,9 @@
     R8Command command = parse(args, Origin.root()).build();
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
-      System.exit(1);
+    } else {
+      R8.run(command);
     }
-    R8.run(command);
   }
 
   // Internal state to verify parsing properties not enforced by the builder.
diff --git a/src/main/java/com/android/tools/r8/ReadMainDexList.java b/src/main/java/com/android/tools/r8/ReadMainDexList.java
index de34761..4061e68 100644
--- a/src/main/java/com/android/tools/r8/ReadMainDexList.java
+++ b/src/main/java/com/android/tools/r8/ReadMainDexList.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.Iterators;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -40,8 +41,9 @@
 
   private void run(String[] args) throws Exception {
     if (args.length < 1 || args.length > 3) {
-      System.out.println("Usage: command [-k] <main_dex_list> [<proguard_map>]");
-      System.exit(0);
+      throw new RuntimeException(
+          StringUtils.joinLines(
+              "Invalid invocation.", "Usage: command [-k] <main_dex_list> [<proguard_map>]"));
     }
 
     Iterator<String> arguments = Iterators.forArray(args);
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 177f2ae..24b42d3 100644
--- a/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
+++ b/src/main/java/com/android/tools/r8/compatproguard/CompatProguard.java
@@ -12,7 +12,7 @@
 import com.android.tools.r8.Version;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.origin.CommandLineOrigin;
-import com.android.tools.r8.utils.AbortException;
+import com.android.tools.r8.utils.ExceptionUtils;
 import com.google.common.collect.ImmutableList;
 import java.nio.file.Paths;
 import java.util.List;
@@ -211,12 +211,6 @@
   }
 
   public static void main(String[] args) {
-    try {
-      run(args);
-    } catch (CompilationFailedException | AbortException e) {
-      // Detail of the errors were already reported
-      System.err.println("Compilation failed");
-      System.exit(1);
-    }
+    ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
 }
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 d9a5609..4ee416c 100644
--- a/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
+++ b/src/main/java/com/android/tools/r8/dexsplitter/DexSplitter.java
@@ -362,8 +362,7 @@
             run(args);
           } catch (FeatureMappingException e) {
             // TODO(ricow): Report feature mapping errors via the reporter.
-            System.err.println("Splitting failed: " + e.getMessage());
-            System.exit(1);
+            throw new RuntimeException("Splitting failed: " + e.getMessage());
           }
         });
   }
diff --git a/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
index f255aee..dc11daa 100644
--- a/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
+++ b/src/main/java/com/android/tools/r8/relocator/RelocatorCommandLine.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.Version;
 import com.android.tools.r8.origin.CommandLineOrigin;
 import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.StringUtils;
 
 public class RelocatorCommandLine {
 
@@ -21,8 +22,7 @@
    */
   public static void main(String[] args) {
     if (args.length == 0) {
-      System.err.println(USAGE_MESSAGE);
-      System.exit(ExceptionUtils.STATUS_ERROR);
+      throw new RuntimeException(StringUtils.joinLines("Invalid invocation.", USAGE_MESSAGE));
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
diff --git a/src/main/java/com/android/tools/r8/retrace/Retrace.java b/src/main/java/com/android/tools/r8/retrace/Retrace.java
index d202954..0d66a64 100644
--- a/src/main/java/com/android/tools/r8/retrace/Retrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/Retrace.java
@@ -5,7 +5,6 @@
 package com.android.tools.r8.retrace;
 
 import static com.android.tools.r8.retrace.internal.RetraceUtils.firstNonWhiteSpaceCharacterFromIndex;
-import static com.android.tools.r8.utils.ExceptionUtils.STATUS_ERROR;
 import static com.android.tools.r8.utils.ExceptionUtils.failWithFakeEntry;
 
 import com.android.tools.r8.Diagnostic;
@@ -358,13 +357,9 @@
       action.run();
     } catch (RetraceFailedException | RetraceAbortException e) {
       // Detail of the errors were already reported
-      System.err.println("Retrace failed");
-      System.exit(STATUS_ERROR);
-      throw null;
+      throw new RuntimeException("Retrace failed", e);
     } catch (Throwable t) {
-      System.err.println("Retrace failed with an internal error.");
-      t.printStackTrace();
-      System.exit(STATUS_ERROR);
+      throw new RuntimeException("Retrace failed with an internal error.", t);
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index 8c5cce7..b5c6375 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -16,6 +16,7 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.StringUtils;
 import com.android.tools.r8.utils.Timing;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
@@ -92,8 +93,8 @@
    */
   public static void main(String[] args) {
     if (args.length == 0) {
-      System.err.println(TraceReferencesCommandParser.USAGE_MESSAGE);
-      System.exit(ExceptionUtils.STATUS_ERROR);
+      throw new RuntimeException(
+          StringUtils.joinLines("Invalid invocation.", TraceReferencesCommandParser.USAGE_MESSAGE));
     }
     ExceptionUtils.withMainProgramHandler(() -> run(args));
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
index 61aa232..8c4da03 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -25,8 +25,6 @@
 
 public abstract class ExceptionUtils {
 
-  public static final int STATUS_ERROR = 1;
-
   public static void withConsumeResourceHandler(
       Reporter reporter, StringConsumer consumer, String data) {
     withConsumeResourceHandler(reporter, handler -> consumer.accept(data, handler));
@@ -176,23 +174,19 @@
     try {
       action.run();
     } catch (CompilationFailedException e) {
-      throw exitWithError(e, e.getCause());
+      printExitMessage(e.getCause());
+      throw new RuntimeException(e);
     } catch (RuntimeException e) {
-      throw exitWithError(e, e);
+      printExitMessage(e);
+      throw e;
     }
   }
 
-  private static RuntimeException exitWithError(Throwable e, Throwable cause) {
-    if (isExpectedException(cause)) {
-      // Detail of the errors were already reported
-      System.err.println("Compilation failed");
-      System.exit(STATUS_ERROR);
-      throw null;
-    }
-    System.err.println("Compilation failed with an internal error.");
-    e.printStackTrace();
-    System.exit(STATUS_ERROR);
-    throw null;
+  private static void printExitMessage(Throwable cause) {
+    System.err.println(
+        isExpectedException(cause)
+            ? "Compilation failed"
+            : "Compilation failed with an internal error.");
   }
 
   private static boolean isExpectedException(Throwable e) {