Skip to content

Commit b874e5f

Browse files
ted-xietjgqBencodesShreeM01
authored
[6.2.0]Add test coverage support to android_local_test (#17467)
* Propagate runfiles for singlejar. Since the singlejar toolchain attribute is currently defined as a single file and carried around as an Artifact instead of a FilesToRunProvider, it's not possible to implement it as a wrapper script that dispatches to an actual implementation somewhere in its runfiles. This would be useful to experiment with cross-platform action sharing. PiperOrigin-RevId: 492487124 Change-Id: Ib0f80314eae09bd865b3f31a4180bf068833cdf4 * Add test coverage support to android_local_test Adding test coverage support to `android_local_test`. #15827 Closes #15840. RELNOTES: Adds coverage metric support to android_local_test PiperOrigin-RevId: 508549884 Change-Id: I6977efa51ca1c7a6df1f776fe1a326d07989a185 --------- Co-authored-by: Googler <[email protected]> Co-authored-by: Benjamin Lee <[email protected]> Co-authored-by: kshyanashree <[email protected]>
1 parent 6c89303 commit b874e5f

File tree

16 files changed

+359
-33
lines changed

16 files changed

+359
-33
lines changed

src/main/java/com/google/devtools/build/lib/bazel/rules/android/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ java_library(
2727
"//src/main/java/com/google/devtools/build/lib/analysis:actions/custom_command_line",
2828
"//src/main/java/com/google/devtools/build/lib/analysis:analysis_cluster",
2929
"//src/main/java/com/google/devtools/build/lib/analysis:blaze_directories",
30+
"//src/main/java/com/google/devtools/build/lib/analysis:config/execution_transition_factory",
3031
"//src/main/java/com/google/devtools/build/lib/analysis:config/toolchain_type_requirement",
3132
"//src/main/java/com/google/devtools/build/lib/analysis:config/transitions/composing_transition_factory",
3233
"//src/main/java/com/google/devtools/build/lib/analysis:rule_definition_environment",

src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTest.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414
package com.google.devtools.build.lib.bazel.rules.android;
1515

16+
import com.google.common.base.Preconditions;
1617
import com.google.common.collect.ImmutableList;
1718
import com.google.common.collect.Iterables;
1819
import com.google.devtools.build.lib.actions.Artifact;
@@ -34,6 +35,9 @@
3435
/** An implementation for the "android_local_test" rule. */
3536
public class BazelAndroidLocalTest extends AndroidLocalTestBase {
3637

38+
private static final String JACOCO_COVERAGE_RUNNER_MAIN_CLASS =
39+
"com.google.testing.coverage.JacocoCoverageRunner";
40+
3741
public BazelAndroidLocalTest() {
3842
super(BazelAndroidSemantics.INSTANCE);
3943
}
@@ -76,9 +80,14 @@ protected String addCoverageSupport(
7680
JavaTargetAttributes.Builder attributesBuilder,
7781
String mainClass)
7882
throws RuleErrorException {
79-
// coverage does not yet work with android_local_test
80-
ruleContext.throwWithRuleError("android_local_test does not yet support coverage");
81-
return "";
83+
// This method can be called only for *_binary/*_test targets.
84+
Preconditions.checkNotNull(executable);
85+
86+
helper.addCoverageSupport();
87+
88+
// We do not add the instrumented jar to the runtime classpath, but provide it in the shell
89+
// script via an environment variable.
90+
return JACOCO_COVERAGE_RUNNER_MAIN_CLASS;
8291
}
8392

8493
@Override

src/main/java/com/google/devtools/build/lib/bazel/rules/android/BazelAndroidLocalTestRule.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.google.devtools.build.lib.analysis.BaseRuleClasses;
2626
import com.google.devtools.build.lib.analysis.RuleDefinition;
2727
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
28+
import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory;
2829
import com.google.devtools.build.lib.bazel.rules.java.BazelJavaRuleClasses.BaseJavaBinaryRule;
2930
import com.google.devtools.build.lib.packages.ImplicitOutputsFunction;
3031
import com.google.devtools.build.lib.packages.RuleClass;
@@ -82,7 +83,11 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment envi
8283
.removeAttribute("main_class")
8384
.removeAttribute("resources")
8485
.removeAttribute("use_testrunner")
85-
.removeAttribute(":java_launcher")
86+
.removeAttribute(":java_launcher") // Input files for test actions collecting code coverage
87+
.add(
88+
attr(":lcov_merger", LABEL)
89+
.cfg(ExecutionTransitionFactory.create())
90+
.value(BaseRuleClasses.getCoverageOutputGeneratorLabel()))
8691
.cfg(
8792
new ConfigFeatureFlagTransitionFactory(AndroidFeatureFlagSetProvider.FEATURE_FLAG_ATTR))
8893
.build();

src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
2121
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
2222
import com.google.devtools.build.lib.analysis.FileProvider;
23+
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
2324
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
2425
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
2526
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetFactory;
@@ -403,7 +404,7 @@ private static SpawnAction createAarEmbeddedJarsExtractorActions(
403404
private static SpawnAction createAarJarsMergingActions(
404405
RuleContext ruleContext, Artifact jarsTreeArtifact, Artifact mergedJar, Artifact paramFile) {
405406
SpawnAction.Builder builder = new SpawnAction.Builder().useDefaultShellEnvironment();
406-
Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
407+
FilesToRunProvider singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
407408
return builder
408409
.setExecutable(singleJar)
409410
.setMnemonic("AarJarsMerger")

src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1957,7 +1957,7 @@ private static SpawnAction.Builder createSpawnActionBuilder(RuleContext ruleCont
19571957
}
19581958

19591959
private static SpawnAction.Builder singleJarSpawnActionBuilder(RuleContext ruleContext) {
1960-
Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
1960+
FilesToRunProvider singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
19611961
SpawnAction.Builder builder =
19621962
createSpawnActionBuilder(ruleContext).useDefaultShellEnvironment();
19631963
builder.setExecutable(singleJar);

src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
2121
import com.google.devtools.build.lib.analysis.Allowlist;
2222
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
23+
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
2324
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
2425
import com.google.devtools.build.lib.analysis.RequiredConfigFragmentsProvider;
2526
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
@@ -29,6 +30,7 @@
2930
import com.google.devtools.build.lib.analysis.Runfiles;
3031
import com.google.devtools.build.lib.analysis.RunfilesProvider;
3132
import com.google.devtools.build.lib.analysis.RunfilesSupport;
33+
import com.google.devtools.build.lib.analysis.SourceManifestAction;
3234
import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
3335
import com.google.devtools.build.lib.analysis.actions.Substitution;
3436
import com.google.devtools.build.lib.analysis.actions.Template;
@@ -66,6 +68,7 @@
6668
import com.google.devtools.build.lib.rules.java.OneVersionCheckActionBuilder;
6769
import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistryProvider;
6870
import com.google.devtools.build.lib.util.OS;
71+
import com.google.devtools.build.lib.util.Pair;
6972
import com.google.devtools.build.lib.vfs.PathFragment;
7073
import java.util.ArrayList;
7174
import java.util.List;
@@ -292,7 +295,7 @@ public ConfiguredTarget create(RuleContext ruleContext)
292295
originalMainClass,
293296
filesToBuildBuilder,
294297
javaExecutable,
295-
/* createCoverageMetadataJar= */ true);
298+
/* createCoverageMetadataJar= */ false);
296299

297300
Artifact oneVersionOutputArtifact = null;
298301
JavaConfiguration javaConfig = ruleContext.getFragment(JavaConfiguration.class);
@@ -364,6 +367,60 @@ public ConfiguredTarget create(RuleContext ruleContext)
364367

365368
JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create();
366369

370+
NestedSetBuilder<Pair<String, String>> coverageEnvironment = NestedSetBuilder.stableOrder();
371+
NestedSetBuilder<Artifact> coverageSupportFiles = NestedSetBuilder.stableOrder();
372+
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
373+
374+
// Create an artifact that contains the runfiles relative paths of the jars on the runtime
375+
// classpath. Using SourceManifestAction is the only reliable way to match the runfiles
376+
// creation code.
377+
Artifact runtimeClasspathArtifact =
378+
ruleContext.getUniqueDirectoryArtifact(
379+
"runtime_classpath_for_coverage",
380+
"runtime_classpath.txt",
381+
ruleContext.getBinOrGenfilesDirectory());
382+
ruleContext.registerAction(
383+
new SourceManifestAction(
384+
SourceManifestAction.ManifestType.SOURCES_ONLY,
385+
ruleContext.getActionOwner(),
386+
runtimeClasspathArtifact,
387+
new Runfiles.Builder(
388+
ruleContext.getWorkspaceName(),
389+
ruleContext.getConfiguration().legacyExternalRunfiles())
390+
// This matches the code below in collectDefaultRunfiles.
391+
.addTransitiveArtifactsWrappedInStableOrder(javaCommon.getRuntimeClasspath())
392+
.build(),
393+
null,
394+
true));
395+
filesToBuildBuilder.add(runtimeClasspathArtifact);
396+
397+
// Pass the artifact through an environment variable in the coverage environment so it
398+
// can be read by the coverage collection script.
399+
coverageEnvironment.add(
400+
new Pair<>(
401+
"JAVA_RUNTIME_CLASSPATH_FOR_COVERAGE", runtimeClasspathArtifact.getExecPathString()));
402+
// Add the file to coverageSupportFiles so it ends up as an input for the test action
403+
// when coverage is enabled.
404+
coverageSupportFiles.add(runtimeClasspathArtifact);
405+
406+
// Make single jar reachable from the coverage environment because it needs to be executed
407+
// by the coverage collection script.
408+
FilesToRunProvider singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
409+
coverageEnvironment.add(
410+
new Pair<>("SINGLE_JAR_TOOL", singleJar.getExecutable().getExecPathString()));
411+
coverageSupportFiles.addTransitive(singleJar.getFilesToRun());
412+
}
413+
414+
javaCommon.addTransitiveInfoProviders(
415+
builder,
416+
javaInfoBuilder,
417+
filesToBuild,
418+
classJar,
419+
coverageEnvironment.build(),
420+
coverageSupportFiles.build());
421+
javaCommon.addGenJarsProvider(
422+
builder, javaInfoBuilder, outputs.genClass(), outputs.genSource());
423+
367424
javaCommon.addTransitiveInfoProviders(builder, javaInfoBuilder, filesToBuild, classJar);
368425
javaCommon.addGenJarsProvider(
369426
builder, javaInfoBuilder, outputs.genClass(), outputs.genSource());

src/main/java/com/google/devtools/build/lib/rules/android/ApkActionsBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ private void signApk(
418418

419419
private static void setSingleJarAsExecutable(
420420
RuleContext ruleContext, SpawnAction.Builder builder) {
421-
Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
421+
FilesToRunProvider singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
422422
builder.setExecutable(singleJar);
423423
}
424424

src/main/java/com/google/devtools/build/lib/rules/java/DeployArchiveBuilder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.google.devtools.build.lib.actions.ParamFileInfo;
2727
import com.google.devtools.build.lib.actions.ParameterFile.ParameterFileType;
2828
import com.google.devtools.build.lib.actions.ResourceSet;
29+
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
2930
import com.google.devtools.build.lib.analysis.RuleContext;
3031
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
3132
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
@@ -420,7 +421,7 @@ public void build() throws InterruptedException {
420421
inputs.add(libModules);
421422
}
422423

423-
Artifact singlejar = JavaToolchainProvider.from(ruleContext).getSingleJar();
424+
FilesToRunProvider singlejar = JavaToolchainProvider.from(ruleContext).getSingleJar();
424425

425426
String toolchainIdentifier = null;
426427
try {

src/main/java/com/google/devtools/build/lib/rules/java/JavaBinary.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
3232
import com.google.devtools.build.lib.analysis.Allowlist;
3333
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
34+
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
3435
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
3536
import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts;
3637
import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder;
@@ -510,9 +511,10 @@ public ConfiguredTarget create(RuleContext ruleContext)
510511

511512
// Make single jar reachable from the coverage environment because it needs to be executed
512513
// by the coverage collection script.
513-
Artifact singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
514-
coverageEnvironment.add(new Pair<>("SINGLE_JAR_TOOL", singleJar.getExecPathString()));
515-
coverageSupportFiles.add(singleJar);
514+
FilesToRunProvider singleJar = JavaToolchainProvider.from(ruleContext).getSingleJar();
515+
coverageEnvironment.add(
516+
new Pair<>("SINGLE_JAR_TOOL", singleJar.getExecutable().getExecPathString()));
517+
coverageSupportFiles.addTransitive(singleJar.getFilesToRun());
516518
}
517519

518520
common.addTransitiveInfoProviders(

src/main/java/com/google/devtools/build/lib/rules/java/JavaToolchain.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public ConfiguredTarget create(RuleContext ruleContext)
7878
.get("reduced_classpath_incompatible_processors", Type.STRING_LIST));
7979
boolean forciblyDisableHeaderCompilation =
8080
ruleContext.attributes().get("forcibly_disable_header_compilation", Type.BOOLEAN);
81-
Artifact singleJar = ruleContext.getPrerequisiteArtifact("singlejar");
81+
FilesToRunProvider singleJar = ruleContext.getExecutablePrerequisite("singlejar");
8282
FilesToRunProvider oneVersion = ruleContext.getExecutablePrerequisite("oneversion");
8383
Artifact oneVersionAllowlist = ruleContext.getPrerequisiteArtifact("oneversion_whitelist");
8484
Artifact genClass = ruleContext.getPrerequisiteArtifact("genclass");

0 commit comments

Comments
 (0)