Merge "Start looking for specific code patterns in the enqueuer"
diff --git a/.gitignore b/.gitignore
index 86cb42d..582f1a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
 build/
+buildSrc/out/
 .idea/
+r8.iml
 .gradle/
 android-data*/
 tests/2016-12-19/art.tar.gz
diff --git a/build.gradle b/build.gradle
index d93cbf1..8a5830d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -12,27 +12,38 @@
 apply plugin: 'net.ltgt.errorprone-base'
 apply plugin: "net.ltgt.apt"
 
+// Ensure importing into IntelliJ IDEA use the same output directories as Gradle. In tests we
+// use the output path for tests (ultimately through ToolHelper.getClassPathForTests()) and
+// therefore these paths need to be the same. See https://youtrack.jetbrains.com/issue/IDEA-175172
+// for context.
+idea {
+    module {
+        outputDir file('build/classes/main')
+        testOutputDir file('build/classes/test')
+    }
+}
+
 def errorProneConfiguration = [
     '-XepDisableAllChecks',
     // D8 want to use reference equality, thus disable the checker explicitly
     '-Xep:ReferenceEquality:OFF',
-    '-Xep:ClassCanBeStatic:WARN',
-    '-Xep:OperatorPrecedence:WARN',
-    '-Xep:RemoveUnusedImports:WARN',
-    '-Xep:MissingOverride:WARN',
-    '-Xep:OvershadowingSubclassFields:WARN',
-    '-Xep:IntLongMath:WARN',
-    '-Xep:EqualsHashCode:WARN',
-    '-Xep:InconsistentOverloads:WARN',
-    '-Xep:ArrayHashCode:WARN',
-    '-Xep:EqualsIncompatibleType:WARN',
-    '-Xep:NonOverridingEquals:WARN',
-    '-Xep:FallThrough:WARN',
-    '-Xep:MissingCasesInEnumSwitch:WARN',
-    '-Xep:MissingDefault:WARN',
-    '-Xep:MultipleTopLevelClasses:WARN',
-    '-Xep:NarrowingCompoundAssignment:WARN',
-    '-Xep:BoxedPrimitiveConstructor:WARN']
+    '-Xep:ClassCanBeStatic:ERROR',
+    '-Xep:OperatorPrecedence:ERROR',
+    '-Xep:RemoveUnusedImports:ERROR',
+    '-Xep:MissingOverride:ERROR',
+    '-Xep:OvershadowingSubclassFields:ERROR',
+    '-Xep:IntLongMath:ERROR',
+    '-Xep:EqualsHashCode:ERROR',
+    '-Xep:InconsistentOverloads:ERROR',
+    '-Xep:ArrayHashCode:ERROR',
+    '-Xep:EqualsIncompatibleType:ERROR',
+    '-Xep:NonOverridingEquals:ERROR',
+    '-Xep:FallThrough:ERROR',
+    '-Xep:MissingCasesInEnumSwitch:ERROR',
+    '-Xep:MissingDefault:ERROR',
+    '-Xep:MultipleTopLevelClasses:ERROR',
+    '-Xep:NarrowingCompoundAssignment:ERROR',
+    '-Xep:BoxedPrimitiveConstructor:ERROR']
 
 apply from: 'copyAdditionalJctfCommonFiles.gradle'
 
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index e35010a..5cd87fb 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -5,6 +5,7 @@
 
 import com.android.tools.r8.errors.CompilationError;
 import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.utils.AbortException;
@@ -159,26 +160,14 @@
     }
 
     /** Add Java-bytecode program-data. */
-    public B addClassProgramData(byte[]... data) {
-      app.addClassProgramData(data);
-      return self();
-    }
-
-    /** Add Java-bytecode program-data. */
-    public B addClassProgramData(Collection<byte[]> data) {
-      app.addClassProgramData(data);
+    public B addClassProgramData(byte[] data, Origin origin) {
+      app.addClassProgramData(data, origin);
       return self();
     }
 
     /** Add dex program-data. */
-    public B addDexProgramData(byte[]... data) {
-      app.addDexProgramData(data);
-      return self();
-    }
-
-    /** Add dex program-data. */
-    public B addDexProgramData(Collection<byte[]> data) {
-      app.addDexProgramData(data);
+    public B addDexProgramData(byte[] data, Origin origin) {
+      app.addDexProgramData(data, origin);
       return self();
     }
 
diff --git a/src/main/java/com/android/tools/r8/ExtractMarker.java b/src/main/java/com/android/tools/r8/ExtractMarker.java
index a32aa20..24dfb07 100644
--- a/src/main/java/com/android/tools/r8/ExtractMarker.java
+++ b/src/main/java/com/android/tools/r8/ExtractMarker.java
@@ -12,137 +12,40 @@
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.concurrent.ExecutionException;
 
 public class ExtractMarker {
-  private static class Command {
 
-    public static class Builder {
-      private boolean printHelp = false;
-      private boolean verbose;
-      private boolean summary;
-      private List<Path> programFiles = new ArrayList<>();
+  public static Marker extractMarkerFromDexFile(Path file) throws IOException, ExecutionException {
+    AndroidApp.Builder appBuilder = AndroidApp.builder();
+    appBuilder.setVdexAllowed();
+    appBuilder.addProgramFiles(FilteredClassPath.unfiltered(file));
+    return extractMarker(appBuilder.build());
+  }
 
-      public Builder setPrintHelp(boolean printHelp) {
-        this.printHelp = printHelp;
-        return this;
-      }
+  public static Marker extractMarkerFromDexProgramData(byte[] data)
+      throws IOException, ExecutionException {
+    AndroidApp app = AndroidApp.fromDexProgramData(data);
+    return extractMarker(app);
+  }
 
-      public boolean isPrintHelp() {
-        return printHelp;
-      }
-
-      public Builder setVerbose(boolean verbose) {
-        this.verbose = verbose;
-        return this;
-      }
-
-      public Builder setSummary(boolean summary) {
-        this.summary = summary;
-        return this;
-      }
-
-      public Builder addProgramFile(Path programFile) {
-        programFiles.add(programFile);
-        return this;
-      }
-
-      public ExtractMarker.Command build() throws CompilationException, IOException {
-        // If printing versions ignore everything else.
-        if (isPrintHelp()) {
-          return new ExtractMarker.Command(isPrintHelp());
-        }
-        return new ExtractMarker.Command(verbose, summary, programFiles);
-      }
-    }
-
-    static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
-        "Usage: extractmarker [options] <input-files>",
-        " where <input-files> are dex or vdex files",
-        "  --verbose               # More verbose output.",
-        "  --summary               # Print summary at the end.",
-        "  --help                  # Print this message."));
-
-    public static ExtractMarker.Command.Builder builder() {
-      return new Builder();
-    }
-
-    public static ExtractMarker.Command.Builder parse(String[] args)
-        throws CompilationException, IOException {
-      ExtractMarker.Command.Builder builder = builder();
-      parse(args, builder);
-      return builder;
-    }
-
-    private static void parse(String[] args, ExtractMarker.Command.Builder builder)
-        throws CompilationException, IOException {
-      for (int i = 0; i < args.length; i++) {
-        String arg = args[i].trim();
-        if (arg.length() == 0) {
-          continue;
-        } else if (arg.equals("--verbose")) {
-          builder.setVerbose(true);
-        } else if (arg.equals("--summary")) {
-          builder.setSummary(true);
-        } else if (arg.equals("--help")) {
-          builder.setPrintHelp(true);
-        } else {
-          if (arg.startsWith("--")) {
-            throw new CompilationException("Unknown option: " + arg);
-          }
-          builder.addProgramFile(Paths.get(arg));
-        }
-      }
-    }
-
-    private final boolean printHelp;
-    private final boolean verbose;
-    private final boolean summary;
-    private final List<Path> programFiles;
-
-    private Command(boolean verbose, boolean summary, List<Path> programFiles) {
-      this.printHelp = false;
-      this.verbose = verbose;
-      this.summary = summary;
-      this.programFiles = programFiles;
-    }
-
-    private Command(boolean printHelp) {
-      this.printHelp = printHelp;
-      this.verbose = false;
-      this.summary = false;
-      programFiles = ImmutableList.of();
-    }
-
-    public boolean isPrintHelp() {
-      return printHelp;
-    }
-
-    public List<Path> getProgramFiles() {
-      return programFiles;
-    }
-
-    public boolean getVerbose() {
-      return verbose;
-    }
-
-    public boolean getSummary() {
-      return summary;
-    }
+  private static Marker extractMarker(AndroidApp app) throws IOException, ExecutionException {
+    InternalOptions options = new InternalOptions();
+    options.skipReadingDexCode = true;
+    options.minApiLevel = AndroidApiLevel.P.getLevel();
+    DexApplication dexApp =
+        new ApplicationReader(app, options, new Timing("ExtractMarker")).read();
+    return dexApp.dexItemFactory.extractMarker();
   }
 
   public static void main(String[] args)
       throws IOException, CompilationException, ExecutionException {
-    ExtractMarker.Command.Builder builder = ExtractMarker.Command.parse(args);
-    ExtractMarker.Command command = builder.build();
+    ExtractMarkerCommand.Builder builder = ExtractMarkerCommand.parse(args);
+    ExtractMarkerCommand command = builder.build();
     if (command.isPrintHelp()) {
-      System.out.println(ExtractMarker.Command.USAGE_MESSAGE);
+      System.out.println(ExtractMarkerCommand.USAGE_MESSAGE);
       return;
     }
 
@@ -153,26 +56,17 @@
     int otherCount = 0;
     for (Path programFile : command.getProgramFiles()) {
       try {
-        InternalOptions options = new InternalOptions();
-        options.skipReadingDexCode = true;
-        options.minApiLevel = AndroidApiLevel.P.getLevel();
-        AndroidApp.Builder appBuilder = AndroidApp.builder();
-        appBuilder.setVdexAllowed();
-        appBuilder.addProgramFiles(FilteredClassPath.unfiltered(programFile));
-        DexApplication dexApp =
-            new ApplicationReader(appBuilder.build(), options, new Timing("ExtractMarker"))
-                .read();
-        Marker readMarker = dexApp.dexItemFactory.extractMarker();
         if (command.getVerbose()) {
           System.out.print(programFile);
           System.out.print(": ");
         }
-        if (readMarker == null) {
+        Marker marker = extractMarkerFromDexFile(programFile);
+        if (marker == null) {
           System.out.println("D8/R8 marker not found.");
           otherCount++;
         } else {
-          System.out.println(readMarker.toString());
-          if (readMarker.isD8()) {
+          System.out.println(marker.toString());
+          if (marker.isD8()) {
             d8Count++;
           } else {
             r8Count++;
diff --git a/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
new file mode 100644
index 0000000..729b50a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ExtractMarkerCommand.java
@@ -0,0 +1,127 @@
+// Copyright (c) 2017, 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;
+
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+
+class ExtractMarkerCommand {
+
+  public static class Builder {
+    private boolean printHelp = false;
+    private boolean verbose;
+    private boolean summary;
+    private List<Path> programFiles = new ArrayList<>();
+
+    public Builder setPrintHelp(boolean printHelp) {
+      this.printHelp = printHelp;
+      return this;
+    }
+
+    public boolean isPrintHelp() {
+      return printHelp;
+    }
+
+    public Builder setVerbose(boolean verbose) {
+      this.verbose = verbose;
+      return this;
+    }
+
+    public Builder setSummary(boolean summary) {
+      this.summary = summary;
+      return this;
+    }
+
+    public Builder addProgramFile(Path programFile) {
+      programFiles.add(programFile);
+      return this;
+    }
+
+    public ExtractMarkerCommand build() throws CompilationException, IOException {
+      // If printing versions ignore everything else.
+      if (isPrintHelp()) {
+        return new ExtractMarkerCommand(isPrintHelp());
+      }
+      return new ExtractMarkerCommand(verbose, summary, programFiles);
+    }
+  }
+
+  static final String USAGE_MESSAGE = String.join("\n", ImmutableList.of(
+      "Usage: extractmarker [options] <input-files>",
+      " where <input-files> are dex or vdex files",
+      "  --verbose               # More verbose output.",
+      "  --summary               # Print summary at the end.",
+      "  --help                  # Print this message."));
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static Builder parse(String[] args)
+      throws CompilationException, IOException {
+    Builder builder = builder();
+    parse(args, builder);
+    return builder;
+  }
+
+  private static void parse(String[] args, Builder builder)
+      throws CompilationException, IOException {
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i].trim();
+      if (arg.length() == 0) {
+        continue;
+      } else if (arg.equals("--verbose")) {
+        builder.setVerbose(true);
+      } else if (arg.equals("--summary")) {
+        builder.setSummary(true);
+      } else if (arg.equals("--help")) {
+        builder.setPrintHelp(true);
+      } else {
+        if (arg.startsWith("--")) {
+          throw new CompilationException("Unknown option: " + arg);
+        }
+        builder.addProgramFile(Paths.get(arg));
+      }
+    }
+  }
+
+  private final boolean printHelp;
+  private final boolean verbose;
+  private final boolean summary;
+  private final List<Path> programFiles;
+
+  private ExtractMarkerCommand(boolean verbose, boolean summary, List<Path> programFiles) {
+    this.printHelp = false;
+    this.verbose = verbose;
+    this.summary = summary;
+    this.programFiles = programFiles;
+  }
+
+  private ExtractMarkerCommand(boolean printHelp) {
+    this.printHelp = printHelp;
+    this.verbose = false;
+    this.summary = false;
+    programFiles = ImmutableList.of();
+  }
+
+  public boolean isPrintHelp() {
+    return printHelp;
+  }
+
+  public List<Path> getProgramFiles() {
+    return programFiles;
+  }
+
+  public boolean getVerbose() {
+    return verbose;
+  }
+
+  public boolean getSummary() {
+    return summary;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 44e1bf4..4368013 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -8,6 +8,8 @@
 
 public final class Version {
 
+  // This field is accessed from release scripts using simple pattern matching.
+  // Therefore, changing this field could break our release scripts.
   public static final String LABEL = "v0.2.0-dev";
 
   private Version() {
diff --git a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
index a1ff8b8..7a5f627 100644
--- a/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
+++ b/src/main/java/com/android/tools/r8/benchmarks/FrameworkIncrementalDexingBenchmark.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Command.Builder;
 import com.android.tools.r8.D8Output;
 import com.android.tools.r8.Resource;
 import com.android.tools.r8.origin.Origin;
@@ -141,21 +142,21 @@
     long start = System.nanoTime();
     for (int iteration = 0; iteration < ITERATIONS; iteration++) {
       int index = iteration * increment;
-      List<byte[]> inputs = new ArrayList<>(count);
+      Builder builder = D8Command.builder()
+          .setMinApiLevel(API)
+          .setIntermediate(true)
+          .setMode(CompilationMode.DEBUG)
+          .addClasspathResourceProvider(provider)
+          .addLibraryFiles(LIB)
+          .setOutputMode(OutputMode.FilePerInputClass)
+          .setEnableDesugaring(desugar);
       for (int j = 0; j < count; j++) {
-        provider.resources.get(descriptors.get(index + j));
+        builder.addClassProgramData(provider.resources.get(descriptors.get(index + j)),
+            Origin.unknown());
       }
       D8Output out =
           D8.run(
-              D8Command.builder()
-                  .setMinApiLevel(API)
-                  .setIntermediate(true)
-                  .setMode(CompilationMode.DEBUG)
-                  .addClassProgramData(inputs)
-                  .addClasspathResourceProvider(provider)
-                  .addLibraryFiles(LIB)
-                  .setOutputMode(OutputMode.FilePerInputClass)
-                  .setEnableDesugaring(desugar)
+              builder
                   .build(),
               executor);
       for (Resource resource : out.getDexResources()) {
@@ -174,22 +175,21 @@
       Map<String, Resource> outputs,
       ExecutorService executor)
       throws IOException, CompilationException, CompilationFailedException {
-    List<byte[]> bytes = new ArrayList<>(outputs.size());
+    Builder builder = D8Command.builder()
+        .setMinApiLevel(API)
+        .setIntermediate(false)
+        .setMode(CompilationMode.DEBUG)
+        .setOutputMode(OutputMode.Indexed)
+        .setEnableDesugaring(false);
     for (Resource input : outputs.values()) {
       try (InputStream inputStream = input.getStream()) {
-        bytes.add(ByteStreams.toByteArray(inputStream));
+        builder.addDexProgramData(ByteStreams.toByteArray(inputStream), input.origin);
       }
     }
     long start = System.nanoTime();
     D8Output out =
         D8.run(
-            D8Command.builder()
-                .setMinApiLevel(API)
-                .setIntermediate(false)
-                .setMode(CompilationMode.DEBUG)
-                .addDexProgramData(bytes)
-                .setOutputMode(OutputMode.Indexed)
-                .setEnableDesugaring(false) // never need to desugar when merging dex.
+            builder // never need to desugar when merging dex.
                 .build(),
             executor);
     printRuntimeNanoseconds(title("DexMerge", desugar), System.nanoTime() - start);
diff --git a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
index 8972a96..07496c8 100644
--- a/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
+++ b/src/main/java/com/android/tools/r8/compatdexbuilder/CompatDexBuilder.java
@@ -7,6 +7,8 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Output;
+import com.android.tools.r8.origin.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.ThreadUtils;
@@ -135,7 +137,9 @@
     try (InputStream stream = zipFile.getInputStream(classEntry)) {
       CompatDexBuilderCommandBuilder builder = new CompatDexBuilderCommandBuilder();
       builder
-          .addClassProgramData(ByteStreams.toByteArray(stream))
+          .addClassProgramData(ByteStreams.toByteArray(stream),
+              new ArchiveEntryOrigin(classEntry.getName(),
+                  new PathOrigin(Paths.get(zipFile.getName()))))
           .setMode(noLocals ? CompilationMode.RELEASE : CompilationMode.DEBUG)
           .setMinApiLevel(AndroidApiLevel.H_MR2.getLevel());
       return D8.run(builder.build(), executor);
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 463d484..e1f3316 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -51,6 +51,10 @@
     return tool == Tool.R8;
   }
 
+  public String getVersion() {
+    return (String) content.get("version");
+  }
+
   public Marker put(String key, int value) {
     // value is converted to Long ensuring equals works with the parsed json string.
     return internalPut(key, Long.valueOf(value));
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index 412a7a1..1cfd2ae 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -86,22 +86,24 @@
   }
 
   @Override
-  public boolean canBeDeadCode(IRCode code, InternalOptions options) {
-    // The const-string instruction can be a throwing instruction in DEX, if decode() fails,
-    // but not so in CF.
-    if (options.outputClassFiles) {
-      return true;
-    }
+  public boolean instructionInstanceCanThrow() {
+    // The const-string instruction can be a throwing instruction in DEX, if decode() fails.
     try {
       value.toString();
     } catch (RuntimeException e) {
       if (e.getCause() instanceof UTFDataFormatException) {
-        return false;
+        return true;
       } else {
         throw e;
       }
     }
-    return true;
+    return false;
+  }
+
+  @Override
+  public boolean canBeDeadCode(IRCode code, InternalOptions options) {
+    // No side-effect, such as throwing an exception, in CF.
+    return options.outputClassFiles || !instructionInstanceCanThrow();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 2790c03..bbf3a3a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -104,7 +104,6 @@
           && !current.outValue().isUsed()) {
         current.setOutValue(null);
       }
-      // Never remove instructions that can have side effects, except for const-class.
       if (!current.canBeDeadCode(code, options)) {
         continue;
       }
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index d08b0b7..e8e05cf 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -579,8 +579,9 @@
     /**
      * Add dex program-data.
      */
-    public Builder addDexProgramData(byte[]... data) {
-      return addDexProgramData(Arrays.asList(data));
+    public Builder addDexProgramData(byte[] data, Origin origin) {
+      addProgramResources(Kind.DEX, Resource.fromBytes(origin, data));
+      return this;
     }
 
     /**
@@ -596,13 +597,6 @@
     /**
      * Add Java-bytecode program data.
      */
-    public Builder addClassProgramData(byte[]... data) {
-      return addClassProgramData(Arrays.asList(data));
-    }
-
-    /**
-     * Add Java-bytecode program data.
-     */
     public Builder addClassProgramData(Collection<byte[]> data) {
       for (byte[] datum : data) {
         addProgramResources(Kind.CLASS, Resource.fromBytes(Origin.unknown(), datum));
diff --git a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
index 7842a6a..97818f2 100644
--- a/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
+++ b/src/test/apiUsageSample/com/android/tools/apiusagesample/D8Compiler.java
@@ -7,6 +7,7 @@
 import com.android.tools.r8.CompilationMode;
 import com.android.tools.r8.D8;
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.origin.PathOrigin;
 import com.android.tools.r8.utils.CompilationFailedException;
 import com.android.tools.r8.utils.OutputMode;
 import java.io.IOException;
@@ -99,7 +100,7 @@
         @Override
         public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
             throws IOException {
-          builder.addClassProgramData(Files.readAllBytes(path));
+          builder.addClassProgramData(Files.readAllBytes(path), new PathOrigin(path));
           return FileVisitResult.CONTINUE;
         }
       });
@@ -118,7 +119,7 @@
         @Override
         public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
             throws IOException {
-          merger.addDexProgramData(Files.readAllBytes(path));
+          merger.addDexProgramData(Files.readAllBytes(path), new PathOrigin(path));
           return FileVisitResult.CONTINUE;
         }
       });
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index c8ca57e..7f9b06f 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -183,7 +183,7 @@
     Resource mergeClassFiles(List<Resource> dexFiles, Path out) throws Throwable {
       D8Command.Builder builder = D8Command.builder();
       for (Resource dexFile : dexFiles) {
-        builder.addDexProgramData(readFromResource(dexFile));
+        builder.addDexProgramData(readFromResource(dexFile), dexFile.origin);
       }
       for (UnaryOperator<D8Command.Builder> transformation : builderTransformations) {
         builder = transformation.apply(builder);
diff --git a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
index 1c68d75..f8b388b 100644
--- a/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8LazyRunExamplesAndroidOTest.java
@@ -127,7 +127,7 @@
     D8Command.Builder builder = D8Command.builder()
         .setMinApiLevel(minAPILevel);
     for (Resource resource : individalDexes) {
-      builder.addDexProgramData(readFromResource(resource));
+      builder.addDexProgramData(readFromResource(resource), resource.origin);
     }
     AndroidApp mergedResult = ToolHelper.runD8(builder.build(),
         options -> options.setMarker(null));
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
new file mode 100644
index 0000000..fd1bfcb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -0,0 +1,31 @@
+// Copyright (c) 2017, 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;
+
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.utils.CompilationFailedException;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.concurrent.ExecutionException;
+import org.junit.Test;
+
+public class ExtractMarkerTest {
+
+  @Test
+  public void extractMarkerTest()
+      throws CompilationFailedException, IOException, ExecutionException {
+    String classFile = ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+    D8Command command = D8Command.builder()
+            .addProgramFiles(Paths.get(classFile))
+            .build();
+    D8Output output = D8.run(command);
+    byte[] data = ByteStreams.toByteArray(output.getDexResources().get(0).getStream());
+    Marker marker = ExtractMarker.extractMarkerFromDexProgramData(data);
+    assert marker != null;
+    assert marker.getTool() == Tool.D8;
+    assert marker.getVersion().equals(Version.LABEL);
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
index 8480791..7c1ce1b 100644
--- a/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
+++ b/src/test/java/com/android/tools/r8/compatproguard/ForNameTest.java
@@ -97,7 +97,7 @@
     Path dexOutputDir = temp.newFolder().toPath();
     R8Command command =
         new CompatProguardCommandBuilder(true, true)
-            .addDexProgramData(builder.compile())
+            .addDexProgramData(builder.compile(), Origin.unknown())
             .setOutputPath(dexOutputDir)
             .addProguardConfiguration(proguardConfigurations, Origin.unknown())
             .build();
diff --git a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
index 3376cb8..07a4463 100644
--- a/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
+++ b/src/test/java/com/android/tools/r8/debug/BlockReorderingTest.java
@@ -3,8 +3,12 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import org.junit.Assume;
 import org.junit.BeforeClass;
 import org.junit.Test;
@@ -17,15 +21,24 @@
   public static final String CLASS = "BlockReordering";
   public static final String FILE = "BlockReordering.java";
 
-  private static DebuggeePath debuggeePath;
+  private static DebugTestConfig dexConfig;
 
   @BeforeClass
-  public static void initDebuggeePath() throws Exception {
+  public static void setup() throws Exception {
     // Force inversion of all conditionals to reliably construct a regression test for incorrect
     // line information when reordering blocks.
-    debuggeePath =
-        DebuggeePath.makeDex(
-            compileToDex(DEBUGGEE_JAR, options -> options.testing.invertConditionals = true));
+    Path result = temp.newFolder().toPath().resolve("inverted_conditionals.jar");
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    ToolHelper.runD8(
+        D8Command.builder()
+            .addProgramFiles(DEBUGGEE_JAR)
+            .setOutputPath(result)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+            .build(),
+        options -> options.testing.invertConditionals = true);
+    dexConfig = new D8BaseDebugTestConfig(temp, result);
   }
 
   @Test
@@ -35,7 +48,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "conditionalReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
@@ -58,7 +71,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "invertConditionalReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
@@ -81,7 +94,7 @@
         ToolHelper.getDexVm().getVersion().isNewerThan(Version.V6_0_1));
     final String method = "fallthroughReturn";
     runDebugTest(
-        debuggeePath,
+        dexConfig,
         CLASS,
         breakpoint(CLASS, method),
         run(),
diff --git a/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java
new file mode 100644
index 0000000..ec6038c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/CfBaseDebugTestConfig.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2017, 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.debug;
+
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+
+public class CfBaseDebugTestConfig extends DebugTestConfig {
+
+  public static final Path JDWP_JAR = ToolHelper.getJdwpTestsJarPath(AndroidApiLevel.N.getLevel());
+
+  @Override
+  public RuntimeKind getRuntimeKind() {
+    return RuntimeKind.CF;
+  }
+
+  @Override
+  public List<Path> getPaths() {
+    return ImmutableList.of(JDWP_JAR);
+  }
+
+  @Override
+  public Path getProguardMap() {
+    return null;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java
new file mode 100644
index 0000000..10c6191
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/D8BaseDebugTestConfig.java
@@ -0,0 +1,95 @@
+// Copyright (c) 2017, 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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.rules.TemporaryFolder;
+
+public class D8BaseDebugTestConfig extends DebugTestConfig {
+
+  public static final Path JDWP_JAR =
+      ToolHelper.getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
+
+  // Internal cache to avoid multiple compilations of the base JDWP code.
+  private static AndroidApp compiledJdwp = null;
+
+  private static synchronized AndroidApp getCompiledJdwp() {
+    if (compiledJdwp == null) {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      try {
+        compiledJdwp =
+            ToolHelper.runD8(
+                D8Command.builder()
+                    .addProgramFiles(JDWP_JAR)
+                    .setMinApiLevel(minSdk)
+                    .setMode(CompilationMode.DEBUG)
+                    .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                    .build());
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+    return compiledJdwp;
+  }
+
+  private final List<Path> paths = new ArrayList<>();
+
+  private Path proguardMap = null;
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp) {
+    this(temp, ImmutableList.of());
+  }
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp, Path... paths) {
+    this(temp, Arrays.asList(paths));
+  }
+
+  public D8BaseDebugTestConfig(TemporaryFolder temp, List<Path> paths) {
+    addPaths(paths);
+    try {
+      Path out = temp.newFolder().toPath().resolve("d8_jdwp.jar");
+      getCompiledJdwp().write(out, OutputMode.Indexed);
+      addPaths(out);
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public RuntimeKind getRuntimeKind() {
+    return RuntimeKind.DEX;
+  }
+
+  @Override
+  public List<Path> getPaths() {
+    return paths;
+  }
+
+  @Override
+  public Path getProguardMap() {
+    return proguardMap;
+  }
+
+  public void addPaths(Path... paths) {
+    addPaths(Arrays.asList(paths));
+  }
+
+  public void addPaths(List<Path> paths) {
+    this.paths.addAll(paths);
+  }
+
+  public void setProguardMap(Path proguardMap) {
+    this.proguardMap = proguardMap;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.java b/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.java
new file mode 100644
index 0000000..448949c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/D8DebugTestResourcesConfig.java
@@ -0,0 +1,46 @@
+// Copyright (c) 2017, 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.debug;
+
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.rules.TemporaryFolder;
+
+// Shared test configuration for D8 compiled resources from the "debugTestResources" target.
+public class D8DebugTestResourcesConfig extends D8BaseDebugTestConfig {
+
+  private static AndroidApp compiledResources = null;
+
+  private static synchronized AndroidApp getCompiledResources() throws Throwable {
+    if (compiledResources == null) {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      compiledResources =
+          ToolHelper.runD8(
+              D8Command.builder()
+                  .addProgramFiles(DebugTestBase.DEBUGGEE_JAR)
+                  .setMinApiLevel(minSdk)
+                  .setMode(CompilationMode.DEBUG)
+                  .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                  .build(),
+              null);
+    }
+    return compiledResources;
+  }
+
+  public D8DebugTestResourcesConfig(TemporaryFolder temp) {
+    super(temp);
+    try {
+      Path path = temp.newFolder().toPath().resolve("d8_debug_test_resources.jar");
+      getCompiledResources().write(path, OutputMode.Indexed);
+      addPaths(path);
+    } catch (Throwable e) {
+      throw new RuntimeException(e);
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
index 510aea1..692dd13 100644
--- a/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestBase.java
@@ -11,8 +11,7 @@
 import com.android.tools.r8.ToolHelper.ArtCommandBuilder;
 import com.android.tools.r8.ToolHelper.DexVm;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.debug.DebugTestConfig.RuntimeKind;
 import com.android.tools.r8.naming.ClassNameMapper;
 import com.android.tools.r8.naming.ClassNamingForNameMapper;
 import com.android.tools.r8.naming.MemberNaming;
@@ -31,7 +30,6 @@
 import it.unimi.dsi.fastutil.longs.LongList;
 import java.io.File;
 import java.io.IOException;
-import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.ArrayDeque;
@@ -74,7 +72,6 @@
 import org.apache.harmony.jpda.tests.share.JPDATestOptions;
 import org.junit.Assert;
 import org.junit.Assume;
-import org.junit.BeforeClass;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -93,104 +90,50 @@
   public static final StepFilter INTELLIJ_FILTER = new StepFilter.IntelliJStepFilter();
   private static final StepFilter DEFAULT_FILTER = NO_FILTER;
 
-  enum DexCompilerKind {
-    DX,
-    D8
-  }
-
-  enum BinaryKind {
-    CF,
-    DEX
-  }
-
   protected static class DebuggeePath {
-    public final BinaryKind kind;
+    public final RuntimeKind kind;
     public final Path path;
 
     public static DebuggeePath makeDex(Path path) {
-      return new DebuggeePath(BinaryKind.DEX, path);
+      return new DebuggeePath(DebugTestConfig.RuntimeKind.DEX, path);
     }
 
     public static DebuggeePath makeClassFile(Path path) {
-      return new DebuggeePath(BinaryKind.CF, path);
+      return new DebuggeePath(DebugTestConfig.RuntimeKind.CF, path);
     }
 
-    public DebuggeePath(BinaryKind kind, Path path) {
+    public DebuggeePath(RuntimeKind kind, Path path) {
       this.kind = kind;
       this.path = path;
     }
   }
 
-  private static final DexCompilerKind DEX_COMPILER_KIND = DexCompilerKind.D8;
-
   private static final int FIRST_LINE = -1;
 
   // Set to true to enable verbose logs
   private static final boolean DEBUG_TESTS = false;
 
-  private static final Path JDWP_JAR = ToolHelper
-      .getJdwpTestsJarPath(ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm()));
-  protected static final Path DEBUGGEE_JAR =
+  public static final Path DEBUGGEE_JAR =
       Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources.jar");
+
   private static final Path DEBUGGEE_JAVA8_JAR = Paths
       .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_java8.jar");
-  private static final Path DEBUGGEE_KOTLIN_JAR = Paths
-      .get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
   private static final String PROGUARD_MAP_FILENAME = "proguard.map";
 
   @ClassRule
-  public static TemporaryFolder temp = new TemporaryFolder();
-
-  // TODO(tamaskenez): Separate test setup from test runner.
-  private static Path jdwpDexD8;
-  private static Path debuggeeDexD8;
-  private static Path debuggeeJava8DexD8;
-  private static Path debuggeeKotlinDexD8;
+  public static TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @Rule
   public TestName testName = new TestName();
 
-  @BeforeClass
-  public static void setUp() throws Exception {
-    jdwpDexD8 = compileToDex(JDWP_JAR, null);
-    debuggeeDexD8 = null;
-    debuggeeJava8DexD8 = null;
-    debuggeeKotlinDexD8 = null;
-  }
-
-  protected static synchronized Path getDebuggeeDexD8()
+  private static Path getDebuggeeJava8DexD8()
       throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeDexD8 == null) {
-      debuggeeDexD8 = compileToDex(DEBUGGEE_JAR, null);
-    }
-    return debuggeeDexD8;
-  }
-
-  private static synchronized Path getDebuggeeJava8DexD8()
-      throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeJava8DexD8 == null) {
-      debuggeeJava8DexD8 =
-          compileToDex(
-              DEBUGGEE_JAVA8_JAR,
-              options -> {
-                // Enable desugaring for preN runtimes
-                options.interfaceMethodDesugaring = OffOrAuto.Auto;
-              });
-    }
-    return debuggeeJava8DexD8;
-  }
-
-  private static synchronized Path getDebuggeeKotlinDexD8()
-      throws IOException, CompilationException, CompilationFailedException {
-    if (debuggeeKotlinDexD8 == null) {
-      debuggeeKotlinDexD8 = compileToDex(DEBUGGEE_KOTLIN_JAR, null);
-    }
-    return debuggeeKotlinDexD8;
-  }
-
-  protected static DebuggeePath getDebuggeeDexD8OrCf(boolean cf)
-      throws IOException, CompilationException, CompilationFailedException {
-    return cf ? DebuggeePath.makeClassFile(DEBUGGEE_JAR) : DebuggeePath.makeDex(getDebuggeeDexD8());
+    return compileToDex(
+        DEBUGGEE_JAVA8_JAR,
+        options -> {
+          // Enable desugaring for preN runtimes
+          options.interfaceMethodDesugaring = OffOrAuto.Auto;
+        });
   }
 
   protected static DebuggeePath getDebuggeeJava8DexD8OrCf(boolean cf)
@@ -200,46 +143,20 @@
         : DebuggeePath.makeDex(getDebuggeeJava8DexD8());
   }
 
-  protected static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
-      throws IOException, CompilationException, CompilationFailedException {
-    return compileToDex(DEX_COMPILER_KIND, jarToCompile, optionsConsumer);
-  }
-
-  static Path compileToDex(
-      DexCompilerKind compiler, Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
+  static Path compileToDex(Path jarToCompile, Consumer<InternalOptions> optionsConsumer)
       throws IOException, CompilationException, CompilationFailedException {
     int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
     assert jarToCompile.toFile().exists();
     Path dexOutputDir = temp.newFolder().toPath();
-    switch (compiler) {
-      case D8:
-        {
-          ToolHelper.runD8(
-              D8Command.builder()
-                  .addProgramFiles(jarToCompile)
-                  .setOutputPath(dexOutputDir)
-                  .setMinApiLevel(minSdk)
-                  .setMode(CompilationMode.DEBUG)
-                  .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
-                  .build(),
-              optionsConsumer);
-          break;
-        }
-      case DX:
-        {
-          ProcessResult result =
-              ToolHelper.runDX(
-                  new String[] {
-                    "--output=" + dexOutputDir,
-                    "--min-sdk-version=" + minSdk,
-                    jarToCompile.toString()
-                  });
-          Assert.assertEquals(result.stderr, 0, result.exitCode);
-          break;
-        }
-      default:
-        throw new Unreachable();
-    }
+    ToolHelper.runD8(
+        D8Command.builder()
+            .addProgramFiles(jarToCompile)
+            .setOutputPath(dexOutputDir)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+            .build(),
+        optionsConsumer);
     return dexOutputDir.resolve("classes.dex");
   }
 
@@ -276,9 +193,9 @@
     return dexOutputDir.resolve("classes.dex");
   }
 
-  private BinaryKind currentlyRunningBinaryKind = null;
+  private RuntimeKind currentlyRunningBinaryKind = null;
 
-  protected final BinaryKind getCurrentlyRunningBinaryKind() {
+  protected final RuntimeKind getCurrentlyRunningBinaryKind() {
     if (currentlyRunningBinaryKind == null) {
       throw new RuntimeException("Nothing is running currently.");
     }
@@ -291,63 +208,42 @@
   }
 
   protected final boolean isRunningJava() {
-    return getCurrentlyRunningBinaryKind() == BinaryKind.CF;
+    return getCurrentlyRunningBinaryKind() == DebugTestConfig.RuntimeKind.CF;
   }
 
   protected final boolean isRunningArt() {
-    return getCurrentlyRunningBinaryKind() == BinaryKind.DEX;
+    return getCurrentlyRunningBinaryKind() == DebugTestConfig.RuntimeKind.DEX;
+  }
+
+  protected final void runDebugTest(
+      DebugTestConfig config, String debuggeeClass, JUnit3Wrapper.Command... commands)
+      throws Throwable {
+    runInternal(config, debuggeeClass, Arrays.asList(commands));
   }
 
   protected final void runDebugTest(String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        Arrays.asList(commands));
-  }
-
-  protected final void runDebugTest(List<Path> extraPaths, String debuggeeClass,
-      JUnit3Wrapper.Command... commands) throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        extraPaths,
-        debuggeeClass,
-        Arrays.asList(commands));
+    runDebugTest(debuggeeClass, Arrays.asList(commands));
   }
 
   protected final void runDebugTest(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeDexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        commands);
+    runInternal(new D8DebugTestResourcesConfig(temp), debuggeeClass, commands);
   }
 
-  protected final void runDebugTestJava8(String debuggeeClass, JUnit3Wrapper.Command... commands)
+  protected final void runDebugTest(
+      List<Path> extraPaths, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        Arrays.asList(commands));
-  }
-
-  protected final void runDebugTestJava8(String debuggeeClass, List<JUnit3Wrapper.Command> commands)
-      throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeJava8DexD8()),
-        Collections.<Path>emptyList(),
-        debuggeeClass,
-        commands);
-  }
-
-  protected final void runDebugTestKotlin(String debuggeeClass, JUnit3Wrapper.Command... commands)
-      throws Throwable {
-    runDebugTest(
-        DebuggeePath.makeDex(getDebuggeeKotlinDexD8()),
-        Collections.<Path>emptyList(),
+    runInternal(
+        new D8DebugTestResourcesConfig(temp) {
+          @Override
+          public List<Path> getPaths() {
+            return new ImmutableList.Builder<Path>()
+                .addAll(super.getPaths())
+                .addAll(extraPaths)
+                .build();
+          }
+        },
         debuggeeClass,
         Arrays.asList(commands));
   }
@@ -355,14 +251,13 @@
   protected void runDebugTest(
       DebuggeePath debuggeePath, String debuggeeClass, JUnit3Wrapper.Command... commands)
       throws Throwable {
-    runDebugTest(
-        debuggeePath, Collections.<Path>emptyList(), debuggeeClass, Arrays.asList(commands));
+    runDebugTest(debuggeePath, ImmutableList.of(), debuggeeClass, Arrays.asList(commands));
   }
 
   protected void runDebugTest(
       DebuggeePath debuggeePath, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
       throws Throwable {
-    runDebugTest(debuggeePath, Collections.<Path>emptyList(), debuggeeClass, commands);
+    runDebugTest(debuggeePath, ImmutableList.of(), debuggeeClass, commands);
   }
 
   protected void runDebugTest(
@@ -380,6 +275,38 @@
       String debuggeeClass,
       List<JUnit3Wrapper.Command> commands)
       throws Throwable {
+    DebugTestConfig debuggeeConfig;
+    if (debuggeePath.kind == DebugTestConfig.RuntimeKind.CF) {
+      debuggeeConfig =
+          new CfBaseDebugTestConfig() {
+            @Override
+            public List<Path> getPaths() {
+              return new ImmutableList.Builder<Path>()
+                  .addAll(super.getPaths())
+                  .addAll(extraPaths)
+                  .add(debuggeePath.path)
+                  .build();
+            }
+          };
+    } else {
+      debuggeeConfig =
+          new D8BaseDebugTestConfig(temp) {
+            @Override
+            public List<Path> getPaths() {
+              return new ImmutableList.Builder<Path>()
+                  .addAll(super.getPaths())
+                  .addAll(extraPaths)
+                  .add(debuggeePath.path)
+                  .build();
+            }
+          };
+    }
+    runInternal(debuggeeConfig, debuggeeClass, commands);
+  }
+
+  private void runInternal(
+      DebugTestConfig config, String debuggeeClass, List<JUnit3Wrapper.Command> commands)
+      throws Throwable {
     // Skip test due to unsupported runtime.
     Assume.assumeTrue("Skipping test " + testName.getMethodName() + " because ART is not supported",
         ToolHelper.artSupported());
@@ -387,25 +314,19 @@
             + " because debug tests are not yet supported on Windows",
         !ToolHelper.isWindows());
 
-    String[] paths = new String[extraPaths.size() + 2];
-    int indexPath = 0;
-    ClassNameMapper classNameMapper = null;
-    if (debuggeePath.kind == BinaryKind.CF) {
-      paths[indexPath++] = JDWP_JAR.toString();
-    } else {
-      paths[indexPath++] = jdwpDexD8.toString();
-      Path proguardMapPath = debuggeePath.path.resolveSibling(PROGUARD_MAP_FILENAME);
-      if (Files.exists(proguardMapPath)) {
-        classNameMapper = ClassNameMapper.mapperFromFile(proguardMapPath);
-      }
-    }
-    paths[indexPath++] = debuggeePath.path.toString();
-    for (Path extraPath : extraPaths) {
-      paths[indexPath++] = extraPath.toString();
-    }
+    ClassNameMapper classNameMapper =
+        config.getProguardMap() == null
+            ? null
+            : ClassNameMapper.mapperFromFile(config.getProguardMap());
 
-    currentlyRunningBinaryKind = debuggeePath.kind;
-    new JUnit3Wrapper(debuggeeClass, paths, commands, classNameMapper, isRunningArt()).runBare();
+    currentlyRunningBinaryKind = config.getRuntimeKind();
+    new JUnit3Wrapper(
+            debuggeeClass,
+            config.getPaths().stream().map(Path::toString).toArray(String[]::new),
+            commands,
+            classNameMapper,
+            isRunningArt())
+        .runBare();
   }
 
   protected final JUnit3Wrapper.Command run() {
diff --git a/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
new file mode 100644
index 0000000..32df428
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debug/DebugTestConfig.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2017, 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.debug;
+
+import java.nio.file.Path;
+import java.util.List;
+
+public abstract class DebugTestConfig {
+
+  public enum RuntimeKind {
+    CF,
+    DEX
+  }
+
+  public abstract RuntimeKind getRuntimeKind();
+
+  public abstract List<Path> getPaths();
+
+  public abstract Path getProguardMap();
+
+  public boolean isCfRuntime() {
+    return getRuntimeKind() == RuntimeKind.CF;
+  }
+
+  public boolean isDexRuntime() {
+    return getRuntimeKind() == RuntimeKind.DEX;
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
index 6f9c726..84af8f9 100644
--- a/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/JasminDebugTest.java
@@ -3,6 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.jasmin.JasminBuilder;
 import com.android.tools.r8.jasmin.JasminBuilder.ClassBuilder;
@@ -13,6 +15,7 @@
 import java.io.StringReader;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 import java.util.List;
 import org.junit.Rule;
@@ -22,7 +25,48 @@
 // TODO(b/65474850) Should we build Jasmin at compile time or runtime ?
 public class JasminDebugTest extends DebugTestBase {
 
-  public static final boolean RUN_JAVA = false;
+  static class JasminTestConfig extends D8BaseDebugTestConfig {
+
+    public JasminTestConfig(TemporaryFolder temp, JasminBuilder builder) {
+      super(temp, compile(temp, builder));
+    }
+
+    private static Path compile(TemporaryFolder temp, JasminBuilder builder) {
+      try {
+        ImmutableList<ClassBuilder> classes = builder.getClasses();
+        File out = temp.newFolder();
+        List<Path> classFiles = new ArrayList<>(classes.size());
+        for (ClassBuilder clazz : classes) {
+          ClassFile file = new ClassFile();
+          file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
+          Path path = out.toPath().resolve(clazz.name + ".class");
+          Files.createDirectories(path.getParent());
+          try (OutputStream outputStream = Files.newOutputStream(path)) {
+            file.write(outputStream);
+          }
+          classFiles.add(path);
+        }
+        return compile(temp, classFiles);
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    private static Path compile(TemporaryFolder temp, List<Path> paths) throws Throwable {
+      int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+      Path out = temp.newFolder().toPath().resolve("d8_jasmin.jar");
+      ToolHelper.runD8(
+          D8Command.builder()
+              .addProgramFiles(paths)
+              .setOutputPath(out)
+              .setMinApiLevel(minSdk)
+              .setMode(CompilationMode.DEBUG)
+              .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+              .build(),
+          null);
+      return out;
+    }
+  }
 
   @Rule
   public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
@@ -32,10 +76,8 @@
     final String className = "UselessCheckCast";
     final String sourcefile = className + ".j";
     final String methodName = "test";
-    List<Path> paths = getExtraPaths(getBuilderForUselessCheckcast(className, methodName));
     runDebugTest(
-        getDebuggeeDexD8OrCf(RUN_JAVA),
-        paths,
+        new JasminTestConfig(temp, getBuilderForUselessCheckcast(className, methodName)),
         className,
         breakpoint(className, methodName),
         run(),
@@ -81,27 +123,4 @@
 
     return builder;
   }
-
-  private List<Path> getExtraPaths(JasminBuilder builder) throws Exception {
-    ImmutableList<ClassBuilder> classes = builder.getClasses();
-    List<Path> extraPaths = new ArrayList<>(classes.size());
-    File out = temp.newFolder();
-
-    for (ClassBuilder clazz : classes) {
-      ClassFile file = new ClassFile();
-      file.readJasmin(new StringReader(clazz.toString()), clazz.name, false);
-      Path path = out.toPath().resolve(clazz.name + ".class");
-      Files.createDirectories(path.getParent());
-      try (OutputStream outputStream = Files.newOutputStream(path)) {
-        file.write(outputStream);
-      }
-      if (RUN_JAVA) {
-        extraPaths.add(path);
-      } else {
-        extraPaths.add(compileToDex(path, null));
-      }
-    }
-
-    return extraPaths;
-  }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
index a0a9c3f..50da158 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinDebugTestBase.java
@@ -4,16 +4,70 @@
 
 package com.android.tools.r8.debug;
 
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.D8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.OutputMode;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Arrays;
 import java.util.List;
 import org.apache.harmony.jpda.tests.framework.jdwp.Frame.Variable;
 import org.apache.harmony.jpda.tests.framework.jdwp.Location;
+import org.junit.BeforeClass;
+import org.junit.rules.TemporaryFolder;
 
 /**
  * A specialization for Kotlin-based tests which provides extra commands.
  */
 public abstract class KotlinDebugTestBase extends DebugTestBase {
 
+  private static final Path DEBUGGEE_KOTLIN_JAR =
+      Paths.get(ToolHelper.BUILD_DIR, "test", "debug_test_resources_kotlin.jar");
+
+  protected static class KotlinD8Config extends D8BaseDebugTestConfig {
+
+    private static AndroidApp compiledResources = null;
+
+    private static synchronized AndroidApp getCompiledResources() throws Throwable {
+      if (compiledResources == null) {
+        int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+        compiledResources =
+            ToolHelper.runD8(
+                D8Command.builder()
+                    .addProgramFiles(DEBUGGEE_KOTLIN_JAR)
+                    .setMinApiLevel(minSdk)
+                    .setMode(CompilationMode.DEBUG)
+                    .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)))
+                    .build());
+      }
+      return compiledResources;
+    }
+
+    public KotlinD8Config(TemporaryFolder temp) {
+      super(temp);
+      try {
+        Path out = temp.newFolder().toPath().resolve("d8_debug_test_resources_kotlin.jar");
+        getCompiledResources().write(out, OutputMode.Indexed);
+        addPaths(out);
+      } catch (Throwable e) {
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  private static KotlinD8Config d8Config;
+
+  @BeforeClass
+  public static void setup() {
+    d8Config = new KotlinD8Config(temp);
+  }
+
+  protected KotlinD8Config getD8Config() {
+    return d8Config;
+  }
+
   protected final JUnit3Wrapper.Command kotlinStepOver() {
     return testBaseBeforeStep -> {
       final JUnit3Wrapper.DebuggeeState debuggeeStateBeforeStep = testBaseBeforeStep
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
index 8e9d145..cdba21b 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinInlineTest.java
@@ -7,7 +7,6 @@
 import static org.junit.Assert.assertTrue;
 
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
-import org.junit.Ignore;
 import org.junit.Test;
 
 // TODO check double-depth inline (an inline in another inline)
@@ -16,7 +15,9 @@
   @Test
   public void testStepOverInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -48,7 +49,9 @@
   @Test
   public void testStepIntoInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -84,7 +87,9 @@
   @Test
   public void testStepOutInline() throws Throwable {
     String methodName = "singleInline";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", methodName),
         run(),
         inspect(s -> {
@@ -121,7 +126,9 @@
   @Test
   public void testKotlinInline() throws Throwable {
     final String inliningMethodName = "invokeInlinedFunctions";
-    runDebugTestKotlin("KotlinInline",
+    runDebugTest(
+        getD8Config(),
+        "KotlinInline",
         breakpoint("KotlinInline", inliningMethodName),
         run(),
         inspect(s -> {
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinTest.java b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
index 2baa426..5623d6d 100644
--- a/src/test/java/com/android/tools/r8/debug/KotlinTest.java
+++ b/src/test/java/com/android/tools/r8/debug/KotlinTest.java
@@ -14,7 +14,9 @@
   // TODO(shertz) add more variables ?
   @Test
   public void testStepOver() throws Throwable {
-    runDebugTestKotlin("KotlinApp",
+    runDebugTest(
+        getD8Config(),
+        "KotlinApp",
         breakpoint("KotlinApp$Companion", "main"),
         run(),
         inspect(s -> {
@@ -44,7 +46,9 @@
 
   @Test
   public void testStepIntoAndOut() throws Throwable {
-    runDebugTestKotlin("KotlinApp",
+    runDebugTest(
+        getD8Config(),
+        "KotlinApp",
         breakpoint("KotlinApp$Companion", "main"),
         run(),
         inspect(s -> {
diff --git a/src/test/java/com/android/tools/r8/debug/LocalsTest.java b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
index 76f8870..f240875 100644
--- a/src/test/java/com/android/tools/r8/debug/LocalsTest.java
+++ b/src/test/java/com/android/tools/r8/debug/LocalsTest.java
@@ -12,7 +12,6 @@
 import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants.Tag;
 import org.apache.harmony.jpda.tests.framework.jdwp.Value;
 import org.junit.Assert;
-import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -20,8 +19,6 @@
  */
 public class LocalsTest extends DebugTestBase {
 
-  private static final boolean RUN_JAVA = false;
-
   public static final String SOURCE_FILE = "Locals.java";
 
   @Test
@@ -726,6 +723,6 @@
     commands.add(checkLocal("i", Value.createInt(0)));
     commands.add(run());
 
-    runDebugTest(getDebuggeeDexD8OrCf(RUN_JAVA), className, commands);
+    runDebugTest(className, commands);
   }
 }
diff --git a/src/test/java/com/android/tools/r8/debug/MinificationTest.java b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
index 1e439c7..3b1985a 100644
--- a/src/test/java/com/android/tools/r8/debug/MinificationTest.java
+++ b/src/test/java/com/android/tools/r8/debug/MinificationTest.java
@@ -3,20 +3,20 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.debug;
 
-import com.android.tools.r8.CompilationException;
 import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8Command;
 import com.android.tools.r8.TestBase.MinifyMode;
-import com.android.tools.r8.shaking.ProguardRuleParserException;
-import com.android.tools.r8.utils.CompilationFailedException;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
 import com.google.common.collect.ImmutableList;
-import java.io.IOException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.concurrent.ExecutionException;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
@@ -24,22 +24,7 @@
 @RunWith(Parameterized.class)
 public class MinificationTest extends DebugTestBase {
 
-  public static final String SOURCE_FILE = "Minified.java";
-  private static final HashMap<Config, Path> debuggeePathMap = new HashMap<>();
-
-  private static class Config {
-    public final MinifyMode minificationMode;
-    public final boolean writeProguardMap;
-
-    Config(MinifyMode minificationMode, boolean writeProguardMap) {
-      this.minificationMode = minificationMode;
-      this.writeProguardMap = writeProguardMap;
-    }
-
-    public boolean minifiedNames() {
-      return minificationMode.isMinify() && !writeProguardMap;
-    }
-  }
+  private static final String SOURCE_FILE = "Minified.java";
 
   @Parameterized.Parameters(name = "minification: {0}, proguardMap: {1}")
   public static Collection minificationControl() {
@@ -53,51 +38,68 @@
     return builder.build();
   }
 
-  private final Config config;
+  @Rule
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
-  private synchronized DebuggeePath getDebuggeePath()
-      throws IOException, CompilationException, ExecutionException, ProguardRuleParserException,
-      CompilationFailedException {
-    Path path = debuggeePathMap.get(config);
-    if (path == null) {
-      List<String> proguardConfigurations = Collections.<String>emptyList();
-      if (config.minificationMode.isMinify()) {
-        ImmutableList.Builder<String> builder = ImmutableList.builder();
-        builder.add("-keep public class Minified { public static void main(java.lang.String[]); }");
-        builder.add("-keepattributes SourceFile");
-        builder.add("-keepattributes LineNumberTable");
-        if (config.minificationMode == MinifyMode.AGGRESSIVE) {
-          builder.add("-overloadaggressively");
-        }
-        proguardConfigurations = builder.build();
-      }
-      path =
-          compileToDexViaR8(
-              null,
-              null,
-              DEBUGGEE_JAR,
-              proguardConfigurations,
-              config.writeProguardMap,
-              CompilationMode.DEBUG);
-      debuggeePathMap.put(config, path);
-    }
-    return DebuggeePath.makeDex(path);
-  }
+  private final MinifyMode minificationMode;
+  private final boolean writeProguardMap;
 
   public MinificationTest(MinifyMode minificationMode, boolean writeProguardMap) throws Exception {
-    config = new Config(minificationMode, writeProguardMap);
+    this.minificationMode = minificationMode;
+    this.writeProguardMap = writeProguardMap;
+  }
+
+  private boolean minifiedNames() {
+    return minificationMode.isMinify() && !writeProguardMap;
+  }
+
+  private DebugTestConfig getTestConfig() throws Throwable {
+    List<String> proguardConfigurations = Collections.emptyList();
+    if (minificationMode.isMinify()) {
+      ImmutableList.Builder<String> builder = ImmutableList.builder();
+      builder.add("-keep public class Minified { public static void main(java.lang.String[]); }");
+      builder.add("-keepattributes SourceFile");
+      builder.add("-keepattributes LineNumberTable");
+      if (minificationMode == MinifyMode.AGGRESSIVE) {
+        builder.add("-overloadaggressively");
+      }
+      proguardConfigurations = builder.build();
+    }
+
+    int minSdk = ToolHelper.getMinApiLevelForDexVm(ToolHelper.getDexVm());
+    Path dexOutputDir = temp.newFolder().toPath();
+    Path proguardMap = writeProguardMap ? dexOutputDir.resolve("proguard.map") : null;
+    R8Command.Builder builder =
+        R8Command.builder()
+            .addProgramFiles(DEBUGGEE_JAR)
+            .setOutputPath(dexOutputDir)
+            .setMinApiLevel(minSdk)
+            .setMode(CompilationMode.DEBUG)
+            .addLibraryFiles(Paths.get(ToolHelper.getAndroidJar(minSdk)));
+    if (proguardMap != null) {
+      builder.setProguardMapOutput(proguardMap);
+    }
+    if (!proguardConfigurations.isEmpty()) {
+      builder.addProguardConfiguration(proguardConfigurations, Origin.unknown());
+    }
+    ToolHelper.runR8(builder.build());
+
+    D8BaseDebugTestConfig config =
+        new D8BaseDebugTestConfig(temp, dexOutputDir.resolve("classes.dex"));
+    config.setProguardMap(proguardMap);
+    return config;
   }
 
   @Test
   public void testBreakInMainClass() throws Throwable {
     final String className = "Minified";
-    final String methodName = config.minifiedNames() ? "a" : "test";
+    final String methodName = minifiedNames() ? "a" : "test";
     final String signature = "()V";
-    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
-    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
+    final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
     runDebugTest(
-        getDebuggeePath(),
+        getTestConfig(),
         className,
         breakpoint(className, methodName, signature),
         run(),
@@ -115,11 +117,11 @@
   @Test
   public void testBreakInPossiblyRenamedClass() throws Throwable {
     final String className = "Minified";
-    final String innerClassName = config.minifiedNames() ? "a" : "Minified$Inner";
-    final String innerMethodName = config.minifiedNames() ? "a" : "innerTest";
+    final String innerClassName = minifiedNames() ? "a" : "Minified$Inner";
+    final String innerMethodName = minifiedNames() ? "a" : "innerTest";
     final String innerSignature = "()I";
     runDebugTest(
-        getDebuggeePath(),
+        getTestConfig(),
         className,
         breakpoint(innerClassName, innerMethodName, innerSignature),
         run(),
diff --git a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
index 36f1b29..3e380fe 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/DebugInfoTestBase.java
@@ -28,7 +28,7 @@
       Paths.get(ToolHelper.BUILD_DIR, "test", "debuginfo_examples_dex.jar");
 
   @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   static AndroidApp compileWithD8(Class... classes) throws CompilationException, IOException,
       CompilationFailedException {
diff --git a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
index dfb3fd1..4b011a2 100644
--- a/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
+++ b/src/test/java/com/android/tools/r8/jdwp/RunJdwpTests.java
@@ -232,7 +232,7 @@
   private static File d8Out = null;
 
   @ClassRule
-  public static TemporaryFolder temp = new TemporaryFolder();
+  public static TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   @BeforeClass
   public static void compileLibraries() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
index 0fe9e75..1c55379 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexListTests.java
@@ -94,7 +94,8 @@
   private static List<String> MANY_CLASSES;
 
   @ClassRule
-  public static TemporaryFolder generatedApplicationsFolder = new TemporaryFolder();
+  public static TemporaryFolder generatedApplicationsFolder =
+      ToolHelper.getTemporaryFolderForTest();
 
   // Generate the test applications in a @BeforeClass method, as they are used by several tests.
   @BeforeClass
diff --git a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
index 41370d4..3b0a933 100644
--- a/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ApplyMappingTest.java
@@ -48,7 +48,7 @@
       Paths.get(ToolHelper.EXAMPLES_BUILD_DIR, "applymapping044" + FileUtils.JAR_EXTENSION);
 
   @Rule
-  public TemporaryFolder temp = new TemporaryFolder();
+  public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
 
   private Path out;
 
diff --git a/src/test/java/com/android/tools/r8/regress/Regress37740372.java b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
index 5258b31..1f13013 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress37740372.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress37740372.java
@@ -9,7 +9,10 @@
 import static org.junit.Assert.assertTrue;
 
 import com.android.tools.r8.D8Command;
+import com.android.tools.r8.D8Command.Builder;
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.EmbeddedOrigin;
+import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.smali.SmaliTestBase;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -133,14 +136,16 @@
     AndroidApp output =
         ToolHelper.runD8(
             D8Command.builder()
-                .addClassProgramData(Base64.getDecoder().decode(javaLangObjectClassFile))
+                .addClassProgramData(Base64.getDecoder().decode(javaLangObjectClassFile),
+                    EmbeddedOrigin.INSTANCE)
                 .build());
     checkApplicationOnlyHasJavaLangObject(output);
 
     // Build an application with the java.lang.Object stub from a dex file.
     List<byte[]> dex = output.writeToMemory();
     assertEquals(1, dex.size());
-    checkApplicationOnlyHasJavaLangObject(
-        ToolHelper.runD8(D8Command.builder().addDexProgramData(dex).build()));
+    Builder builder = D8Command.builder();
+    dex.forEach(data -> builder.addDexProgramData(data, Origin.unknown()));
+    checkApplicationOnlyHasJavaLangObject(ToolHelper.runD8(builder.build()));
   }
 }
diff --git a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
index 76c24f9..dd07b68 100644
--- a/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/staticvalues/inlibraries/StaticLibraryValuesChangeTest.java
@@ -10,6 +10,7 @@
 import com.android.tools.r8.ToolHelper;
 import com.android.tools.r8.ToolHelper.DexVm.Version;
 import com.android.tools.r8.jasmin.JasminBuilder;
+import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.smali.SmaliBuilder;
 import com.android.tools.r8.utils.AndroidApp;
@@ -75,7 +76,7 @@
 
     // Merge the compiled TestMain with the runtime version of LibraryClass.
     builder = AndroidApp.builder(app);
-    builder.addDexProgramData(runtimeLibrary.compile());
+    builder.addDexProgramData(runtimeLibrary.compile(), EmbeddedOrigin.INSTANCE);
     String result = runOnArt(builder.build(), TestMain.class);
     assertEquals("33", result);
   }
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
index 8e946b6..fa08ece 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliBuildTest.java
@@ -7,6 +7,7 @@
 import static org.junit.Assert.assertEquals;
 
 import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.utils.AndroidApp;
 import com.android.tools.r8.utils.DexInspector;
@@ -60,7 +61,7 @@
     );
 
     AndroidApp originalApp = AndroidApp.builder()
-        .addDexProgramData(builder.compile())
+        .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
         .addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
         .build();
 
diff --git a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
index e16b172..01b2af4 100644
--- a/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
+++ b/src/test/java/com/android/tools/r8/smali/SmaliTestBase.java
@@ -18,6 +18,7 @@
 import com.android.tools.r8.graph.DexApplication;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.origin.EmbeddedOrigin;
 import com.android.tools.r8.origin.Origin;
 import com.android.tools.r8.shaking.FilteredClassPath;
 import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -55,7 +56,7 @@
   protected AndroidApp buildApplicationWithAndroidJar(SmaliBuilder builder) {
     try {
       return AndroidApp.builder()
-          .addDexProgramData(builder.compile())
+          .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
           .addLibraryFiles(FilteredClassPath.unfiltered(ToolHelper.getDefaultAndroidJar()))
           .build();
     } catch (IOException | RecognitionException | ExecutionException | DexOverflowException e) {
@@ -98,7 +99,7 @@
       Path dexOutputDir = temp.newFolder().toPath();
       R8Command command =
           R8Command.builder()
-              .addDexProgramData(builder.compile())
+              .addDexProgramData(builder.compile(), EmbeddedOrigin.INSTANCE)
               .setOutputPath(dexOutputDir)
               .setMode(CompilationMode.DEBUG)
               .addLibraryFiles(Paths.get(ToolHelper.getDefaultAndroidJar()))
diff --git a/tests/api_usage_sample.jar b/tests/api_usage_sample.jar
index 51029b5..3b7d14a 100644
--- a/tests/api_usage_sample.jar
+++ b/tests/api_usage_sample.jar
Binary files differ
diff --git a/tools/archive.py b/tools/archive.py
index 20d057c..8e7436b 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -65,10 +65,15 @@
 def Main():
   if not 'BUILDBOT_BUILDERNAME' in os.environ:
     raise Exception('You are not a bot, don\'t archive builds')
+  # Create maven release first which uses a build that exclude dependencies.
+  create_maven_release.main(["--out", utils.LIBS])
+
+  # Generate and copy the build that exclude dependencies.
+  gradle.RunGradleExcludeDeps([utils.R8])
+  shutil.copyfile(utils.R8_JAR, utils.R8_EXCLUDE_DEPS_JAR)
+
   # Ensure all archived artifacts has been built before archiving.
   gradle.RunGradle([utils.D8, utils.R8, utils.COMPATDX, utils.COMPATPROGUARD])
-  create_maven_release.main(['--jar', utils.R8_JAR, "--out", utils.LIBS])
-
   version = GetVersion()
   is_master = IsMaster(version)
   if is_master:
@@ -86,6 +91,7 @@
 
     for file in [utils.D8_JAR,
                  utils.R8_JAR,
+                 utils.R8_EXCLUDE_DEPS_JAR,
                  utils.COMPATDX_JAR,
                  utils.COMPATPROGUARD_JAR,
                  utils.MAVEN_ZIP]:
diff --git a/tools/create_maven_release.py b/tools/create_maven_release.py
index 85b558a..cc045a2 100755
--- a/tools/create_maven_release.py
+++ b/tools/create_maven_release.py
@@ -3,8 +3,9 @@
 # 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.
 
-import hashlib
 import argparse
+import gradle
+import hashlib
 from os import makedirs
 from os.path import join
 from shutil import copyfile, make_archive, rmtree
@@ -14,13 +15,13 @@
 import tempfile
 import utils
 
-LICENSETEMPLATE = Template(
+DEPENDENCYTEMPLATE = Template(
 """
-    <license>
-      <name>$name</name>
-      <url>$url</url>
-      <distribution>repo</distribution>
-    </license>""")
+    <dependency>
+        <groupId>$group</groupId>
+        <artifactId>$artifact</artifactId>
+        <version>$version</version>
+    </dependency>""")
 
 POMTEMPLATE = Template(
 """<project
@@ -42,8 +43,10 @@
       <name>BSD-3-Clause</name>
       <url>https://opensource.org/licenses/BSD-3-Clause</url>
       <distribution>repo</distribution>
-    </license>$library_licenses
+    </license>
   </licenses>
+  <dependencies>$dependencies
+  </dependencies>
   <developers>
     <developer>
       <name>The Android Open Source Project</name>
@@ -62,19 +65,19 @@
 
 def parse_options(argv):
   result = argparse.ArgumentParser()
-  result.add_argument('--jar', help='jar file to package')
   result.add_argument('--out', help='directory in which to put the output zip file')
   return result.parse_args(argv)
 
-def determine_version(jar):
-  cmd = []
-  cmd.append('java')
-  cmd.extend(['-jar', jar]);
-  cmd.append('--version')
-  output = subprocess.check_output(cmd)
-  version_string = output.split()[1]
-  assert version_string.startswith("v")
-  return version_string[1:]
+def determine_version():
+  version_file = join(
+      utils.SRC_ROOT, 'com', 'android', 'tools', 'r8', 'Version.java')
+  with open(version_file, 'r') as file:
+    for line in file:
+      if 'final String LABEL ' in line:
+        result = line[line.find('"v') + 2:]
+        result = result[:result.find('"')]
+        return result
+  raise Exception('Unable to determine version.')
 
 def generate_library_licenses():
   license_prefix = 'license: '
@@ -103,9 +106,76 @@
     result += LICENSETEMPLATE.substitute(name=name, url=url)
   return result
 
+
+# Generate the dependencies block for the pom file.
+#
+# We ask gradle to list all dependencies. In that output
+# we locate the runtimeClasspath block for 'main' which
+# looks something like:
+#
+# runtimeClasspath - Runtime classpath of source set 'main'.
+# +--- net.sf.jopt-simple:jopt-simple:4.6
+# +--- com.googlecode.json-simple:json-simple:1.1
+# +--- com.google.guava:guava:23.0
+# +--- it.unimi.dsi:fastutil:7.2.0
+# +--- org.ow2.asm:asm:6.0
+# +--- org.ow2.asm:asm-commons:6.0
+# |    \--- org.ow2.asm:asm-tree:6.0
+# |         \--- org.ow2.asm:asm:6.0
+# +--- org.ow2.asm:asm-tree:6.0 (*)
+# +--- org.ow2.asm:asm-analysis:6.0
+# |    \--- org.ow2.asm:asm-tree:6.0 (*)
+# \--- org.ow2.asm:asm-util:6.0
+#      \--- org.ow2.asm:asm-tree:6.0 (*)
+#
+# We filter out the repeats that are marked by '(*)'.
+#
+# For each remaining line, we remove the junk at the start
+# in chunks. As an example:
+#
+# '  |    \--- org.ow2.asm:asm-tree:6.0  '  --strip-->
+# '|    \--- org.ow2.asm:asm-tree:6.0'  -->
+# '\--- org.ow2.asm:asm-tree:6.0'  -->
+# 'org.ow2.asm:asm-tree:6.0'
+#
+# The end result is the dependency we are looking for:
+#
+# groupId: org.ow2.asm
+# artifact: asm-tree
+# version: 6.0
+def generate_dependencies():
+  dependencies = gradle.RunGradleGetOutput(['dependencies'])
+  dependency_lines = []
+  collect = False
+  for line in dependencies.splitlines():
+    if 'runtimeClasspath' in line and "'main'" in line:
+      collect = True
+      continue
+    if collect:
+      if not len(line) == 0:
+        if not '(*)' in line:
+          trimmed = line.strip()
+          while trimmed.find(' ') != -1:
+            trimmed = trimmed[trimmed.find(' ') + 1:].strip()
+          if not trimmed in dependency_lines:
+            dependency_lines.append(trimmed)
+      else:
+        break
+  result = ''
+  for dep in dependency_lines:
+    components = dep.split(':')
+    assert len(components) == 3
+    group = components[0]
+    artifact = components[1]
+    version = components[2]
+    result += DEPENDENCYTEMPLATE.substitute(
+        group=group, artifact=artifact, version=version)
+  return result
+
 def write_pom_file(version, pom_file):
-  library_licenses = generate_library_licenses()
-  version_pom = POMTEMPLATE.substitute(version=version, library_licenses=library_licenses)
+  dependencies = generate_dependencies()
+  version_pom = POMTEMPLATE.substitute(
+      version=version, dependencies=dependencies)
   with open(pom_file, 'w') as file:
     file.write(version_pom)
 
@@ -131,13 +201,14 @@
 
 def main(argv):
   options = parse_options(argv)
-  jar = options.jar
   outdir = options.out
-  if jar == None or outdir == None:
-    print 'Need to supply --jar and --out.'
+  if outdir == None:
+    print 'Need to supply output dir with --out.'
     exit(1)
+  # Build the R8 no deps artifact.
+  gradle.RunGradleExcludeDeps([utils.R8])
   # Create directory structure for this version.
-  version = determine_version(jar)
+  version = determine_version()
   with utils.TempDir() as tmp_dir:
     version_dir = join(
         tmp_dir, 'com', 'google', 'android', 'tools', 'r8', version, 'r8')
@@ -147,7 +218,7 @@
     write_pom_file(version, pom_file)
     # Copy the jar to the output.
     target_jar = join(version_dir, 'r8-' + version + '.jar')
-    copyfile(jar, target_jar)
+    copyfile(utils.R8_JAR, target_jar)
     # Create check sums.
     write_md5_for(target_jar)
     write_md5_for(pom_file)
diff --git a/tools/gradle.py b/tools/gradle.py
index 275c270..df80cc9 100755
--- a/tools/gradle.py
+++ b/tools/gradle.py
@@ -54,9 +54,12 @@
   else:
     print 'gradle.py: Shadow library present'
 
-def RunGradle(args, throw_on_failure=True):
+def EnsureDeps():
   EnsureGradle()
   EnsureShadow()
+
+def RunGradle(args, throw_on_failure=True):
+  EnsureDeps()
   cmd = [GRADLE]
   cmd.extend(args)
   utils.PrintCmd(cmd)
@@ -66,6 +69,19 @@
       raise
     return return_value
 
+def RunGradleExcludeDeps(args, throw_on_failure=True):
+  EnsureDeps()
+  args.append('-Pexclude_deps')
+  RunGradle(args, throw_on_failure)
+
+def RunGradleGetOutput(args):
+  EnsureDeps()
+  cmd = [GRADLE]
+  cmd.extend(args)
+  utils.PrintCmd(cmd)
+  with utils.ChangedWorkingDirectory(utils.REPO_ROOT):
+    return subprocess.check_output(cmd)
+
 def Main():
   RunGradle(sys.argv[1:])
 
diff --git a/tools/utils.py b/tools/utils.py
index 40a62b8..cf01d3b 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -20,6 +20,7 @@
 DEX_SEGMENTS_RESULT_PATTERN = re.compile('- ([^:]+): ([0-9]+)')
 LIBS = os.path.join(REPO_ROOT, 'build', 'libs')
 MAVEN_ZIP = os.path.join(LIBS, 'r8.zip')
+SRC_ROOT = os.path.join(REPO_ROOT, 'src', 'main', 'java')
 
 D8 = 'd8'
 R8 = 'r8'
@@ -28,6 +29,7 @@
 
 D8_JAR = os.path.join(LIBS, 'd8.jar')
 R8_JAR = os.path.join(LIBS, 'r8.jar')
+R8_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-exclude-deps.jar')
 COMPATDX_JAR = os.path.join(LIBS, 'compatdx.jar')
 COMPATPROGUARD_JAR = os.path.join(LIBS, 'compatproguard.jar')