Rollforward change of Java coverage logic.

RELNOTES: None.

*** Original change description ***

Automated rollback of commit 8d6fc64b18c7e35b93f5c43dae1dbd2f8cae2147.

PiperOrigin-RevId: 170038845
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/LazyWritePathsFileAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/LazyWritePathsFileAction.java
new file mode 100644
index 0000000..a43f09e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/LazyWritePathsFileAction.java
@@ -0,0 +1,91 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis.actions;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionOwner;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.util.Fingerprint;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Lazily writes the exec path of the given files separated by newline into a specified output file.
+ */
+public final class LazyWritePathsFileAction extends AbstractFileWriteAction {
+  private static final String GUID = "6be94d90-96f3-4bec-8104-1fb08abc2546";
+
+  private final NestedSet<Artifact> files;
+  private final boolean includeOnlyIfSource;
+
+  public LazyWritePathsFileAction(
+      ActionOwner owner, Artifact output, NestedSet<Artifact> files, boolean includeOnlyIfSource) {
+    super(owner, files, output, false);
+    this.files = NestedSetBuilder.fromNestedSet(files).build();
+    this.includeOnlyIfSource = includeOnlyIfSource;
+  }
+
+  public LazyWritePathsFileAction(
+      ActionOwner owner, Artifact output,
+      ImmutableSet<Artifact> files,
+      boolean includeOnlyIfSource) {
+    super(owner, Artifact.NO_ARTIFACTS, output, false);
+    this.files = NestedSetBuilder.<Artifact>stableOrder().addAll(files).build();
+    this.includeOnlyIfSource = includeOnlyIfSource;
+  }
+
+  @Override
+  public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) {
+    return new DeterministicWriter() {
+      @Override
+      public void writeOutputFile(OutputStream out) throws IOException {
+        out.write(getContents().toString().getBytes(UTF_8));
+      }
+    };
+  }
+
+  /**
+   * Computes the Action key for this action by computing the fingerprint for the file contents.
+   */
+  @Override
+  protected String computeKey() {
+    Fingerprint f = new Fingerprint();
+    f.addString(GUID);
+    f.addBoolean(includeOnlyIfSource);
+    f.addString(getContents());
+    return f.hexDigestAndReset();
+  }
+
+  private String getContents() {
+    StringBuilder stringBuilder = new StringBuilder();
+    for (Artifact file : files) {
+      if (includeOnlyIfSource) {
+        if (file.isSourceArtifact()) {
+          stringBuilder.append(file.getRootRelativePathString());
+          stringBuilder.append("\n");
+        }
+      } else {
+        stringBuilder.append(file.getRootRelativePathString());
+        stringBuilder.append("\n");
+      }
+    }
+    return stringBuilder.toString();
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
index f7d2fc1..8b42e96 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/test/TestRunnerAction.java
@@ -461,8 +461,8 @@
       env.put("COVERAGE_MANIFEST", getCoverageManifest().getExecPathString());
       env.put("COVERAGE_DIR", getCoverageDirectory().getPathString());
       env.put("COVERAGE_OUTPUT_FILE", getCoverageData().getExecPathString());
-      // TODO(elenairina): Remove this after the next blaze release (after 2017.07.30).
-      env.put("NEW_JAVA_COVERAGE_IMPL", "True");
+      // TODO(elenairina): Remove this after it reaches a blaze release.
+      env.put("NEW_JAVA_COVERAGE_IMPL", "released");
     }
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java
index de646ac..424e16c 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/BazelJavaSemantics.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.analysis.Runfiles;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
+import com.google.devtools.build.lib.analysis.actions.LazyWritePathsFileAction;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.ComputedSubstitution;
 import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
@@ -261,6 +262,8 @@
       List<String> jvmFlags,
       Artifact executable,
       String javaStartClass,
+      String coverageStartClass,
+      NestedSetBuilder<Artifact> filesBuilder,
       String javaExecutable) {
     Preconditions.checkState(ruleContext.getConfiguration().hasFragment(Jvm.class));
 
@@ -314,22 +317,35 @@
     }
     arguments.add(new ComputedClasspathSubstitution(classpath, workspacePrefix, isRunfilesEnabled));
 
-    JavaCompilationArtifacts javaArtifacts = javaCommon.getJavaCompilationArtifacts();
-    String path =
-        javaArtifacts.getInstrumentedJar() != null
-            ? "${JAVA_RUNFILES}/"
-                + workspacePrefix
-                + javaArtifacts.getInstrumentedJar().getRootRelativePath().getPathString()
-            : "";
-    arguments.add(
-        Substitution.of(
-            "%set_jacoco_metadata%",
-            ruleContext.getConfiguration().isCodeCoverageEnabled()
-                ? "export JACOCO_METADATA_JAR=" + path
-                : ""));
+    if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
+      Artifact runtimeClassPathArtifact = ruleContext.getUniqueDirectoryArtifact(
+          "coverage_runtime_classpath",
+          "runtime-classpath.txt",
+          ruleContext.getBinOrGenfilesDirectory());
+      ruleContext.registerAction(new LazyWritePathsFileAction(
+          ruleContext.getActionOwner(),
+          runtimeClassPathArtifact,
+          javaCommon.getRuntimeClasspath(),
+          false));
+      filesBuilder.add(runtimeClassPathArtifact);
+      arguments.add(Substitution.of(
+          JACOCO_METADATA_PLACEHOLDER,
+          "export JACOCO_METADATA_JAR=${JAVA_RUNFILES}/" + workspacePrefix + "/"
+              + runtimeClassPathArtifact.getRootRelativePathString()
+      ));
+      arguments.add(Substitution.of(
+          JACOCO_MAIN_CLASS_PLACEHOLDER, "export JACOCO_MAIN_CLASS=" + coverageStartClass));
+      arguments.add(Substitution.of(
+          JACOCO_JAVA_RUNFILES_ROOT_PLACEHOLDER,
+          "export JACOCO_JAVA_RUNFILES_ROOT=${JAVA_RUNFILES}/" + workspacePrefix)
+      );
+    } else {
+      arguments.add(Substitution.of(JavaSemantics.JACOCO_METADATA_PLACEHOLDER, ""));
+      arguments.add(Substitution.of(JavaSemantics.JACOCO_MAIN_CLASS_PLACEHOLDER, ""));
+      arguments.add(Substitution.of(JavaSemantics.JACOCO_JAVA_RUNFILES_ROOT_PLACEHOLDER, ""));
+    }
 
-    arguments.add(Substitution.of("%java_start_class%",
-        ShellEscaper.escapeString(javaStartClass)));
+    arguments.add(Substitution.of("%java_start_class%", ShellEscaper.escapeString(javaStartClass)));
 
     ImmutableList<String> jvmFlagsList = ImmutableList.copyOf(jvmFlags);
     arguments.add(Substitution.ofSpaceSeparatedList("%jvm_flags%", jvmFlagsList));
@@ -660,49 +676,11 @@
     return jvmFlags.build();
   }
 
-  /**
-   * Returns whether coverage has instrumented artifacts.
-   */
-  public static boolean hasInstrumentationMetadata(JavaTargetAttributes.Builder attributes) {
-    return !attributes.getInstrumentationMetadata().isEmpty();
-  }
-
-  // TODO(yueg): refactor this (only mainClass different for now)
   @Override
-  public String addCoverageSupport(
-      JavaCompilationHelper helper,
-      JavaTargetAttributes.Builder attributes,
-      Artifact executable,
-      Artifact instrumentationMetadata,
-      JavaCompilationArtifacts.Builder javaArtifactsBuilder,
-      String mainClass)
+  public String addCoverageSupport(JavaCompilationHelper helper, Artifact executable)
       throws InterruptedException {
     // This method can be called only for *_binary/*_test targets.
     Preconditions.checkNotNull(executable);
-    // Add our own metadata artifact (if any).
-    if (instrumentationMetadata != null) {
-      attributes.addInstrumentationMetadataEntries(ImmutableList.of(instrumentationMetadata));
-    }
-
-    if (!hasInstrumentationMetadata(attributes)) {
-      return mainClass;
-    }
-
-    Artifact instrumentedJar =
-        helper
-            .getRuleContext()
-            .getBinArtifact(helper.getRuleContext().getLabel().getName() + "_instrumented.jar");
-
-    // Create an instrumented Jar. This will be referenced on the runtime classpath prior
-    // to all other Jars.
-    JavaCommon.createInstrumentedJarAction(
-        helper.getRuleContext(),
-        this,
-        attributes.getInstrumentationMetadata(),
-        instrumentedJar,
-        mainClass);
-    javaArtifactsBuilder.setInstrumentedJar(instrumentedJar);
-
     // Add the coverage runner to the list of dependencies when compiling in coverage mode.
     TransitiveInfoCollection runnerTarget =
         helper.getRuleContext().getPrerequisite("$jacocorunner", Mode.TARGET);
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
index dbe84b4..b549b34 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/java/java_stub_template.txt
@@ -254,6 +254,8 @@
 # We need to make the metadata jar with uninstrumented classes available for generating
 # the lcov-compatible coverage report, and we don't want it on the classpath.
 %set_jacoco_metadata%
+%set_jacoco_main_class%
+%set_jacoco_java_runfiles_root%
 
 if [[ -n "$JVM_DEBUG_PORT" ]]; then
   JVM_DEBUG_SUSPEND=${DEFAULT_JVM_DEBUG_SUSPEND:-"y"}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index 1385b16..d4cdd63 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -670,7 +670,6 @@
             ApkProvider.create(
                 zipAlignedApk,
                 unsignedApk,
-                androidCommon.getInstrumentedJar(),
                 applicationManifest.getManifest(),
                 debugKeystore))
         .addProvider(AndroidPreDexJarProvider.class, AndroidPreDexJarProvider.create(jarToDex))
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
index 0796ae2..b9b1986 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidCommon.java
@@ -452,12 +452,11 @@
       } else {
         Artifact outputDepsProto =
             javacHelper.createOutputDepsProtoArtifact(resourceClassJar, javaArtifactsBuilder);
-        javacHelper.createCompileActionWithInstrumentation(
+        javacHelper.createCompileAction(
             resourceClassJar,
             null /* manifestProtoOutput */,
             null /* genSourceJar */,
-            outputDepsProto,
-            javaArtifactsBuilder);
+            outputDepsProto);
       }
     } else {
       // Otherwise, it should have been the AndroidRuleClasses.ANDROID_RESOURCES_CLASS_JAR.
@@ -705,8 +704,7 @@
     helper.createSourceJarAction(srcJar, genSourceJar);
 
     outputDepsProto = helper.createOutputDepsProtoArtifact(classJar, javaArtifactsBuilder);
-    helper.createCompileActionWithInstrumentation(classJar, manifestProtoOutput, genSourceJar,
-        outputDepsProto, javaArtifactsBuilder);
+    helper.createCompileAction(classJar, manifestProtoOutput, genSourceJar, outputDepsProto);
 
     if (isBinary) {
       generatedExtensionRegistryProvider =
@@ -887,6 +885,10 @@
     return javaCommon.getJavacOpts();
   }
 
+  public Artifact getClassJar() {
+    return classJar;
+  }
+
   public Artifact getGenClassJar() {
     return genClassJar;
   }
@@ -912,10 +914,6 @@
     return jarsProducedForRuntime;
   }
 
-  public Artifact getInstrumentedJar() {
-    return javaCommon.getJavaCompilationArtifacts().getInstrumentedJar();
-  }
-
   public NestedSet<Artifact> getTransitiveNeverLinkLibraries() {
     return transitiveNeverlinkLibraries;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
index 6ac746f..423b13a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
@@ -126,8 +126,6 @@
     JavaCompilationHelper helper =
         getJavaCompilationHelperWithDependencies(ruleContext, javaSemantics, javaCommon,
             attributesBuilder);
-    Artifact instrumentationMetadata =
-        helper.createInstrumentationMetadata(classJar, javaArtifactsBuilder);
     Artifact executable; // the artifact for the rule itself
     if (OS.getCurrent() == OS.WINDOWS
         && ruleContext.getConfiguration().enableWindowsExeLauncher()) {
@@ -154,7 +152,7 @@
             javaSemantics,
             helper,
             executable,
-            instrumentationMetadata,
+            null,
             javaArtifactsBuilder,
             attributesBuilder);
 
@@ -178,11 +176,7 @@
         helper.createOutputDepsProtoArtifact(classJar, javaArtifactsBuilder);
     javaRuleOutputJarsProviderBuilder.setJdeps(outputDepsProtoArtifact);
     helper.createCompileAction(
-        classJar,
-        manifestProtoOutput,
-        genSourceJar,
-        outputDepsProtoArtifact,
-        instrumentationMetadata);
+        classJar, manifestProtoOutput, genSourceJar, outputDepsProtoArtifact);
     helper.createSourceJarAction(srcJar, genSourceJar);
 
     setUpJavaCommon(javaCommon, helper, javaArtifactsBuilder.build());
@@ -202,6 +196,8 @@
         getJvmFlags(ruleContext, testClass),
         executable,
         mainClass,
+        "com.google.testing.junit.runner.GoogleTestRunner",
+        filesToBuildBuilder,
         javaExecutable);
 
     Artifact deployJar =
@@ -435,13 +431,6 @@
     builder.addTargets(depsForRunfiles, RunfilesProvider.DEFAULT_RUNFILES);
     builder.addTransitiveArtifacts(transitiveAarArtifacts);
 
-    if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
-      Artifact instrumentedJar = javaCommon.getJavaCompilationArtifacts().getInstrumentedJar();
-      if (instrumentedJar != null) {
-        builder.addArtifact(instrumentedJar);
-      }
-    }
-
     // We assume that the runtime jars will not have conflicting artifacts
     // with the same root relative path
     builder.addTransitiveArtifactsWrappedInStableOrder(javaCommon.getRuntimeClasspath());
@@ -478,3 +467,4 @@
     return testClass;
   }
 }
+
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java
index d265fd3..70676b7 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java
@@ -17,7 +17,6 @@
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.TransitiveInfoProvider;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-import javax.annotation.Nullable;
 
 /** A provider for targets that produce an apk file. */
 @AutoValue
@@ -27,10 +26,9 @@
   public static ApkProvider create(
       Artifact apk,
       Artifact unsignedApk,
-      @Nullable Artifact coverageMetdata,
       Artifact mergedManifest,
       Artifact keystore) {
-    return new AutoValue_ApkProvider(apk, unsignedApk, coverageMetdata, mergedManifest, keystore);
+    return new AutoValue_ApkProvider(apk, unsignedApk, mergedManifest, keystore);
   }
 
   /** Returns the APK file built in the transitive closure. */
@@ -39,10 +37,6 @@
   /** Returns the unsigned APK file built in the transitive closure. */
   public abstract Artifact getUnsignedApk();
 
-  /** Returns the coverage metadata artifacts generated in the transitive closure. */
-  @Nullable
-  public abstract Artifact getCoverageMetadata();
-
   /** Returns the merged manifest. */
   public abstract Artifact getMergedManifest();
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java
index 10f42ee..e94c4e1 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java
@@ -79,7 +79,6 @@
     JavaTargetAttributes.Builder attributesBuilder = common.initCommon();
     attributesBuilder.addClassPathResources(
         ruleContext.getPrerequisiteArtifacts("classpath_resources", Mode.TARGET).list());
-
     // Add Java8 timezone resource data
     addTimezoneResourceForJavaBinaries(ruleContext, attributesBuilder);
 
@@ -148,8 +147,6 @@
     }
 
     JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
-    Artifact instrumentationMetadata =
-        helper.createInstrumentationMetadata(classJar, javaArtifactsBuilder);
 
     NestedSetBuilder<Artifact> filesBuilder = NestedSetBuilder.stableOrder();
     Artifact executableForRunfiles = null;
@@ -166,14 +163,7 @@
       filesBuilder.add(classJar).add(executableForRunfiles);
 
       if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
-        mainClass =
-            semantics.addCoverageSupport(
-                helper,
-                attributesBuilder,
-                executableForRunfiles,
-                instrumentationMetadata,
-                javaArtifactsBuilder,
-                mainClass);
+        mainClass = semantics.addCoverageSupport(helper, executableForRunfiles);
       }
     } else {
       filesBuilder.add(classJar);
@@ -224,8 +214,7 @@
       helper.createGenJarAction(classJar, manifestProtoOutput, genClassJar);
     }
 
-    helper.createCompileAction(
-        classJar, manifestProtoOutput, genSourceJar, outputDepsProto, instrumentationMetadata);
+    helper.createCompileAction(classJar, manifestProtoOutput, genSourceJar, outputDepsProto);
     helper.createSourceJarAction(srcJar, genSourceJar);
 
     common.setClassPathFragment(
@@ -260,6 +249,8 @@
               jvmFlags,
               executableForRunfiles,
               mainClass,
+              originalMainClass,
+              filesBuilder,
               javaExecutable);
       if (!executableToRun.equals(executableForRunfiles)) {
         filesBuilder.add(executableToRun);
@@ -511,13 +502,6 @@
     builder.addTargets(runtimeDeps, RunfilesProvider.DEFAULT_RUNFILES);
     semantics.addDependenciesForRunfiles(ruleContext, builder);
 
-    if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
-      Artifact instrumentedJar = javaArtifacts.getInstrumentedJar();
-      if (instrumentedJar != null) {
-        builder.addArtifact(instrumentedJar);
-      }
-    }
-
     builder.addArtifacts((Iterable<Artifact>) common.getRuntimeClasspath());
 
     // Add the JDK files if it comes from the source repository (see java_stub_template.txt).
@@ -600,3 +584,4 @@
     }
   }
 }
+
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
index d75277c..73e4f91 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCommon.java
@@ -173,28 +173,6 @@
     }
   }
 
-  /**
-   * Creates an action to aggregate all metadata artifacts into a single
-   * &lt;target_name&gt;_instrumented.jar file.
-   */
-  public static void createInstrumentedJarAction(
-      RuleContext ruleContext,
-      JavaSemantics semantics,
-      List<Artifact> metadataArtifacts,
-      Artifact instrumentedJar,
-      String mainClass)
-      throws InterruptedException {
-    // In Jacoco's setup, metadata artifacts are real jars.
-    new DeployArchiveBuilder(semantics, ruleContext)
-        .setOutputJar(instrumentedJar)
-        // We need to save the original mainClass because we're going to run inside CoverageRunner
-        .setJavaStartClass(mainClass)
-        .setAttributes(new JavaTargetAttributes.Builder(semantics).build())
-        .addRuntimeJars(ImmutableList.copyOf(metadataArtifacts))
-        .setCompression(DeployArchiveBuilder.Compression.UNCOMPRESSED)
-        .build();
-  }
-
   public static ImmutableList<String> getConstraints(RuleContext ruleContext) {
     return ruleContext.getRule().isAttrDefined("constraints", Type.STRING_LIST)
         ? ImmutableList.copyOf(ruleContext.attributes().get("constraints", Type.STRING_LIST))
@@ -756,7 +734,6 @@
         .addTransitiveTargets(runtimeDepInfo, true, ClasspathType.RUNTIME_ONLY)
         .build();
     attributes.addRuntimeClassPathEntries(args.getRuntimeJars());
-    attributes.addInstrumentationMetadataEntries(args.getInstrumentationMetadata());
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArgs.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArgs.java
index f9198eee..a83bf36 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArgs.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArgs.java
@@ -22,7 +22,6 @@
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.util.FileType;
-import java.util.Collection;
 
 /** A container of Java compilation artifacts. */
 @AutoValue
@@ -43,16 +42,13 @@
       JavaCompilationArgs.create(
           NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER),
           NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER),
-          NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER),
           NestedSetBuilder.<Artifact>create(Order.NAIVE_LINK_ORDER));
 
   private static JavaCompilationArgs create(
       NestedSet<Artifact> runtimeJars,
       NestedSet<Artifact> compileTimeJars,
-      NestedSet<Artifact> fullCompileTimeJars,
-      NestedSet<Artifact> instrumentationMetadata) {
-    return new AutoValue_JavaCompilationArgs(
-        runtimeJars, compileTimeJars, fullCompileTimeJars, instrumentationMetadata);
+      NestedSet<Artifact> fullCompileTimeJars) {
+    return new AutoValue_JavaCompilationArgs(runtimeJars, compileTimeJars, fullCompileTimeJars);
   }
 
   /** Returns transitive runtime jars. */
@@ -67,9 +63,6 @@
    */
   public abstract NestedSet<Artifact> getFullCompileTimeJars();
 
-  /** Returns transitive instrumentation metadata jars. */
-  public abstract NestedSet<Artifact> getInstrumentationMetadata();
-
   /**
    * Returns a new builder instance.
    */
@@ -87,8 +80,6 @@
         NestedSetBuilder.naiveLinkOrder();
     private final NestedSetBuilder<Artifact> fullCompileTimeJarsBuilder =
         NestedSetBuilder.naiveLinkOrder();
-    private final NestedSetBuilder<Artifact> instrumentationMetadataBuilder =
-        NestedSetBuilder.naiveLinkOrder();
 
     /**
      * Use {@code TransitiveJavaCompilationArgs#builder()} to instantiate the builder.
@@ -107,7 +98,6 @@
       }
       addCompileTimeJars(other.getCompileTimeJars());
       addFullCompileTimeJars(other.getFullCompileTimeJars());
-      addInstrumentationMetadata(other.getInstrumentationMetadata());
       return this;
     }
 
@@ -165,16 +155,6 @@
       return this;
     }
 
-    public Builder addInstrumentationMetadata(Artifact instrumentationMetadata) {
-      this.instrumentationMetadataBuilder.add(instrumentationMetadata);
-      return this;
-    }
-
-    public Builder addInstrumentationMetadata(Collection<Artifact> instrumentationMetadata) {
-      this.instrumentationMetadataBuilder.addAll(instrumentationMetadata);
-      return this;
-    }
-
     public Builder addTransitiveCompilationArgs(
         JavaCompilationArgsProvider dep, boolean recursive, ClasspathType type) {
       JavaCompilationArgs args = recursive
@@ -268,8 +248,6 @@
       if (!ClasspathType.COMPILE_ONLY.equals(type)) {
         runtimeJarsBuilder.addTransitive(args.getRuntimeJars());
       }
-      instrumentationMetadataBuilder.addTransitive(
-          args.getInstrumentationMetadata());
       return this;
     }
 
@@ -280,8 +258,7 @@
       return JavaCompilationArgs.create(
           runtimeJarsBuilder.build(),
           compileTimeJarsBuilder.build(),
-          fullCompileTimeJarsBuilder.build(),
-          instrumentationMetadataBuilder.build());
+          fullCompileTimeJarsBuilder.build());
     }
   }
 
@@ -301,3 +278,4 @@
 
   JavaCompilationArgs() {}
 }
+
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArtifacts.java
index a6d346a..0d31303 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArtifacts.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationArtifacts.java
@@ -20,10 +20,8 @@
 import com.google.common.collect.Iterables;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
-
 import java.util.LinkedHashSet;
 import java.util.Set;
-
 import javax.annotation.Nullable;
 
 /**
@@ -49,9 +47,7 @@
   public abstract ImmutableList<Artifact> getRuntimeJars();
   public abstract ImmutableList<Artifact> getCompileTimeJars();
   abstract ImmutableList<Artifact> getFullCompileTimeJars();
-  public abstract ImmutableList<Artifact> getInstrumentationMetadata();
   @Nullable public abstract Artifact getCompileTimeDependencyArtifact();
-  @Nullable public abstract Artifact getInstrumentedJar();
 
   /** Returns a builder for a {@link JavaCompilationArtifacts}. */
   public static Builder builder() {
@@ -65,9 +61,7 @@
     private final Set<Artifact> runtimeJars = new LinkedHashSet<>();
     private final Set<Artifact> compileTimeJars = new LinkedHashSet<>();
     private final Set<Artifact> fullCompileTimeJars = new LinkedHashSet<>();
-    private final Set<Artifact> instrumentationMetadata = new LinkedHashSet<>();
     private Artifact compileTimeDependencies;
-    private Artifact instrumentedJar;
 
     public JavaCompilationArtifacts build() {
       Preconditions.checkState(fullCompileTimeJars.size() == compileTimeJars.size());
@@ -75,9 +69,7 @@
           ImmutableList.copyOf(runtimeJars),
           ImmutableList.copyOf(compileTimeJars),
           ImmutableList.copyOf(fullCompileTimeJars),
-          ImmutableList.copyOf(instrumentationMetadata),
-          compileTimeDependencies,
-          instrumentedJar);
+          compileTimeDependencies);
     }
 
     public Builder addRuntimeJar(Artifact jar) {
@@ -112,19 +104,9 @@
       return this;
     }
 
-    public Builder addInstrumentationMetadata(Artifact instrumentationMetadata) {
-      this.instrumentationMetadata.add(instrumentationMetadata);
-      return this;
-    }
-
     public Builder setCompileTimeDependencies(@Nullable Artifact compileTimeDependencies) {
       this.compileTimeDependencies = compileTimeDependencies;
       return this;
     }
-
-    public Builder setInstrumentedJar(@Nullable Artifact instrumentedJar) {
-      this.instrumentedJar = instrumentedJar;
-      return this;
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
index b1629f0..49d3e97 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompilationHelper.java
@@ -165,18 +165,16 @@
    *
    * @param outputJar the class jar Artifact to create with the Action
    * @param manifestProtoOutput the output artifact for the manifest proto emitted from JavaBuilder
-   * @param gensrcOutputJar the generated sources jar Artifact to create with the Action
-   *        (null if no sources will be generated).
-   * @param outputDepsProto the compiler-generated jdeps file to create with the Action
-   *        (null if not requested)
-   * @param outputMetadata metadata file (null if no instrumentation is needed).
+   * @param gensrcOutputJar the generated sources jar Artifact to create with the Action (null if no
+   *     sources will be generated).
+   * @param outputDepsProto the compiler-generated jdeps file to create with the Action (null if not
+   *     requested)
    */
   public void createCompileAction(
       Artifact outputJar,
       Artifact manifestProtoOutput,
       @Nullable Artifact gensrcOutputJar,
-      @Nullable Artifact outputDepsProto,
-      @Nullable Artifact outputMetadata) {
+      @Nullable Artifact outputDepsProto) {
 
     JavaTargetAttributes attributes = getAttributes();
 
@@ -209,7 +207,7 @@
     builder.setGensrcOutputJar(gensrcOutputJar);
     builder.setOutputDepsProto(outputDepsProto);
     builder.setAdditionalOutputs(attributes.getAdditionalOutputs());
-    builder.setMetadata(outputMetadata);
+    builder.setFileWithPathsForCoverage(maybeCreateFileWithPathsForCoverage(outputJar));
     builder.setInstrumentationJars(jacocoInstrumentation);
     builder.setSourceFiles(attributes.getSourceFiles());
     builder.addSourceJars(attributes.getSourceJars());
@@ -253,65 +251,15 @@
     }
   }
 
-  /**
-   * Returns the instrumentation metadata files to be generated for a given output jar.
-   *
-   * <p>Only called if the output jar actually needs to be instrumented.
-   */
-  @Nullable
-  private static Artifact createInstrumentationMetadataArtifact(
-      RuleContext ruleContext, Artifact outputJar) {
-    PathFragment packageRelativePath = outputJar.getRootRelativePath().relativeTo(
-        ruleContext.getPackageDirectory());
-    return ruleContext.getPackageRelativeArtifact(
-        FileSystemUtils.replaceExtension(packageRelativePath, ".em"), outputJar.getRoot());
-  }
-
-  /**
-   * Creates the Action that compiles Java source files and optionally instruments them for
-   * coverage.
-   *
-   * @param outputJar the class jar Artifact to create with the Action
-   * @param manifestProtoOutput the output artifact for the manifest proto emitted from JavaBuilder
-   * @param gensrcJar the generated sources jar Artifact to create with the Action
-   * @param outputDepsProto the compiler-generated jdeps file to create with the Action
-   * @param javaArtifactsBuilder the build to store the instrumentation metadata in
-   */
-  public void createCompileActionWithInstrumentation(
-      Artifact outputJar,
-      Artifact manifestProtoOutput,
-      @Nullable Artifact gensrcJar,
-      @Nullable Artifact outputDepsProto,
-      JavaCompilationArtifacts.Builder javaArtifactsBuilder) {
-    createCompileAction(
-        outputJar,
-        manifestProtoOutput,
-        gensrcJar,
-        outputDepsProto,
-        createInstrumentationMetadata(outputJar, javaArtifactsBuilder));
-  }
-
-  /**
-   * Creates the instrumentation metadata artifact if needed.
-   *
-   * @return the instrumentation metadata artifact or null if instrumentation is
-   *         disabled
-   */
-  @Nullable
-  public Artifact createInstrumentationMetadata(Artifact outputJar,
-      JavaCompilationArtifacts.Builder javaArtifactsBuilder) {
-    // If we need to instrument the jar, add additional output (the coverage metadata file) to the
-    // JavaCompileAction.
-    Artifact instrumentationMetadata = null;
-    if (shouldInstrumentJar()) {
-      instrumentationMetadata = createInstrumentationMetadataArtifact(
-          getRuleContext(), outputJar);
-
-      if (instrumentationMetadata != null) {
-        javaArtifactsBuilder.addInstrumentationMetadata(instrumentationMetadata);
-      }
+  private Artifact maybeCreateFileWithPathsForCoverage(Artifact outputJar) {
+    if (!shouldInstrumentJar()) {
+      return null;
     }
-    return instrumentationMetadata;
+    PathFragment packageRelativePath =
+        outputJar.getRootRelativePath().relativeTo(ruleContext.getPackageDirectory());
+    PathFragment path =
+        FileSystemUtils.replaceExtension(packageRelativePath, "-paths-for-coverage.txt");
+    return ruleContext.getPackageRelativeArtifact(path, outputJar.getRoot());
   }
 
   private boolean shouldInstrumentJar() {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
index 7f04049..fc59b2f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaCompileAction.java
@@ -45,6 +45,7 @@
 import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
 import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomMultiArgv;
 import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg;
+import com.google.devtools.build.lib.analysis.actions.LazyWritePathsFileAction;
 import com.google.devtools.build.lib.analysis.actions.ParameterFileWriteAction;
 import com.google.devtools.build.lib.analysis.actions.SpawnAction;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
@@ -500,7 +501,7 @@
     private Artifact outputDepsProto;
     private Collection<Artifact> additionalOutputs;
     private Artifact paramFile;
-    private Artifact metadata;
+    private Artifact fileWithPathsForCoverage;
     private ImmutableSet<Artifact> sourceFiles = ImmutableSet.of();
     private final Collection<Artifact> sourceJars = new ArrayList<>();
     private BuildConfiguration.StrictDepsMode strictJavaDeps =
@@ -592,7 +593,6 @@
           .addAll(
               new ArrayList<>(Collections2.filter(Arrays.asList(
                   outputJar,
-                  metadata,
                   gensrcOutputJar,
                   manifestProtoOutput,
                   outputDepsProto), Predicates.notNull())));
@@ -615,6 +615,11 @@
               semantics.getJavaBuilderMainClass(),
               pathSeparator);
 
+      if (fileWithPathsForCoverage != null) {
+        analysisEnvironment.registerAction(new LazyWritePathsFileAction(
+                owner, fileWithPathsForCoverage, sourceFiles, true));
+      }
+
       // The actual params-file-based command line executed for a compile action.
       CommandLine javaBuilderCommandLine =
           CustomCommandLine.builder()
@@ -630,7 +635,7 @@
               .addAll(instrumentationJars)
               .build();
 
-      NestedSet<Artifact> inputs =
+      NestedSetBuilder<Artifact> inputsBuilder =
           NestedSetBuilder.<Artifact>stableOrder()
               .addTransitive(classpathEntries)
               .addTransitive(compileTimeDependencyArtifacts)
@@ -642,8 +647,12 @@
               .addAll(sourcePathEntries)
               .addAll(extdirInputs)
               .add(paramFile)
-              .addTransitive(tools)
-              .build();
+              .addTransitive(tools);
+      if (fileWithPathsForCoverage != null) {
+        inputsBuilder.add(fileWithPathsForCoverage);
+      }
+
+      NestedSet<Artifact> inputs = inputsBuilder.build();
 
       return new JavaCompileAction(
           owner,
@@ -757,9 +766,9 @@
           }
         }
       }
-      if (metadata != null) {
+      if (fileWithPathsForCoverage != null) {
         result.add("--post_processor");
-        result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, metadata);
+        result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, fileWithPathsForCoverage);
         result.addPath(
             configuration
                 .getCoverageMetadataDirectory(targetLabel.getPackageIdentifier().getRepository())
@@ -869,8 +878,8 @@
       return this;
     }
 
-    public Builder setMetadata(Artifact metadata) {
-      this.metadata = metadata;
+    public Builder setFileWithPathsForCoverage(Artifact fileWithExecPathsForCoverage) {
+      this.fileWithPathsForCoverage = fileWithExecPathsForCoverage;
       return this;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
index 5932b19..9326f73 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibrary.java
@@ -126,8 +126,7 @@
 
     Artifact outputDepsProto = helper.createOutputDepsProtoArtifact(classJar, javaArtifactsBuilder);
 
-    helper.createCompileActionWithInstrumentation(classJar, manifestProtoOutput, genSourceJar,
-        outputDepsProto, javaArtifactsBuilder);
+    helper.createCompileAction(classJar, manifestProtoOutput, genSourceJar, outputDepsProto);
     helper.createSourceJarAction(srcJar, genSourceJar);
 
     Artifact iJar = null;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibraryHelper.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibraryHelper.java
index 1be54d5..8b43e94 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibraryHelper.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaLibraryHelper.java
@@ -188,11 +188,7 @@
             jacocoInstrumental);
     Artifact outputDepsProto = helper.createOutputDepsProtoArtifact(output, artifactsBuilder);
     helper.createCompileAction(
-        output,
-        null /* manifestProtoOutput */,
-        null /* gensrcOutputJar */,
-        outputDepsProto,
-        null /* outputMetadata */);
+        output, null /* manifestProtoOutput */, null /* gensrcOutputJar */, outputDepsProto);
     helper.createCompileTimeJarAction(output, artifactsBuilder);
     artifactsBuilder.addRuntimeJar(output);
 
@@ -244,7 +240,6 @@
             .build();
     attributes.addCompileTimeClassPathEntries(args.getCompileTimeJars());
     attributes.addRuntimeClassPathEntries(args.getRuntimeJars());
-    attributes.addInstrumentationMetadataEntries(args.getInstrumentationMetadata());
   }
 
   private NestedSet<Artifact> getNonRecursiveCompileTimeJarsFromDeps() {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java
index 0dd4ab0..7289d1a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaSemantics.java
@@ -21,6 +21,7 @@
 import com.google.common.base.Optional;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Streams;
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.LanguageDependentFragment.LibraryLanguage;
 import com.google.devtools.build.lib.analysis.OutputGroupProvider;
@@ -31,6 +32,7 @@
 import com.google.devtools.build.lib.analysis.Runfiles.Builder;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.ComputedSubstitution;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -44,8 +46,10 @@
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import java.io.File;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 
 /**
@@ -65,6 +69,9 @@
   SafeImplicitOutputsFunction JAVA_BINARY_SOURCE_JAR =
       fromTemplates("%{name}-src.jar");
 
+  SafeImplicitOutputsFunction JAVA_COVERAGE_RUNTIME_CLASS_PATH_TXT =
+      fromTemplates("%{name}-runtime-classpath.txt");
+
   SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_JAR =
       fromTemplates("%{name}_deploy.jar");
   SafeImplicitOutputsFunction JAVA_BINARY_MERGED_JAR =
@@ -209,7 +216,34 @@
                 Optional.presentInstances(javaConfig.getBytecodeOptimizers().values()));
           });
 
-  String IJAR_LABEL = "//tools/defaults:ijar";
+  String JACOCO_METADATA_PLACEHOLDER = "%set_jacoco_metadata%";
+  String JACOCO_MAIN_CLASS_PLACEHOLDER = "%set_jacoco_main_class%";
+  String JACOCO_JAVA_RUNFILES_ROOT_PLACEHOLDER = "%set_jacoco_java_runfiles_root%";
+
+  /**
+   * Substitution for exporting the jars needed for jacoco coverage.
+   */
+  class ComputedJacocoSubstitution extends ComputedSubstitution {
+    private final NestedSet<Artifact> jars;
+    private final String pathPrefix;
+
+    public ComputedJacocoSubstitution(NestedSet<Artifact> jars, String workspacePrefix) {
+      super(JACOCO_METADATA_PLACEHOLDER);
+      this.jars = jars;
+      this.pathPrefix = "${JAVA_RUNFILES}/" + workspacePrefix;
+    }
+
+    /**
+     * Concatenating the root relative paths of the artifacts. Each relative path entry is prepended
+     * with "${JAVA_RUNFILES}" and the workspace prefix.
+     */
+    @Override
+    public String getValue() {
+      return Streams.stream(jars)
+          .map(artifact -> pathPrefix + "/" + artifact.getRootRelativePathString())
+          .collect(Collectors.joining(File.pathSeparator, "export JACOCO_METADATA_JARS=", ""));
+    }
+  }
 
   /**
    * Verifies if the rule contains any errors.
@@ -286,6 +320,8 @@
       List<String> jvmFlags,
       Artifact executable,
       String javaStartClass,
+      String coverageStartClass,
+      NestedSetBuilder<Artifact> filesBuilder,
       String javaExecutable);
 
   /**
@@ -331,13 +367,7 @@
    *
    * @return new main class
    */
-  String addCoverageSupport(
-      JavaCompilationHelper helper,
-      JavaTargetAttributes.Builder attributes,
-      Artifact executable,
-      Artifact instrumentationMetadata,
-      JavaCompilationArtifacts.Builder javaArtifactsBuilder,
-      String mainClass)
+  String addCoverageSupport(JavaCompilationHelper helper, Artifact executable)
       throws InterruptedException;
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java b/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
index 28f6c86..2cf3a36a 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/java/JavaTargetAttributes.java
@@ -81,7 +81,6 @@
     private final Map<PathFragment, Artifact> resources = new LinkedHashMap<>();
     private final NestedSetBuilder<Artifact> resourceJars = NestedSetBuilder.stableOrder();
     private final List<Artifact> messages = new ArrayList<>();
-    private final List<Artifact> instrumentationMetadata = new ArrayList<>();
     private final List<Artifact> sourceJars = new ArrayList<>();
 
     private final List<Artifact> classPathResources = new ArrayList<>();
@@ -141,7 +140,6 @@
       Preconditions.checkArgument(!built);
       addCompileTimeClassPathEntries(context.getCompileTimeJars());
       addRuntimeClassPathEntries(context.getRuntimeJars());
-      addInstrumentationMetadataEntries(context.getInstrumentationMetadata());
       return this;
     }
 
@@ -255,12 +253,6 @@
       return this;
     }
 
-    public Builder addInstrumentationMetadataEntries(Iterable<Artifact> metadataEntries) {
-      Preconditions.checkArgument(!built);
-      Iterables.addAll(instrumentationMetadata, metadataEntries);
-      return this;
-    }
-
     public Builder addNativeLibrary(Artifact nativeLibrary) {
       Preconditions.checkArgument(!built);
       String name = nativeLibrary.getFilename();
@@ -400,12 +392,6 @@
       return !sourceFiles.isEmpty();
     }
 
-    /** @deprecated prefer {@link JavaTargetAttributes#getInstrumentationMetadata} */
-    @Deprecated
-    public List<Artifact> getInstrumentationMetadata() {
-      return instrumentationMetadata;
-    }
-
     /** @deprecated prefer {@link JavaTargetAttributes#hasSourceJars} */
     @Deprecated
     public boolean hasSourceJars() {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
index 4ed0142..341a7ab 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/java/JavaSkylarkApiTest.java
@@ -869,10 +869,6 @@
         args.getCompileTimeJars(), otherArgs.getCompileTimeJars())) {
       return false;
     }
-    if (!nestedSetsOfArtifactHaveTheSameParent(
-        args.getInstrumentationMetadata(), otherArgs.getInstrumentationMetadata())) {
-      return false;
-    }
     if (!nestedSetsOfArtifactHaveTheSameParent(args.getRuntimeJars(), otherArgs.getRuntimeJars())) {
       return false;
     }
diff --git a/src/test/java/com/google/devtools/build/lib/rules/java/proto/SkylarkJavaLiteProtoLibraryTest.java b/src/test/java/com/google/devtools/build/lib/rules/java/proto/SkylarkJavaLiteProtoLibraryTest.java
index 0622eeb..ead884a 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/java/proto/SkylarkJavaLiteProtoLibraryTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/java/proto/SkylarkJavaLiteProtoLibraryTest.java
@@ -297,7 +297,6 @@
             "              srcs = ['input1.proto', 'input2.proto'])");
     JavaCompilationArgs compilationArgs =
         getProvider(JavaCompilationArgsProvider.class, rule).getJavaCompilationArgs();
-    assertThat(compilationArgs.getInstrumentationMetadata()).isEmpty();
 
     JavaSourceJarsProvider sourceJarsProvider = getProvider(JavaSourceJarsProvider.class, rule);
     assertThat(sourceJarsProvider).isNotNull();
diff --git a/src/test/shell/bazel/bazel_coverage_test.sh b/src/test/shell/bazel/bazel_coverage_test.sh
index 25e20dc..a084419 100755
--- a/src/test/shell/bazel/bazel_coverage_test.sh
+++ b/src/test/shell/bazel/bazel_coverage_test.sh
@@ -202,7 +202,98 @@
   [ -e $coverage_file_path ] || fail "Coverage output file does not exist!"
 
   cat <<EOF > result.dat
-SF:com/example/Collatz.java
+SF:src/main/com/example/Collatz.java
+FN:3,com/example/Collatz::<init> ()V
+FNDA:0,com/example/Collatz::<init> ()V
+FN:6,com/example/Collatz::getCollatzFinal (I)I
+FNDA:1,com/example/Collatz::getCollatzFinal (I)I
+BA:6,2
+BA:6,2
+BA:9,2
+BA:9,2
+DA:3,0
+DA:6,3
+DA:7,2
+DA:9,4
+DA:10,5
+DA:12,7
+end_of_record
+EOF
+  if ! cmp result.dat $coverage_file_path; then
+    fail "Coverage output file is different than the expected file"
+  fi
+}
+
+function test_java_test_java_import_coverage() {
+
+  cat <<EOF > BUILD
+java_test(
+    name = "test",
+    srcs = glob(["src/test/**/*.java"]),
+    test_class = "com.example.TestCollatz",
+    deps = [":collatz-import"],
+)
+
+java_import(
+    name = "collatz-import",
+    jars = [":libcollatz-lib.jar"],
+)
+
+java_library(
+    name = "collatz-lib",
+    srcs = glob(["src/main/**/*.java"]),
+)
+EOF
+
+  mkdir -p src/main/com/example
+  cat <<EOF > src/main/com/example/Collatz.java
+package com.example;
+
+public class Collatz {
+
+  public static int getCollatzFinal(int n) {
+    if (n == 1) {
+      return 1;
+    }
+    if (n % 2 == 0) {
+      return getCollatzFinal(n / 2);
+    } else {
+      return getCollatzFinal(n * 3 + 1);
+    }
+  }
+
+}
+EOF
+
+  mkdir -p src/test/com/example
+  cat <<EOF > src/test/com/example/TestCollatz.java
+package com.example;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+public class TestCollatz {
+
+  @Test
+  public void testGetCollatzFinal() {
+    assertEquals(Collatz.getCollatzFinal(1), 1);
+    assertEquals(Collatz.getCollatzFinal(5), 1);
+    assertEquals(Collatz.getCollatzFinal(10), 1);
+    assertEquals(Collatz.getCollatzFinal(21), 1);
+  }
+
+}
+EOF
+
+  bazel coverage //:test &>$TEST_log || fail "Coverage for //:test failed"
+  cat $TEST_log
+  ending_part=$(sed -n -e '/PASSED/,$p' $TEST_log)
+
+  coverage_file_path=$(grep -Eo "/[/a-zA-Z0-9\.\_\-]+\.dat$" <<< "$ending_part")
+  [ -e $coverage_file_path ] || fail "Coverage output file not exists!"
+
+  cat <<EOF > result.dat
+SF:src/main/com/example/Collatz.java
 FN:3,com/example/Collatz::<init> ()V
 FNDA:0,com/example/Collatz::<init> ()V
 FN:6,com/example/Collatz::getCollatzFinal (I)I
@@ -221,7 +312,7 @@
 EOF
 
   if ! cmp result.dat $coverage_file_path; then
-    fail "Coverage output file is different with expected"
+    fail "Coverage output file is different than the expected file"
   fi
 }