diff options
7 files changed, 134 insertions, 13 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java index 4baf3b3d7e..48887ddf1b 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/BaseRuleClasses.java @@ -93,7 +93,17 @@ public class BaseRuleClasses { @Override public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { return configuration.isCodeCoverageEnabled() - ? ImmutableList.<Label>copyOf(configuration.getCoverageLabels()) + ? ImmutableList.copyOf(configuration.getCoverageLabels()) + : ImmutableList.<Label>of(); + } + }; + + private static final LateBoundLabelList<BuildConfiguration> GCOV = + new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) { + @Override + public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { + return configuration.isCodeCoverageEnabled() + ? ImmutableList.copyOf(configuration.getGcovLabels()) : ImmutableList.<Label>of(); } }; @@ -103,7 +113,7 @@ public class BaseRuleClasses { @Override public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { return configuration.isCodeCoverageEnabled() - ? ImmutableList.<Label>copyOf(configuration.getCoverageReportGeneratorLabels()) + ? ImmutableList.copyOf(configuration.getCoverageReportGeneratorLabels()) : ImmutableList.<Label>of(); } }; @@ -158,6 +168,7 @@ public class BaseRuleClasses { // implicitly depend on crosstool, which provides gcov. We could add gcov to // InstrumentedFilesProvider.getInstrumentationMetadataFiles() (or a new method) for // all the test rules that have C++ in their transitive closure. Then this could go. + .add(attr(":gcov", LABEL_LIST).cfg(HOST).value(GCOV)) .add(attr(":coverage_support", LABEL_LIST).cfg(HOST).value(COVERAGE_SUPPORT)) .add(attr(":coverage_report_generator", LABEL_LIST).cfg(HOST) .value(COVERAGE_REPORT_GENERATOR)) diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java index f8b8199c16..d613f1e37e 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java @@ -173,13 +173,20 @@ public final class BuildConfiguration { } /** - * Returns all the coverage labels for the fragment. + * Returns the labels required to run coverage for the fragment. */ public ImmutableList<Label> getCoverageLabels() { return ImmutableList.of(); } /** + * Returns all labels required to run gcov, if provided by this fragment. + */ + public ImmutableList<Label> getGcovLabels() { + return ImmutableList.of(); + } + + /** * Returns the coverage report generator tool labels. */ public ImmutableList<Label> getCoverageReportGeneratorLabels() { @@ -915,6 +922,7 @@ public final class BuildConfiguration { private final ImmutableSet<Label> coverageLabels; private final ImmutableSet<Label> coverageReportGeneratorLabels; + private final ImmutableSet<Label> gcovLabels; // TODO(bazel-team): Move this to a configuration fragment. private final PathFragment shExecutable; @@ -1048,12 +1056,15 @@ public final class BuildConfiguration { ImmutableSet.Builder<Label> coverageLabelsBuilder = ImmutableSet.builder(); ImmutableSet.Builder<Label> coverageReportGeneratorLabelsBuilder = ImmutableSet.builder(); + ImmutableSet.Builder<Label> gcovLabelsBuilder = ImmutableSet.builder(); for (Fragment fragment : fragments.values()) { coverageLabelsBuilder.addAll(fragment.getCoverageLabels()); coverageReportGeneratorLabelsBuilder.addAll(fragment.getCoverageReportGeneratorLabels()); + gcovLabelsBuilder.addAll(fragment.getGcovLabels()); } this.coverageLabels = coverageLabelsBuilder.build(); this.coverageReportGeneratorLabels = coverageReportGeneratorLabelsBuilder.build(); + this.gcovLabels = gcovLabelsBuilder.build(); this.defaultShellEnvironment = setupShellEnvironment(); @@ -1519,6 +1530,13 @@ public final class BuildConfiguration { } /** + * Returns the set of labels for gcov. + */ + public Set<Label> getGcovLabels() { + return gcovLabels; + } + + /** * Returns the set of labels for the coverage report generator. */ public Set<Label> getCoverageReportGeneratorLabels() { diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java index 06c9e6b9cc..a32c11697b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java @@ -1806,7 +1806,7 @@ public class CppConfiguration extends BuildConfiguration.Fragment { } @Override - public ImmutableList<Label> getCoverageLabels() { + public ImmutableList<Label> getGcovLabels() { // TODO(bazel-team): Using a gcov-specific crosstool filegroup here could reduce the number of // inputs significantly. We'd also need to add logic in tools/coverage/collect_coverage.sh to // drop crosstool dependency if metadataFiles does not contain *.gcno artifacts. diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java index 5bbd81f126..4147412439 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java @@ -40,14 +40,13 @@ public final class ExperimentalIosTest extends IosTest { Runfiles.Builder runfilesBuilder = new Runfiles.Builder() .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES); - NestedSetBuilder<Artifact> filesToBuildBuilder = NestedSetBuilder.<Artifact>stableOrder(); - filesToBuildBuilder.addTransitive(filesToBuild); + NestedSetBuilder<Artifact> filesToBuildBuilder = NestedSetBuilder.<Artifact>stableOrder() + .addTransitive(filesToBuild); - TestSupport testSupport = - new TestSupport(ruleContext) - .registerTestRunnerActions() - .addRunfiles(runfilesBuilder) - .addFilesToBuild(filesToBuildBuilder); + TestSupport testSupport = new TestSupport(ruleContext) + .registerTestRunnerActions() + .addRunfiles(runfilesBuilder, common.getObjcProvider()) + .addFilesToBuild(filesToBuildBuilder); Artifact executable = testSupport.generatedTestScript(); @@ -61,6 +60,7 @@ public final class ExperimentalIosTest extends IosTest { .add(RunfilesProvider.class, RunfilesProvider.simple(runfiles)) .add(ExecutionInfoProvider.class, new ExecutionInfoProvider(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, ""))) + .addProviders(testSupport.getExtraProviders(common.getObjcProvider())) .setRunfilesSupport(runfilesSupport, executable) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java index 2a866b720c..7bf6ec2993 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java @@ -14,20 +14,28 @@ package com.google.devtools.build.lib.rules.objc; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.Type.LABEL; import static com.google.devtools.build.lib.packages.Type.LABEL_LIST; import static com.google.devtools.build.lib.packages.Type.STRING_LIST; +import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.Constants; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.analysis.config.BuildConfiguration; +import com.google.devtools.build.lib.packages.Attribute.LateBoundLabelList; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; +import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.syntax.Label; import com.google.devtools.build.lib.util.FileType; +import java.util.List; + /** * Rule definition for {@code experimental_ios_test} rule in Bazel. * @@ -78,6 +86,17 @@ public final class ExperimentalIosTestRule implements RuleDefinition { .add(attr("$test_template", LABEL) .value(env.getLabel("//tools/objc:ios_test.sh.bazel_template"))) .add(attr("$test_runner", LABEL).value(env.getLabel("//tools/objc:testrunner"))) + .override(attr(":gcov", LABEL_LIST).cfg(HOST) + .value(new LateBoundLabelList<BuildConfiguration>() { + @Override + public List<Label> getDefault(Rule rule, BuildConfiguration configuration) { + if (!configuration.isCodeCoverageEnabled()) { + return ImmutableList.of(); + } + return ImmutableList.of( + configuration.getFragment(ObjcConfiguration.class).getGcovLabel()); + } + })) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java index f1774e4bb0..5506115905 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java @@ -18,22 +18,28 @@ import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.FileProvider; import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; -import com.google.devtools.build.lib.analysis.Runfiles; +import com.google.devtools.build.lib.analysis.Runfiles.Builder; import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction; import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.rules.objc.ObjcProvider.Key; +import com.google.devtools.build.lib.rules.test.InstrumentedFilesProvider; +import com.google.devtools.build.lib.rules.test.InstrumentedFilesProviderImpl; import com.google.devtools.build.lib.util.FileType; import java.util.List; +import java.util.Map; import javax.annotation.Nullable; @@ -184,8 +190,10 @@ class TestSupport { /** * Adds all files needed to run this test to the passed Runfiles builder. + * + * @param objcProvider common information about this rule's attributes and its dependencies */ - TestSupport addRunfiles(Runfiles.Builder runfilesBuilder) { + TestSupport addRunfiles(Builder runfilesBuilder, ObjcProvider objcProvider) { runfilesBuilder .addArtifact(testIpa()) .addArtifacts(xctestIpa().asSet()) @@ -199,10 +207,71 @@ class TestSupport { } else { runfilesBuilder.addTransitiveArtifacts(labDeviceRunfiles()); } + + if (ruleContext.getConfiguration().isCodeCoverageEnabled()) { + runfilesBuilder + .addTransitiveArtifacts(objcProvider.get(ObjcProvider.SOURCE)) + .addTransitiveArtifacts(gcnoFiles(objcProvider)); + } return this; } /** + * Returns any additional providers that need to be exported to the rule context to the passed + * builder. + * + * @param objcProvider common information about this rule's attributes and its dependencies + */ + public Map<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider> + getExtraProviders(ObjcProvider objcProvider) { + return ImmutableMap.<Class<? extends TransitiveInfoProvider>, TransitiveInfoProvider>of( + InstrumentedFilesProvider.class, + new InstrumentedFilesProviderImpl( + instrumentedFiles(objcProvider), gcnoFiles(objcProvider), gcovEnv())); + } + + /** + * Returns a map of extra environment variable names to their values used to point to gcov binary, + * which should be added to the test action environment, if coverage is enabled. + */ + private Map<String, String> gcovEnv() { + if (ruleContext.getConfiguration().isCodeCoverageEnabled()) { + return ImmutableMap.of("COVERAGE_GCOV_PATH", + ruleContext.getHostPrerequisiteArtifact(":gcov").getExecPathString()); + } + return ImmutableMap.of(); + } + + /** + * Returns all GCC coverage notes files available for computing coverage. + */ + private NestedSet<Artifact> gcnoFiles(ObjcProvider objcProvider) { + return filesWithXcTestApp(ObjcProvider.GCNO, objcProvider); + } + + /** + * Returns all source files from the test (and if present xctest app) which have been + * instrumented for code coverage. + */ + private NestedSet<Artifact> instrumentedFiles(ObjcProvider objcProvider) { + return filesWithXcTestApp(ObjcProvider.INSTRUMENTED_SOURCE, objcProvider); + } + + private NestedSet<Artifact> filesWithXcTestApp(Key<Artifact> key, ObjcProvider objcProvider) { + NestedSet<Artifact> underlying = objcProvider.get(key); + XcTestAppProvider provider = ruleContext.getPrerequisite( + IosTest.XCTEST_APP, Mode.TARGET, XcTestAppProvider.class); + if (provider == null) { + return underlying; + } + + return NestedSetBuilder.<Artifact>stableOrder() + .addTransitive(underlying) + .addTransitive(provider.getObjcProvider().get(key)) + .build(); + } + + /** * Jar files for plugins to the test runner. May be empty. */ private NestedSet<Artifact> plugins() { diff --git a/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java b/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java index f7393322f1..958e8b3779 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/rules/test/TestActionBuilder.java @@ -209,6 +209,10 @@ public final class TestActionBuilder { ruleContext.getPrerequisites(":coverage_support", Mode.HOST)) { inputsBuilder.addTransitive(dep.getProvider(FileProvider.class).getFilesToBuild()); } + for (TransitiveInfoCollection dep : + ruleContext.getPrerequisites(":gcov", Mode.HOST)) { + inputsBuilder.addTransitive(dep.getProvider(FileProvider.class).getFilesToBuild()); + } Artifact instrumentedFileManifest = InstrumentedFileManifestAction.getInstrumentedFileManifest(ruleContext, ImmutableList.copyOf(instrumentedFiles.getInstrumentedFiles()), |