diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules')
4 files changed, 130 insertions, 10 deletions
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 ed229fee77..2e7f0c5bd0 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 @@ -175,7 +175,8 @@ public class JavaBinary implements RuleConfiguredTargetFactory { executableForRunfiles, instrumentationMetadata, javaArtifactsBuilder, - mainClass); + mainClass, + ruleContext.getConfiguration().isExperimentalJavaCoverage()); } } else { filesBuilder.add(classJar); @@ -262,6 +263,8 @@ public class JavaBinary implements RuleConfiguredTargetFactory { jvmFlags, executableForRunfiles, mainClass, + originalMainClass, + filesBuilder, javaExecutable); if (!executableToRun.equals(executableForRunfiles)) { filesBuilder.add(executableToRun); @@ -526,7 +529,8 @@ public class JavaBinary implements RuleConfiguredTargetFactory { builder.addTargets(runtimeDeps, RunfilesProvider.DEFAULT_RUNFILES); semantics.addDependenciesForRunfiles(ruleContext, builder); - if (ruleContext.getConfiguration().isCodeCoverageEnabled()) { + if (ruleContext.getConfiguration().isCodeCoverageEnabled() + && !ruleContext.getConfiguration().isExperimentalJavaCoverage()) { Artifact instrumentedJar = javaArtifacts.getInstrumentedJar(); if (instrumentedJar != null) { builder.addArtifact(instrumentedJar); 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 64140170cf..75877350ac 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 @@ -168,14 +168,15 @@ public final class JavaCompilationHelper { * (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 instrumentationMetadataJar metadata file (null if no instrumentation is needed or if + * --experimental_java_coverage is true). */ public void createCompileAction( Artifact outputJar, Artifact manifestProtoOutput, @Nullable Artifact gensrcOutputJar, @Nullable Artifact outputDepsProto, - @Nullable Artifact outputMetadata) { + @Nullable Artifact instrumentationMetadataJar) { JavaTargetAttributes attributes = getAttributes(); @@ -196,6 +197,7 @@ public final class JavaCompilationHelper { } JavaCompileAction.Builder builder = createJavaCompileActionBuilder(semantics); + builder.setArtifactForExperimentalCoverage(maybeCreateExperimentalCoverageArtifact(outputJar)); builder.setClasspathEntries(attributes.getCompileTimeClassPath()); builder.setBootclasspathEntries(getBootclasspathOrDefault()); builder.setSourcePathEntries(attributes.getSourcePath()); @@ -208,7 +210,7 @@ public final class JavaCompilationHelper { builder.setGensrcOutputJar(gensrcOutputJar); builder.setOutputDepsProto(outputDepsProto); builder.setAdditionalOutputs(attributes.getAdditionalOutputs()); - builder.setMetadata(outputMetadata); + builder.setMetadata(instrumentationMetadataJar); builder.setInstrumentationJars(jacocoInstrumentation); builder.setSourceFiles(attributes.getSourceFiles()); builder.addSourceJars(attributes.getSourceJars()); @@ -253,6 +255,26 @@ public final class JavaCompilationHelper { } /** + * Creates an {@link Artifact} needed by {@code JacocoCoverageRunner} when + * {@code --experimental_java_coverage} is true. + * + * <p> The {@link Artifact} is created in the same directory as the given {@code compileJar} and + * has the suffix {@code -paths-for-coverage.txt}. + * + * <p> Returns {@code null} if {@code compileJar} should not be instrumented. + */ + private Artifact maybeCreateExperimentalCoverageArtifact(Artifact compileJar) { + if (!shouldInstrumentJar() || !getConfiguration().isExperimentalJavaCoverage()) { + return null; + } + PathFragment packageRelativePath = + compileJar.getRootRelativePath().relativeTo(ruleContext.getPackageDirectory()); + PathFragment path = + FileSystemUtils.replaceExtension(packageRelativePath, "-paths-for-coverage.txt"); + return ruleContext.getPackageRelativeArtifact(path, compileJar.getRoot()); + } + + /** * 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. @@ -299,6 +321,10 @@ public final class JavaCompilationHelper { @Nullable public Artifact createInstrumentationMetadata(Artifact outputJar, JavaCompilationArtifacts.Builder javaArtifactsBuilder) { + // In the experimental java coverage we don't create the .em jar for instrumentation. + if (getConfiguration().isExperimentalJavaCoverage()) { + return null; + } // If we need to instrument the jar, add additional output (the coverage metadata file) to the // JavaCompileAction. Artifact instrumentationMetadata = null; 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 1dbd8b73c4..304d3f2d44 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 @@ -48,6 +48,7 @@ import com.google.devtools.build.lib.analysis.actions.CommandLine; 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; @@ -511,6 +512,7 @@ public final class JavaCompileAction extends SpawnAction { private Collection<Artifact> additionalOutputs; private Artifact paramFile; private Artifact metadata; + private Artifact artifactForExperimentalCoverage; private ImmutableSet<Artifact> sourceFiles = ImmutableSet.of(); private final Collection<Artifact> sourceJars = new ArrayList<>(); private BuildConfiguration.StrictDepsMode strictJavaDeps = @@ -625,6 +627,11 @@ public final class JavaCompileAction extends SpawnAction { semantics.getJavaBuilderMainClass(), pathSeparator); + if (artifactForExperimentalCoverage != null) { + analysisEnvironment.registerAction(new LazyWritePathsFileAction( + owner, artifactForExperimentalCoverage, sourceFiles, false)); + } + // The actual params-file-based command line executed for a compile action. CommandLine javaBuilderCommandLine = CustomCommandLine.builder() @@ -640,7 +647,7 @@ public final class JavaCompileAction extends SpawnAction { .addAll(instrumentationJars) .build(); - NestedSet<Artifact> inputs = + NestedSetBuilder<Artifact> inputsBuilder = NestedSetBuilder.<Artifact>stableOrder() .addTransitive(classpathEntries) .addTransitive(compileTimeDependencyArtifacts) @@ -652,8 +659,11 @@ public final class JavaCompileAction extends SpawnAction { .addAll(sourcePathEntries) .addAll(extdirInputs) .add(paramFile) - .addTransitive(tools) - .build(); + .addTransitive(tools); + if (artifactForExperimentalCoverage != null) { + inputsBuilder.add(artifactForExperimentalCoverage); + } + NestedSet<Artifact> inputs = inputsBuilder.build(); return new JavaCompileAction( owner, @@ -768,9 +778,13 @@ public final class JavaCompileAction extends SpawnAction { } } } - if (metadata != null) { + + // Chose what artifact to pass to JavaBuilder, as input to jacoco instrumentation processor. + // metadata should be null when --experimental_java_coverage is true. + Artifact coverageArtifact = metadata != null ? metadata : artifactForExperimentalCoverage; + if (coverageArtifact != null) { result.add("--post_processor"); - result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, metadata); + result.addExecPath(JACOCO_INSTRUMENTATION_PROCESSOR, coverageArtifact); result.addPath( configuration .getCoverageMetadataDirectory(targetLabel.getPackageIdentifier().getRepository()) @@ -1006,6 +1020,11 @@ public final class JavaCompileAction extends SpawnAction { return this; } + public Builder setArtifactForExperimentalCoverage(Artifact artifactForExperimentalCoverage) { + this.artifactForExperimentalCoverage = artifactForExperimentalCoverage; + return this; + } + public Builder setRuleKind(String ruleKind) { this.ruleKind = ruleKind; return this; 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 c4af1dfd93..76e0376a8a 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 static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fro 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; 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; @@ -45,8 +47,10 @@ import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistry 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; /** @@ -85,6 +89,9 @@ public interface JavaSemantics { SafeImplicitOutputsFunction JAVA_ONE_VERSION_ARTIFACT = fromTemplates("%{name}-one-version.txt"); + SafeImplicitOutputsFunction JAVA_COVERAGE_RUNTIME_CLASS_PATH_TXT = + fromTemplates("%{name}-runtime-classpath.txt"); + SafeImplicitOutputsFunction JAVA_BINARY_DEPLOY_SOURCE_JAR = fromTemplates("%{name}_deploy-src.jar"); @@ -212,6 +219,35 @@ public interface JavaSemantics { 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. * @@ -293,6 +329,24 @@ public interface JavaSemantics { String javaExecutable); /** + * Same as {@link #createStubAction(RuleContext, JavaCommon, List, Artifact, String, String)}. + * + * <p> In *experimental* coverage mode creates a txt file containing the runtime jars names. + * {@code JacocoCoverageRunner} will use it to retrieve the name of the jars considered for + * collecting coverage. {@code JacocoCoverageRunner} will *not* collect coverage implicitly + * for all the runtime jars, only for those that pack a file ending in "-paths-for-coverage.txt". + */ + public Artifact createStubAction( + RuleContext ruleContext, + JavaCommon javaCommon, + List<String> jvmFlags, + Artifact executable, + String javaStartClass, + String coverageStartClass, + NestedSetBuilder<Artifact> filesBuilder, + String javaExecutable); + + /** * Returns true if {@code createStubAction} considers {@code javaExecutable} as a substitution. * Returns false if {@code createStubAction} considers {@code javaExecutable} as a file path. */ @@ -345,6 +399,23 @@ public interface JavaSemantics { throws InterruptedException; /** + * Same as {@link #addCoverageSupport(JavaCompilationHelper, JavaTargetAttributes.Builder, + * Artifact, Artifact, JavaCompilationArtifacts.Builder, String)}. + * + * <p> In *experimental* coverage mode omits dealing with instrumentation metadata and does not + * create the instrumented jar. + */ + String addCoverageSupport( + JavaCompilationHelper helper, + JavaTargetAttributes.Builder attributes, + Artifact executable, + Artifact instrumentationMetadata, + JavaCompilationArtifacts.Builder javaArtifactsBuilder, + String mainClass, + boolean isExperimentalCoverage) + throws InterruptedException; + + /** * Return the JVM flags to be used in a Java binary. */ Iterable<String> getJvmFlags( |