Dump all input when dumpInputToFile is set

We will now also dump library input, classpath input, proguard config and the version

Change-Id: If234b027cd28104df67f4da4e524eced53b9e270
diff --git a/src/main/java/com/android/tools/r8/ClassFileConsumer.java b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
index f940c29..18f68ba 100644
--- a/src/main/java/com/android/tools/r8/ClassFileConsumer.java
+++ b/src/main/java/com/android/tools/r8/ClassFileConsumer.java
@@ -3,24 +3,19 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-
 import com.android.tools.r8.utils.ArchiveBuilder;
 import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.DirectoryBuilder;
 import com.android.tools.r8.utils.OutputBuilder;
 import com.android.tools.r8.utils.ZipUtils;
-import com.google.common.io.ByteStreams;
 import com.google.common.io.Closer;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.OpenOption;
 import java.nio.file.Path;
 import java.nio.file.StandardOpenOption;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
 /**
@@ -123,7 +118,7 @@
     @Override
     public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
       super.accept(data, descriptor, handler);
-      outputBuilder.addFile(getClassFileName(descriptor), data, handler);
+      outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
     }
 
     @Override
@@ -147,11 +142,6 @@
       return outputBuilder.getPath();
     }
 
-    private static String getClassFileName(String classDescriptor) {
-      assert classDescriptor != null && DescriptorUtils.isClassDescriptor(classDescriptor);
-      return DescriptorUtils.getClassBinaryNameFromDescriptor(classDescriptor) + CLASS_EXTENSION;
-    }
-
     public static void writeResources(
         Path archive, List<ProgramResource> resources, Set<DataEntryResource> dataResources)
         throws IOException, ResourceException {
@@ -159,22 +149,11 @@
           new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
       try (Closer closer = Closer.create()) {
         try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(archive, options))) {
-          for (ProgramResource resource : resources) {
-            assert resource.getClassDescriptors().size() == 1;
-            Iterator<String> iterator = resource.getClassDescriptors().iterator();
-            String className = iterator.next();
-            String entryName = getClassFileName(className);
-            byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
-            ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
-          }
-          for (DataEntryResource dataResource : dataResources) {
-            String entryName = dataResource.getName();
-            byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
-            ZipUtils.writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
-          }
+          ZipUtils.writeResourcesToZip(resources, dataResources, closer, out);
         }
       }
     }
+
   }
 
   /** Directory consumer to write program resources to a directory. */
@@ -210,7 +189,7 @@
     @Override
     public void accept(ByteDataView data, String descriptor, DiagnosticsHandler handler) {
       super.accept(data, descriptor, handler);
-      outputBuilder.addFile(ArchiveConsumer.getClassFileName(descriptor), data, handler);
+      outputBuilder.addFile(DescriptorUtils.getClassFileName(descriptor), data, handler);
     }
 
     @Override
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index d6a9696..6e95cac 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -9,14 +9,15 @@
 import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
 import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION;
 
-import com.android.tools.r8.ClassFileConsumer;
 import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DataEntryResource;
 import com.android.tools.r8.DataResourceProvider;
 import com.android.tools.r8.ProgramResource;
 import com.android.tools.r8.ProgramResource.Kind;
 import com.android.tools.r8.ProgramResourceProvider;
 import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.StringResource;
+import com.android.tools.r8.Version;
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.graph.ClassKind;
 import com.android.tools.r8.graph.DexApplication;
@@ -42,21 +43,30 @@
 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.ZipUtils;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.OpenOption;
 import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Queue;
+import java.util.Set;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 import org.objectweb.asm.ClassVisitor;
 
 public class ApplicationReader {
@@ -151,34 +161,108 @@
   }
 
   private void dumpInputToFile() throws IOException  {
-    List<ProgramResource> programResourcesWithDescriptors = new ArrayList<>();
     try {
-      for (ProgramResourceProvider programResourceProvider : inputApp
-          .getProgramResourceProviders()) {
-        for (ProgramResource programResource : programResourceProvider.getProgramResources()) {
-          if (programResource.getKind() != Kind.CF) {
-            continue;
+      List<ProgramResourceProvider> programResourceProviders =
+          inputApp.getProgramResourceProviders();
+      Set<DataEntryResource> dataEntryResources = inputApp.getDataEntryResourcesForTesting();
+      List<ProgramResource> programResourcesWithDescriptors = new ArrayList<>();
+      for (ProgramResourceProvider programResourceProvider : programResourceProviders) {
+        addProgramResourcesWithDescriptor(
+            programResourcesWithDescriptors, programResourceProvider.getProgramResources());
+      }
+
+      List<ProgramResource> libraryProgramResourcesWithDescriptors =
+          getProgramResourcesWithDescriptors(inputApp.getLibraryResourceProviders());
+
+      List<ProgramResource> classpathProgramResourcesWithDescriptors =
+          getProgramResourcesWithDescriptors(inputApp.getClasspathResourceProviders());
+
+      OpenOption[] openOptions =
+          new OpenOption[] {StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING};
+      try (Closer closer = Closer.create()) {
+        try (ZipOutputStream out =
+            new ZipOutputStream(
+                Files.newOutputStream(Paths.get(options.dumpInputToFile), openOptions))) {
+          writeToZip(
+              dataEntryResources, programResourcesWithDescriptors, closer, out, "program.jar");
+          writeToZip(
+              ImmutableSet.of(),
+              libraryProgramResourcesWithDescriptors,
+              closer,
+              out,
+              "library.jar");
+          writeToZip(
+              ImmutableSet.of(),
+              classpathProgramResourcesWithDescriptors,
+              closer,
+              out,
+              "classpath.jar");
+          if (options.hasProguardConfiguration()) {
+            String proguardConfig = options.getProguardConfiguration().getParsedConfiguration();
+            ZipUtils.writeToZipStream(
+                out, "proguard.config", proguardConfig.getBytes(), ZipEntry.DEFLATED);
           }
-          try (InputStream inputStream = programResource.getByteStream()) {
-            byte[] bytes = ByteStreams.toByteArray(inputStream);
-            String descriptor = extractClassInternalType(bytes);
-            programResourcesWithDescriptors.add(
-                ProgramResource.fromBytes(
-                    programResource.getOrigin(),
-                    programResource.getKind(),
-                    bytes,
-                    ImmutableSet.of(descriptor)));
-          }
+
+          ZipUtils.writeToZipStream(
+              out, "r8-version", Version.getVersionString().getBytes(), ZipEntry.DEFLATED);
         }
       }
-      ClassFileConsumer.ArchiveConsumer.writeResources(
-          Paths.get(options.dumpInputToFile),
-          programResourcesWithDescriptors, inputApp.getDataEntryResourcesForTesting());
     } catch (ResourceException e) {
       options.reporter.fatalError("Failed to write input:" + e.getMessage());
     }
   }
 
+  private static void writeToZip(
+      Set<DataEntryResource> dataEntryResources,
+      List<ProgramResource> programResourcesWithDescriptors,
+      Closer closer,
+      ZipOutputStream out,
+      String entry)
+      throws IOException, ResourceException {
+    try (ByteArrayOutputStream programByteStream = new ByteArrayOutputStream()) {
+      try (ZipOutputStream archiveOutputStream = new ZipOutputStream(programByteStream)) {
+        ZipUtils.writeResourcesToZip(
+            programResourcesWithDescriptors, dataEntryResources, closer, archiveOutputStream);
+      }
+      ZipUtils.writeToZipStream(out, entry, programByteStream.toByteArray(), ZipEntry.DEFLATED);
+    }
+  }
+
+  private static List<ProgramResource> getProgramResourcesWithDescriptors(
+      List<ClassFileResourceProvider> classFileResourceProviders)
+      throws IOException, ResourceException {
+    ArrayList<ProgramResource> programResourcesWithDescriptors = new ArrayList<>();
+    for (ClassFileResourceProvider libraryResourceProvider : classFileResourceProviders) {
+      List<ProgramResource> programResources = new ArrayList<>();
+      for (String classDescriptor : libraryResourceProvider.getClassDescriptors()) {
+        programResources.add(libraryResourceProvider.getProgramResource(classDescriptor));
+      }
+      addProgramResourcesWithDescriptor(programResourcesWithDescriptors, programResources);
+    }
+    return programResourcesWithDescriptors;
+  }
+
+  private static void addProgramResourcesWithDescriptor(
+      List<ProgramResource> programResourcesWithDescriptors,
+      Collection<ProgramResource> programResources)
+      throws IOException, ResourceException {
+    for (ProgramResource programResource : programResources) {
+      if (programResource.getKind() != Kind.CF) {
+        continue;
+      }
+      try (InputStream inputStream = programResource.getByteStream()) {
+        byte[] bytes = ByteStreams.toByteArray(inputStream);
+        String descriptor = extractClassInternalType(bytes);
+        programResourcesWithDescriptors.add(
+            ProgramResource.fromBytes(
+                programResource.getOrigin(),
+                programResource.getKind(),
+                bytes,
+                ImmutableSet.of(descriptor)));
+      }
+    }
+  }
+
   private static String extractClassInternalType(byte[] bytes) throws IOException {
     class ClassNameExtractor extends ClassVisitor {
       private String className;
diff --git a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
index ba9af9d..af056a8 100644
--- a/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/DescriptorUtils.java
@@ -444,4 +444,9 @@
     assert isValidJavaType(typeName);
     return typeName.replace(JAVA_PACKAGE_SEPARATOR, DESCRIPTOR_PACKAGE_SEPARATOR) + ".class";
   }
+
+  public static String getClassFileName(String classDescriptor) {
+    assert classDescriptor != null && isClassDescriptor(classDescriptor);
+    return getClassBinaryNameFromDescriptor(classDescriptor) + CLASS_EXTENSION;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index da87e2b..89ef8f3 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -8,8 +8,12 @@
 import static com.android.tools.r8.utils.FileUtils.MODULE_INFO_CLASS;
 
 import com.android.tools.r8.ByteDataView;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ResourceException;
 import com.android.tools.r8.errors.CompilationError;
 import com.google.common.io.ByteStreams;
+import com.google.common.io.Closer;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -19,7 +23,9 @@
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 import java.util.function.Predicate;
 import java.util.zip.CRC32;
 import java.util.zip.ZipEntry;
@@ -28,6 +34,27 @@
 
 public class ZipUtils {
 
+  public static void writeResourcesToZip(
+      List<ProgramResource> resources,
+      Set<DataEntryResource> dataResources,
+      Closer closer,
+      ZipOutputStream out)
+      throws IOException, ResourceException {
+    for (ProgramResource resource : resources) {
+      assert resource.getClassDescriptors().size() == 1;
+      Iterator<String> iterator = resource.getClassDescriptors().iterator();
+      String className = iterator.next();
+      String entryName = DescriptorUtils.getClassFileName(className);
+      byte[] bytes = ByteStreams.toByteArray(closer.register(resource.getByteStream()));
+      writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+    }
+    for (DataEntryResource dataResource : dataResources) {
+      String entryName = dataResource.getName();
+      byte[] bytes = ByteStreams.toByteArray(closer.register(dataResource.getByteStream()));
+      writeToZipStream(out, entryName, bytes, ZipEntry.DEFLATED);
+    }
+  }
+
   public interface OnEntryHandler {
     void onEntry(ZipEntry entry, InputStream input) throws IOException;
   }
diff --git a/src/test/java/com/android/tools/r8/R8EntryPointTests.java b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
index 0bbc7bc..553ff0e 100644
--- a/src/test/java/com/android/tools/r8/R8EntryPointTests.java
+++ b/src/test/java/com/android/tools/r8/R8EntryPointTests.java
@@ -9,13 +9,20 @@
 
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.ZipUtils.OnEntryHandler;
 import com.google.common.collect.ImmutableList;
 import java.io.IOException;
+import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.zip.ZipEntry;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -173,6 +180,53 @@
   }
 
   @Test
+  public void testDumpInputs() throws IOException {
+    Path out = temp.newFile("dex.zip").toPath();
+    Path dump = temp.newFile("dump.zip").toPath();
+    ProcessResult r8 =
+        ToolHelper.forkR8WithJavaOptions(
+            Paths.get("."),
+            ImmutableList.of("-Dcom.android.tools.r8.dumpinputtofile=" + dump.toString()),
+            "--lib",
+            ToolHelper.getDefaultAndroidJar().toString(),
+            "--dex",
+            "--output",
+            out.toString(),
+            "--pg-conf",
+            PROGUARD_FLAGS.toString(),
+            "--pg-conf",
+            testFlags.toString(),
+            INPUT_JAR.toString());
+
+    List<ZipEntry> entries = new ArrayList<>();
+    ZipUtils.iter(
+        dump.toString(),
+        new OnEntryHandler() {
+          @Override
+          public void onEntry(ZipEntry entry, InputStream input) throws IOException {
+            entries.add(entry);
+          }
+        });
+    Assert.assertTrue(hasEntry(entries, "program.jar"));
+    Assert.assertTrue(hasEntry(entries, "library.jar"));
+    Assert.assertTrue(hasEntry(entries, "classpath.jar"));
+    Assert.assertTrue(hasEntry(entries, "proguard.config"));
+    Assert.assertTrue(hasEntry(entries, "r8-version"));
+    // When dumping the inputs we throw an error in the program to exit early.
+    Assert.assertNotEquals(0, r8.exitCode);
+  }
+
+  private boolean hasEntry(Collection<ZipEntry> entries, String name) {
+    for (ZipEntry entry : entries) {
+      if (entry.getName().equals(name)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Test
   public void testSpecifyDexAndClassfileNotAllowed() throws IOException, InterruptedException {
     Path out = temp.newFile("dex.zip").toPath();
     ProcessResult r8 =
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 84e8cc9..d363c34 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1353,6 +1353,12 @@
     return forkJava(dir, R8.class, args);
   }
 
+  public static ProcessResult forkR8WithJavaOptions(
+      Path dir, List<String> javaOptions, String... args) throws IOException {
+    String r8Jar = R8_JAR.toAbsolutePath().toString();
+    return forkJavaWithJarAndJavaOptions(dir, r8Jar, Arrays.asList(args), javaOptions);
+  }
+
   public static ProcessResult forkR8Jar(Path dir, String... args)
       throws IOException, InterruptedException {
     String r8Jar = R8_JAR.toAbsolutePath().toString();
@@ -1379,14 +1385,23 @@
 
   private static ProcessResult forkJavaWithJar(Path dir, String jarPath, List<String> args)
       throws IOException {
-    List<String> command = new ImmutableList.Builder<String>()
-        .add(getJavaExecutable())
-        .add("-jar").add(jarPath)
-        .addAll(args)
-        .build();
+    return forkJavaWithJarAndJavaOptions(dir, jarPath, args, ImmutableList.of());
+  }
+
+  private static ProcessResult forkJavaWithJarAndJavaOptions(
+      Path dir, String jarPath, List<String> args, List<String> javaOptions) throws IOException {
+    List<String> command =
+        new ImmutableList.Builder<String>()
+            .add(getJavaExecutable())
+            .addAll(javaOptions)
+            .add("-jar")
+            .add(jarPath)
+            .addAll(args)
+            .build();
     return runProcess(new ProcessBuilder(command).directory(dir.toFile()));
   }
 
+
   private static ProcessResult forkJava(Path dir, Class clazz, List<String> args)
       throws IOException {
     List<String> command = new ImmutableList.Builder<String>()