Enable adding --classpath on commandline and in test builders

Change-Id: Ib054e22eb30ab2175344940e19f6d48a6de583b3
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index b7d6cfe..5eb8ce9 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -213,6 +213,28 @@
       return self();
     }
 
+    /** Add classpath file resources. */
+    public B addClasspathFiles(Path... files) {
+      guard(() -> Arrays.stream(files).forEach(this::addClasspathFile));
+      return self();
+    }
+
+    /** Add classpath file resources. */
+    public B addClasspathFiles(Collection<Path> files) {
+      guard(() -> files.forEach(this::addClasspathFile));
+      return self();
+    }
+
+    private void addClasspathFile(Path file) {
+      guard(() -> getAppBuilder().addClasspathFile(file));
+    }
+
+    /** Add classfile resources provider for class-path resources. */
+    public B addClasspathResourceProvider(ClassFileResourceProvider provider) {
+      guard(() -> getAppBuilder().addClasspathResourceProvider(provider));
+      return self();
+    }
+
     /** Add Java-bytecode program-data. */
     public B addClassProgramData(byte[] data, Origin origin) {
       guard(() -> app.addClassProgramData(data, origin));
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java
index 545e102..6bfcfe2 100644
--- a/src/main/java/com/android/tools/r8/D8Command.java
+++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.utils.Reporter;
 import com.android.tools.r8.utils.StringDiagnostic;
 import java.nio.file.Path;
-import java.util.Arrays;
 import java.util.Collection;
 
 /**
@@ -84,26 +83,22 @@
       return self();
     }
 
-    /** Add classpath file resources. */
+    /** Add classpath file resources. These have @Override to ensure binary compatibility. */
+    @Override
     public Builder addClasspathFiles(Path... files) {
-      guard(() -> Arrays.stream(files).forEach(this::addClasspathFile));
-      return self();
+      return super.addClasspathFiles(files);
     }
 
     /** Add classpath file resources. */
+    @Override
     public Builder addClasspathFiles(Collection<Path> files) {
-      guard(() -> files.forEach(this::addClasspathFile));
-      return self();
-    }
-
-    private void addClasspathFile(Path file) {
-      guard(() -> getAppBuilder().addClasspathFile(file));
+      return super.addClasspathFiles(files);
     }
 
     /** Add classfile resources provider for class-path resources. */
+    @Override
     public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) {
-      guard(() -> getAppBuilder().addClasspathResourceProvider(provider));
-      return self();
+      return super.addClasspathResourceProvider(provider);
     }
 
     /**
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index b91739a..6984283 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -19,6 +19,7 @@
       ImmutableSet.of(
           "--output",
           "--lib",
+          "--classpath",
           "--min-api",
           "--main-dex-rules",
           "--main-dex-list",
@@ -59,6 +60,7 @@
               "  --output <file>          # Output result in <file>.",
               "                           # <file> must be an existing directory or a zip file.",
               "  --lib <file>             # Add <file> as a library resource.",
+              "  --classpath <file>       # Add <file> as a classpath resource.",
               "  --min-api <number>       # Minimum Android API level compatibility, default: "
                   + AndroidApiLevel.getDefault().getLevel()
                   + ".",
@@ -178,6 +180,8 @@
         state.outputPath = Paths.get(nextArg);
       } else if (arg.equals("--lib")) {
         builder.addLibraryFiles(Paths.get(nextArg));
+      } else if (arg.equals("--classpath")) {
+        builder.addClasspathFiles(Paths.get(nextArg));
       } else if (arg.equals("--min-api")) {
         if (state.hasDefinedApiLevel) {
           builder.error(new StringDiagnostic("Cannot set multiple --min-api options", argsOrigin));
diff --git a/src/test/java/com/android/tools/r8/D8TestBuilder.java b/src/test/java/com/android/tools/r8/D8TestBuilder.java
index 5e450f0..00b8cd6 100644
--- a/src/test/java/com/android/tools/r8/D8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/D8TestBuilder.java
@@ -7,6 +7,8 @@
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
+import java.nio.file.Path;
+import java.util.Collection;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -28,6 +30,18 @@
   }
 
   @Override
+  public D8TestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    builder.addClasspathResourceProvider(ClassFileResourceProviderFromClasses(classes));
+    return self();
+  }
+
+  @Override
+  public D8TestBuilder addClasspathFiles(Collection<Path> files) {
+    builder.addClasspathFiles(files);
+    return self();
+  }
+
+  @Override
   D8TestCompileResult internalCompile(
       Builder builder, Consumer<InternalOptions> optionsConsumer, Supplier<AndroidApp> app)
       throws CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/DXTestBuilder.java b/src/test/java/com/android/tools/r8/DXTestBuilder.java
index 8462f7b..84ecee1 100644
--- a/src/test/java/com/android/tools/r8/DXTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/DXTestBuilder.java
@@ -65,6 +65,16 @@
   }
 
   @Override
+  public DXTestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
+  public DXTestBuilder addClasspathFiles(Collection<Path> files) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
   public DXTestBuilder addProgramClasses(Collection<Class<?>> classes) {
     return addProgramClassFileData(
         ListUtils.map(
diff --git a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
index 0225784..3584faa 100644
--- a/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ExternalR8TestBuilder.java
@@ -188,6 +188,16 @@
   }
 
   @Override
+  public ExternalR8TestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
+  public ExternalR8TestBuilder addClasspathFiles(Collection<Path> files) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
   public ExternalR8TestBuilder addKeepRuleFiles(List<Path> proguardConfigFiles) {
     this.proguardConfigFiles.addAll(proguardConfigFiles);
     return self();
diff --git a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
index ac3b582..15200b5 100644
--- a/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/ProguardTestBuilder.java
@@ -259,6 +259,16 @@
   }
 
   @Override
+  public ProguardTestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
+  public ProguardTestBuilder addClasspathFiles(Collection<Path> files) {
+    throw new Unimplemented("No support for adding classpath data directly");
+  }
+
+  @Override
   public ProguardTestBuilder setProgramConsumer(ProgramConsumer programConsumer) {
     throw new Unimplemented("No support for program consumer");
   }
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 8edbb41..bb3d9cd 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -116,6 +116,18 @@
         graphConsumer);
   }
 
+  @Override
+  public R8TestBuilder addClasspathClasses(Collection<Class<?>> classes) {
+    builder.addClasspathResourceProvider(ClassFileResourceProviderFromClasses(classes));
+    return self();
+  }
+
+  @Override
+  public R8TestBuilder addClasspathFiles(Collection<Path> files) {
+    builder.addClasspathFiles(files);
+    return self();
+  }
+
   public R8TestBuilder addDataResources(List<DataEntryResource> resources) {
     resources.forEach(builder.getAppBuilder()::addDataResource);
     return self();
diff --git a/src/test/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
index 61d6996..581bfe8 100644
--- a/src/test/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestBaseBuilder.java
@@ -63,30 +63,7 @@
 
   @Override
   public T addLibraryClasses(Collection<Class<?>> classes) {
-    addLibraryProvider(
-        new ClassFileResourceProvider() {
-          final Map<String, ProgramResource> resources;
-
-          {
-            ImmutableMap.Builder<String, ProgramResource> builder = ImmutableMap.builder();
-            classes.forEach(
-                c ->
-                    builder.put(
-                        DescriptorUtils.javaTypeToDescriptor(c.getTypeName()),
-                        ProgramResource.fromFile(Kind.CF, ToolHelper.getClassFileForTestClass(c))));
-            resources = builder.build();
-          }
-
-          @Override
-          public Set<String> getClassDescriptors() {
-            return resources.keySet();
-          }
-
-          @Override
-          public ProgramResource getProgramResource(String descriptor) {
-            return resources.get(descriptor);
-          }
-        });
+    builder.addLibraryResourceProvider(ClassFileResourceProviderFromClasses(classes));
     return self();
   }
 
@@ -94,4 +71,31 @@
     builder.addMainDexListFiles(files);
     return self();
   }
+
+  public static ClassFileResourceProvider ClassFileResourceProviderFromClasses(
+      Collection<Class<?>> classes) {
+    return new ClassFileResourceProvider() {
+      final Map<String, ProgramResource> resources;
+
+      {
+        ImmutableMap.Builder<String, ProgramResource> builder = ImmutableMap.builder();
+        classes.forEach(
+            c ->
+                builder.put(
+                    DescriptorUtils.javaTypeToDescriptor(c.getTypeName()),
+                    ProgramResource.fromFile(Kind.CF, ToolHelper.getClassFileForTestClass(c))));
+        resources = builder.build();
+      }
+
+      @Override
+      public Set<String> getClassDescriptors() {
+        return resources.keySet();
+      }
+
+      @Override
+      public ProgramResource getProgramResource(String descriptor) {
+        return resources.get(descriptor);
+      }
+    };
+  }
 }
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 94c15db..7014cff 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -3,22 +3,18 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8;
 
-import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
 import com.android.tools.r8.TestBase.Backend;
 import com.android.tools.r8.debug.DebugTestConfig;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.AndroidAppConsumers;
-import com.android.tools.r8.utils.DescriptorUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.google.common.base.Suppliers;
 import java.io.IOException;
 import java.io.PrintStream;
 import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -47,9 +43,6 @@
   private Consumer<InternalOptions> optionsConsumer = DEFAULT_OPTIONS;
   private PrintStream stdout = null;
 
-  // Consider an in-order collection of both class and files on the classpath.
-  private List<Class<?>> classpathClasses = new ArrayList<>();
-
   TestCompilerBuilder(TestState state, B builder, Backend backend) {
     super(state, builder);
     this.backend = backend;
@@ -80,31 +73,6 @@
     if (backend == Backend.DEX && defaultMinApiLevel != null) {
       builder.setMinApiLevel(defaultMinApiLevel.getLevel());
     }
-    if (!classpathClasses.isEmpty()) {
-      Path cp;
-      try {
-        cp = getState().getNewTempFolder().resolve("cp.jar");
-      } catch (IOException e) {
-        throw builder.getReporter().fatalError("Failed to create temp file for classpath archive");
-      }
-      ArchiveConsumer archiveConsumer = new ArchiveConsumer(cp);
-      for (Class<?> classpathClass : classpathClasses) {
-        try {
-          archiveConsumer.accept(
-              ByteDataView.of(ToolHelper.getClassAsBytes(classpathClass)),
-              DescriptorUtils.javaTypeToDescriptor(classpathClass.getTypeName()),
-              builder.getReporter());
-        } catch (IOException e) {
-          builder
-              .getReporter()
-              .error("Failed to read bytes for classpath class: " + classpathClass.getTypeName());
-        }
-      }
-      archiveConsumer.finished(builder.getReporter());
-      // TODO(zerny): Remove this HACK to add classpath to R8.
-      builder.getAppBuilder().addClasspathFiles(cp);
-    }
-
     PrintStream oldOut = System.out;
     try {
       if (stdout != null) {
@@ -209,19 +177,13 @@
     return addClasspathClasses(Arrays.asList(classes));
   }
 
-  public T addClasspathClasses(Collection<Class<?>> classes) {
-    classpathClasses.addAll(classes);
-    return self();
-  }
+  public abstract T addClasspathClasses(Collection<Class<?>> classes);
 
   public T addClasspathFiles(Path... files) {
     return addClasspathFiles(Arrays.asList(files));
   }
 
-  public T addClasspathFiles(Collection<Path> files) {
-    builder.getAppBuilder().addClasspathFiles(files);
-    return self();
-  }
+  public abstract T addClasspathFiles(Collection<Path> files);
 
   public T noDesugaring() {
     builder.setDisableDesugaring(true);