Merge "Add simple flag file support to D8 and R8."
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 1db187e..250f2e9 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -17,6 +17,7 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
@@ -93,8 +94,9 @@
         });
   }
 
-  private static void run(String[] args) throws CompilationFailedException {
-    D8Command command = D8Command.parse(args, CommandLineOrigin.INSTANCE).build();
+  private static void run(String[] args) throws CompilationFailedException, IOException {
+    String[] expandedArgs = FlagFile.expandFlagFiles(args);
+    D8Command command = D8Command.parse(expandedArgs, CommandLineOrigin.INSTANCE).build();
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
       return;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 53e2f87..8e1722e 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -51,6 +51,7 @@
 import com.android.tools.r8.utils.CfgPrinter;
 import com.android.tools.r8.utils.ExceptionUtils;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.FlagFile;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
 import com.android.tools.r8.utils.LineNumberOptimizer;
@@ -89,7 +90,7 @@
  * <p>R8 supports some configuration using configuration files mostly compatible with the format of
  * the <a href="https://www.guardsquare.com/en/proguard">ProGuard</a> optimizer.
  *
- * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {@link
+ * <p>The compiler is invoked by calling {@link #run(R8Command) R8.run} with an appropriate {link
  * R8Command}. For example:
  *
  * <pre>
@@ -533,8 +534,9 @@
     }
   }
 
-  private static void run(String[] args) throws CompilationFailedException {
-    R8Command command = R8Command.parse(args, CommandLineOrigin.INSTANCE).build();
+  private static void run(String[] args) throws CompilationFailedException, IOException {
+    String[] expandedArgs = FlagFile.expandFlagFiles(args);
+    R8Command command = R8Command.parse(expandedArgs, CommandLineOrigin.INSTANCE).build();
     if (command.isPrintHelp()) {
       System.out.println(USAGE_MESSAGE);
       return;
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index dafa35a..ced17d4 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -23,7 +23,6 @@
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.io.InputStream;
 import java.nio.file.Path;
 import java.nio.file.Paths;
@@ -581,29 +580,6 @@
         builder.addProguardConfigurationFiles(Paths.get(args[++i]));
       } else if (arg.equals("--pg-map-output")) {
         builder.setProguardMapOutputPath(Paths.get(args[++i]));
-      } else if (arg.startsWith("@")) {
-        // TODO(zerny): Replace this with pipe reading.
-        Path argsFile = Paths.get(arg.substring(1));
-        Origin argsFileOrigin = new PathOrigin(argsFile);
-        try {
-          List<String> linesInFile = FileUtils.readAllLines(argsFile);
-          List<String> argsInFile = new ArrayList<>();
-          for (String line : linesInFile) {
-            for (String word : line.split("\\s")) {
-              String trimmed = word.trim();
-              if (!trimmed.isEmpty()) {
-                argsInFile.add(trimmed);
-              }
-            }
-          }
-          // TODO(zerny): We need to define what CWD should be for files referenced in an args file.
-          state = parse(argsInFile.toArray(new String[argsInFile.size()]),
-              argsFileOrigin, builder, state);
-        } catch (IOException e) {
-          builder.getReporter().error(new StringDiagnostic(
-              "Failed to read arguments from file " + argsFile + ": " + e.getMessage(),
-              argsFileOrigin));
-        }
       } else {
         if (arg.startsWith("--")) {
           builder.getReporter().error(new StringDiagnostic("Unknown option: " + arg, argsOrigin));
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 9c7f525..577632a 100644
--- a/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ExceptionUtils.java
@@ -74,7 +74,7 @@
   }
 
   public interface MainAction {
-    void run() throws CompilationFailedException;
+    void run() throws CompilationFailedException, IOException;
   }
 
   public static void withMainProgramHandler(MainAction action) {
@@ -84,7 +84,7 @@
       // Detail of the errors were already reported
       System.err.println("Compilation failed");
       System.exit(STATUS_ERROR);
-    } catch (RuntimeException e) {
+    } catch (RuntimeException  | IOException e) {
       System.err.println("Compilation failed with an internal error.");
       Throwable cause = e.getCause() == null ? e : e.getCause();
       cause.printStackTrace();
diff --git a/src/main/java/com/android/tools/r8/utils/FlagFile.java b/src/main/java/com/android/tools/r8/utils/FlagFile.java
new file mode 100644
index 0000000..2354066
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/FlagFile.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlagFile {
+  public static String[] expandFlagFiles(String[] args) throws IOException {
+    List<String> flags = new ArrayList<>(args.length);
+    for (String arg : args) {
+      if (arg.startsWith("@")) {
+        flags.addAll(Files.readAllLines(Paths.get(arg.substring(1))));
+      } else {
+        flags.add(arg);
+      }
+    }
+    return flags.toArray(new String[flags.size()]);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/D8CommandTest.java b/src/test/java/com/android/tools/r8/D8CommandTest.java
index a11c960..5576058 100644
--- a/src/test/java/com/android/tools/r8/D8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/D8CommandTest.java
@@ -6,12 +6,15 @@
 import static com.android.tools.r8.R8CommandTest.getOutputPath;
 import static com.android.tools.r8.ToolHelper.EXAMPLES_BUILD_DIR;
 import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
+import static java.nio.file.StandardOpenOption.CREATE_NEW;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import com.android.sdklib.AndroidVersion;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.utils.AndroidApp;
@@ -20,6 +23,7 @@
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.List;
@@ -87,6 +91,26 @@
   }
 
   @Test
+  public void flagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path flagsFile = working.resolve("flags.txt");
+    Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("output.zip");
+    FileUtils.writeTextFile(
+        flagsFile,
+        "--output",
+        output.toString(),
+        "--min-api",
+        "24",
+        input.toString());
+    assertEquals(0, ToolHelper.forkD8(working, "@flags.txt").exitCode);
+    assertTrue(Files.exists(output));
+    Marker marker = ExtractMarker.extractMarkerFromDexFile(output);
+    assertEquals(24, marker.getMinApi().intValue());
+    assertEquals(Tool.D8, marker.getTool());
+  }
+
+  @Test
   public void printsHelpOnNoInput() throws Throwable {
     ProcessResult result = ToolHelper.forkD8(temp.getRoot().toPath());
     assertFalse(result.exitCode == 0);
diff --git a/src/test/java/com/android/tools/r8/R8CommandTest.java b/src/test/java/com/android/tools/r8/R8CommandTest.java
index a7c7e02..c1072bd 100644
--- a/src/test/java/com/android/tools/r8/R8CommandTest.java
+++ b/src/test/java/com/android/tools/r8/R8CommandTest.java
@@ -11,6 +11,8 @@
 
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
 import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.utils.FileUtils;
 import com.google.common.collect.ImmutableList;
@@ -90,6 +92,29 @@
   }
 
   @Test
+  public void flagsFile() throws Throwable {
+    Path working = temp.getRoot().toPath();
+    Path library = ToolHelper.getDefaultAndroidJar();
+    Path input = Paths.get(EXAMPLES_BUILD_DIR + "/arithmetic.jar").toAbsolutePath();
+    Path output = working.resolve("output.zip");
+    Path flagsFile = working.resolve("flags.txt");
+    FileUtils.writeTextFile(
+        flagsFile,
+        "--output",
+        output.toString(),
+        "--min-api",
+        "24",
+        "--lib",
+        library.toAbsolutePath().toString(),
+        input.toString());
+    assertEquals(0, ToolHelper.forkR8(working, "@flags.txt").exitCode);
+    assertTrue(Files.exists(output));
+    Marker marker = ExtractMarker.extractMarkerFromDexFile(output);
+    assertEquals(24, marker.getMinApi().intValue());
+    assertEquals(Tool.R8, marker.getTool());
+  }
+
+  @Test
   public void printsHelpOnNoInput() throws Throwable {
     ProcessResult result = ToolHelper.forkR8(temp.getRoot().toPath());
     assertFalse(result.exitCode == 0);
@@ -260,24 +285,6 @@
   }
 
   @Test
-  public void argumentsInFile() throws Throwable {
-    Path inputFile = temp.newFile("foobar.class").toPath();
-    Path pgConfFile = temp.newFile("pgconf.config").toPath();
-    Path argsFile = temp.newFile("more-args.txt").toPath();
-    FileUtils.writeTextFile(argsFile, ImmutableList.of(
-        "--debug --no-minification",
-        "--pg-conf " + pgConfFile,
-        inputFile.toString()
-    ));
-    R8Command command = parse("@" + argsFile.toString());
-    assertEquals(CompilationMode.DEBUG, command.getMode());
-    assertFalse(command.getEnableMinification());
-    assertFalse(
-        command.getEnableTreeShaking()); // We have no keep rules (proguard config file is empty).
-    assertEquals(1, ToolHelper.getApp(command).getClassProgramResourcesForTesting().size());
-  }
-
-  @Test
   public void nonExistingOutputJar() throws Throwable {
     Path nonExistingJar = temp.getRoot().toPath().resolve("non-existing-archive.jar");
     R8Command.builder().setOutput(nonExistingJar, OutputMode.DexIndexed).build();