Add R8 command-line option: --write-pg-map

Bug:
Change-Id: I5ae04e07b16830b90c1e85df2f706193d6d58e9f
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index e8990f3..1306e3b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -63,7 +63,9 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -452,21 +454,31 @@
     }
   }
 
+  private static void writeProguardMapToPath(Path path, AndroidApp outputApp) throws IOException {
+    try (Closer closer = Closer.create()) {
+      OutputStream mapOut = FileUtils.openPathWithDefault(
+          closer,
+          path,
+          System.out,
+          StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
+      outputApp.writeProguardMap(mapOut);
+    }
+  }
+
   static void writeOutputs(R8Command command, InternalOptions options, AndroidApp outputApp)
       throws IOException {
     if (command.getOutputPath() != null) {
       outputApp.write(command.getOutputPath(), options.outputMode);
     }
 
-    if (options.proguardConfiguration.isPrintMapping() && !options.skipMinification) {
+    if ((options.proguardConfiguration.isPrintMapping() || options.printMappingFile != null)
+        && !options.skipMinification) {
       assert outputApp.hasProguardMap();
-      try (Closer closer = Closer.create()) {
-        OutputStream mapOut = FileUtils.openPathWithDefault(
-            closer,
-            options.proguardConfiguration.getPrintMappingFile(),
-            System.out,
-            StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
-        outputApp.writeProguardMap(mapOut);
+      if (options.proguardConfiguration.isPrintMapping()) {
+        writeProguardMapToPath(options.proguardConfiguration.getPrintMappingFile(), outputApp);
+      }
+      if (options.printMappingFile != null) {
+        writeProguardMapToPath(options.printMappingFile, outputApp);
       }
     }
     if (options.proguardConfiguration.isPrintSeeds()) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index b860de4..301c266 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -36,6 +36,7 @@
     private Optional<Boolean> discardedChecker = Optional.empty();
     private Optional<Boolean> minification = Optional.empty();
     private boolean ignoreMissingClasses = false;
+    private Path printMappingFile = null;
     private Path packageDistributionFile = null;
 
     private Builder() {
@@ -176,6 +177,11 @@
       return self();
     }
 
+    Builder setPrintMappingFile(Path path) {
+      this.printMappingFile = path;
+      return self();
+    }
+
     protected void validate() throws CompilationException {
       super.validate();
       if (mainDexListOutput != null && mainDexRules.isEmpty() && !getAppBuilder()
@@ -251,7 +257,8 @@
           useTreeShaking,
           useDiscardedChecker,
           useMinification,
-          ignoreMissingClasses);
+          ignoreMissingClasses,
+          printMappingFile);
     }
   }
 
@@ -274,6 +281,7 @@
       "  --pg-conf <file>         # Proguard configuration <file> (implies tree",
       "                           # shaking/minification).",
       "  --pg-map <file>          # Proguard map <file>.",
+      "  --print-mapping <file>   # Write name/line mapping to <file>.",
       "  --no-tree-shaking        # Force disable tree shaking of unreachable classes.",
       "  --no-discarded-checker   # Force disable the discarded checker (when tree shaking).",
       "  --no-minification        # Force disable minification of names.",
@@ -291,6 +299,7 @@
   private final boolean useDiscardedChecker;
   private final boolean useMinification;
   private final boolean ignoreMissingClasses;
+  private final Path printMappingFile;
 
   public static Builder builder() {
     return new Builder();
@@ -362,6 +371,8 @@
         builder.setProguardMapFile(Paths.get(args[++i]));
       } else if (arg.equals("--ignore-missing-classes")) {
         builder.setIgnoreMissingClasses(true);
+      } else if (arg.equals("--print-mapping")) {
+        builder.setPrintMappingFile(Paths.get(args[++i]));
       } else if (arg.startsWith("@")) {
         // TODO(zerny): Replace this with pipe reading.
         String argsFile = arg.substring(1);
@@ -404,7 +415,8 @@
       boolean useTreeShaking,
       boolean useDiscardedChecker,
       boolean useMinification,
-      boolean ignoreMissingClasses) {
+      boolean ignoreMissingClasses,
+      Path printMappingFile) {
     super(inputApp, outputPath, outputMode, mode, minApiLevel);
     assert proguardConfiguration != null;
     assert mainDexKeepRules != null;
@@ -416,6 +428,7 @@
     this.useDiscardedChecker = useDiscardedChecker;
     this.useMinification = useMinification;
     this.ignoreMissingClasses = ignoreMissingClasses;
+    this.printMappingFile = printMappingFile;
   }
 
   private R8Command(boolean printHelp, boolean printVersion) {
@@ -427,8 +440,8 @@
     useDiscardedChecker = false;
     useMinification = false;
     ignoreMissingClasses = false;
+    printMappingFile = null;
   }
-
   public boolean useTreeShaking() {
     return useTreeShaking;
   }
@@ -471,6 +484,7 @@
       // TODO(zerny): Should we support inlining in debug mode? b/62937285
       internal.inlineAccessors = false;
     }
+    internal.printMappingFile = printMappingFile;
     return internal;
   }
 }
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 31a1724..c03c3d6 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -124,6 +124,8 @@
   // the code contains unsupported byte codes.
   public boolean skipReadingDexCode = false;
 
+  public Path printMappingFile = null;
+
   public void warningInvalidDebugInfo(DexEncodedMethod method, InvalidDebugInfoException e) {
     warningInvalidDebugInfoCount++;
   }