From 62f5e78d08fbdb86a54b9fcb4a210c7e62ccd783 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 29 Apr 2024 15:59:32 +0200 Subject: [PATCH] [2] Announce combined coverage report on the BES This allows BES consumers to access this file without guessing its path and existence, as well as when the actual coverage generation action runs locally. Also removes an unused `CoverageReport` event that appears to have become obsolete. RELNOTES: The combined coverage report produced via `--combined_report=lcov` is now announced on the BES via the new `CoverageReport` event. --- site/en/remote/bep-glossary.md | 6 ++ .../java/com/google/devtools/build/lib/BUILD | 1 + .../google/devtools/build/lib/analysis/BUILD | 12 +++ .../build/lib/analysis/BuildView.java | 22 +++-- .../analysis/test/CoverageReportEvent.java | 82 +++++++++++++++++++ .../buildeventstream/BuildEventIdUtil.java | 6 ++ .../proto/build_event_stream.proto | 10 +++ .../build/lib/buildtool/SkyframeBuilder.java | 3 + .../google/devtools/build/lib/skyframe/BUILD | 1 + .../build/lib/skyframe/SkyframeBuildView.java | 16 +++- 10 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/analysis/test/CoverageReportEvent.java diff --git a/site/en/remote/bep-glossary.md b/site/en/remote/bep-glossary.md index 678f6ac9deff7a..51ae6d3cc275e7 100644 --- a/site/en/remote/bep-glossary.md +++ b/site/en/remote/bep-glossary.md @@ -210,6 +210,12 @@ workspace as if Bazel had been run locally. } ``` +## CoverageReport {:#coveragereport} + +If [`--combined_report`] is used with the `coverage` command, a `CoverageReport` +event is sent at the end of the build indicating the files of the combined +coverage report. + ## Fetch {:#fetch} Indicates that a Fetch operation occurred as a part of the command execution. diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 0fcb37d7b08bd4..9b9916f0aa5aa5 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -344,6 +344,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_action_finished_event", "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_artifacts_known_event", "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_report_action_factory", + "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_report_event", "//src/main/java/com/google/devtools/build/lib/analysis:top_level_artifact_context", "//src/main/java/com/google/devtools/build/lib/analysis:transitive_info_collection", "//src/main/java/com/google/devtools/build/lib/analysis:view_creation_failed_exception", diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BUILD b/src/main/java/com/google/devtools/build/lib/analysis/BUILD index 50c2b8b63f2eeb..978271131ba268 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BUILD +++ b/src/main/java/com/google/devtools/build/lib/analysis/BUILD @@ -2751,6 +2751,18 @@ java_library( ], ) +java_library( + name = "test/coverage_report_event", + srcs = ["test/CoverageReportEvent.java"], + deps = [ + "//src/main/java/com/google/devtools/build/lib/actions:artifacts", + "//src/main/java/com/google/devtools/build/lib/buildeventstream", + "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:build_event_stream_java_proto", + "//third_party:flogger", + "//third_party:guava", + ], +) + java_library( name = "test/execution_info", srcs = ["test/ExecutionInfo.java"], diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java index 4525afbd71d164..8047d1e15b09ce 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java @@ -87,6 +87,7 @@ import com.google.devtools.build.lib.skyframe.SkyframeAnalysisResult; import com.google.devtools.build.lib.skyframe.SkyframeBuildView; import com.google.devtools.build.lib.skyframe.SkyframeBuildView.BuildDriverKeyTestContext; +import com.google.devtools.build.lib.skyframe.SkyframeBuildView.CoverageReportActionsWrapperSupplier; import com.google.devtools.build.lib.skyframe.SkyframeExecutor; import com.google.devtools.build.lib.skyframe.TargetPatternPhaseValue; import com.google.devtools.build.lib.util.AbruptExitException; @@ -156,7 +157,8 @@ public class BuildView { private final ConfiguredRuleClassProvider ruleClassProvider; - @Nullable private ImmutableSet memoizedCoverageArtifacts; + @Nullable + private CoverageReportActionsWrapperSupplier.CoverageArtifacts memoizedCoverageArtifacts; /** A factory class to create the coverage report action. May be null. */ @Nullable private final CoverageReportActionFactory coverageReportActionFactory; @@ -552,7 +554,8 @@ private AnalysisResult createResult( // Coverage artifactsToBuild.addAll( memoizedGetCoverageArtifactsHelper( - configuredTargets, allTargetsToTest, eventHandler, eventBus, loadingResult)); + configuredTargets, allTargetsToTest, eventHandler, eventBus, loadingResult) + .allCoverageArtifacts()); // TODO(cparsons): If extra actions are ever removed, this filtering step can probably be // removed as well: the only concern would be action conflicts involving coverage artifacts, @@ -846,7 +849,7 @@ private static void pollInterruptedStatus() throws InterruptedException { } } - private ImmutableSet memoizedGetCoverageArtifactsHelper( + private CoverageReportActionsWrapperSupplier.CoverageArtifacts memoizedGetCoverageArtifactsHelper( Set configuredTargets, Set allTargetsToTest, EventHandler eventHandler, @@ -856,11 +859,13 @@ private ImmutableSet memoizedGetCoverageArtifactsHelper( if (memoizedCoverageArtifacts != null) { return memoizedCoverageArtifacts; } - ImmutableSet.Builder resultBuilder = ImmutableSet.builder(); + ImmutableSet.Builder allCoverageArtifacts = ImmutableSet.builder(); + ImmutableSet.Builder coverageReportArtifacts = ImmutableSet.builder(); // Coverage NestedSet baselineCoverageArtifacts = getBaselineCoverageArtifacts(configuredTargets); - resultBuilder.addAll(baselineCoverageArtifacts.toList()); + allCoverageArtifacts.addAll(baselineCoverageArtifacts.toList()); + Artifact coverageReport = null; if (coverageReportActionFactory != null) { CoverageReportActionsWrapper actionsWrapper = coverageReportActionFactory.createCoverageReportActionsWrapper( @@ -875,10 +880,13 @@ private ImmutableSet memoizedGetCoverageArtifactsHelper( loadingResult.getWorkspaceName()); if (actionsWrapper != null) { skyframeExecutor.injectCoverageReportData(actionsWrapper.getActions()); - actionsWrapper.getCoverageOutputs().forEach(resultBuilder::add); + actionsWrapper.getCoverageOutputs().forEach(allCoverageArtifacts::add); + actionsWrapper.getCoverageOutputs().forEach(coverageReportArtifacts::add); } } - memoizedCoverageArtifacts = resultBuilder.build(); + memoizedCoverageArtifacts = + new CoverageReportActionsWrapperSupplier.CoverageArtifacts( + allCoverageArtifacts.build(), coverageReportArtifacts.build()); return memoizedCoverageArtifacts; } } diff --git a/src/main/java/com/google/devtools/build/lib/analysis/test/CoverageReportEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/test/CoverageReportEvent.java new file mode 100644 index 00000000000000..437e0878b0cbca --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/analysis/test/CoverageReportEvent.java @@ -0,0 +1,82 @@ +// Copyright 2023 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.test; + +import static com.google.common.collect.ImmutableList.toImmutableList; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.flogger.GoogleLogger; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.buildeventstream.BuildEvent; +import com.google.devtools.build.lib.buildeventstream.BuildEventContext; +import com.google.devtools.build.lib.buildeventstream.BuildEventIdUtil; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.CoverageReport; +import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.File; +import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent; +import java.util.Collection; + +/** Inform BEP consumers that the coverage report is available. */ +public class CoverageReportEvent implements BuildEvent { + private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); + + private final ImmutableSet coverageReport; + + public CoverageReportEvent(ImmutableSet coverageReport) { + this.coverageReport = coverageReport; + } + + @Override + public BuildEventStreamProtos.BuildEvent asStreamProto(BuildEventContext context) + throws InterruptedException { + ImmutableList.Builder reportFiles = ImmutableList.builder(); + for (Artifact artifact : coverageReport) { + String uri = context.pathConverter().apply(artifact.getPath()); + if (uri != null) { + reportFiles.add(File.newBuilder().setUri(uri).build()); + } else { + logger.atInfo().log("Dropped unfinished upload: %s", artifact.getPath()); + } + } + return GenericBuildEvent.protoChaining(this) + .setCoverageReport(CoverageReport.newBuilder().addAllCoverageReport(reportFiles.build())) + .build(); + } + + @Override + public BuildEventId getEventId() { + return BuildEventIdUtil.coverageReport(); + } + + @Override + public Collection getChildrenEvents() { + return ImmutableList.of(); + } + + @Override + public Collection referencedLocalFiles() { + return coverageReport.stream() + .map( + artifact -> + new LocalFile( + artifact.getPath(), + LocalFile.LocalFileType.COVERAGE_OUTPUT, + artifact, + /* artifactMetadata= */ null)) + .collect(toImmutableList()); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventIdUtil.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventIdUtil.java index 8f0c66079256a1..66746bcd81e474 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventIdUtil.java +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventIdUtil.java @@ -168,6 +168,12 @@ public static BuildEventId coverageActionsFinished() { .build(); } + public static BuildEventId coverageReport() { + return BuildEventId.newBuilder() + .setCoverageReport(BuildEventId.CoverageReportId.getDefaultInstance()) + .build(); + } + public static BuildEventId aspectConfigured(Label label, String aspect) { BuildEventId.TargetConfiguredId configuredId = BuildEventId.TargetConfiguredId.newBuilder() diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto index d72c9808dbcc02..e0f6501c6da4d8 100644 --- a/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto +++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/proto/build_event_stream.proto @@ -254,6 +254,9 @@ message BuildEventId { // Identifier of an event providing the ExecRequest of a run command. message ExecRequestId {} + // Identifier of an event providing the coverage report. + message CoverageReportId {} + oneof id { UnknownBuildEventId unknown = 1; ProgressId progress = 2; @@ -284,6 +287,7 @@ message BuildEventId { ConvenienceSymlinksIdentifiedId convenience_symlinks_identified = 25; CoverageActionsFinishedId coverage_actions_finished = 27; ExecRequestId exec_request = 28; + CoverageReportId coverage_report = 30; } } @@ -1354,6 +1358,11 @@ message EnvironmentVariable { bytes value = 2; } +// Event that contains the combined coverage report files. +message CoverageReport { + repeated File coverage_report = 1; +} + // Message describing a build event. Events will have an identifier that // is unique within a given build invocation; they also announce follow-up // events as children. More details, which are specific to the kind of event @@ -1390,5 +1399,6 @@ message BuildEvent { BuildMetadata build_metadata = 26; ConvenienceSymlinksIdentified convenience_symlinks_identified = 27; ExecRequestConstructed exec_request = 29; + CoverageReport coverage_report = 31; } } diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java index 81488208efa1c4..2e4a67e2882376 100644 --- a/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java +++ b/src/main/java/com/google/devtools/build/lib/buildtool/SkyframeBuilder.java @@ -35,6 +35,7 @@ import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.TopLevelArtifactContext; import com.google.devtools.build.lib.analysis.test.CoverageActionFinishedEvent; +import com.google.devtools.build.lib.analysis.test.CoverageReportEvent; import com.google.devtools.build.lib.analysis.test.TestProvider; import com.google.devtools.build.lib.bugreport.BugReporter; import com.google.devtools.build.lib.buildtool.buildevent.ExecutionProgressReceiverAvailableEvent; @@ -242,6 +243,8 @@ public void buildArtifacts( bugReporter); if (detailedExitCode != null) { detailedExitCodes.add(detailedExitCode); + } else { + skyframeExecutor.getEventBus().post(new CoverageReportEvent(coverageReportArtifacts)); } } } finally { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD index dc2241ac32c3f7..705c3f29ca185a 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD +++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD @@ -271,6 +271,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/analysis:test/analysis_failure_propagation_exception", "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_action_finished_event", "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_artifacts_known_event", + "//src/main/java/com/google/devtools/build/lib/analysis:test/coverage_report_event", "//src/main/java/com/google/devtools/build/lib/analysis:toolchain_collection", "//src/main/java/com/google/devtools/build/lib/analysis:toolchain_context", "//src/main/java/com/google/devtools/build/lib/analysis:top_level_artifact_context", diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java index a58434c8bbaac7..86db1ce6697664 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeBuildView.java @@ -75,6 +75,7 @@ import com.google.devtools.build.lib.analysis.test.AnalysisFailurePropagationException; import com.google.devtools.build.lib.analysis.test.CoverageActionFinishedEvent; import com.google.devtools.build.lib.analysis.test.CoverageArtifactsKnownEvent; +import com.google.devtools.build.lib.analysis.test.CoverageReportEvent; import com.google.devtools.build.lib.bugreport.BugReport; import com.google.devtools.build.lib.bugreport.BugReporter; import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildMetrics.BuildGraphMetrics; @@ -760,13 +761,14 @@ public SkyframeAnalysisResult analyzeAndExecuteTargets( // combined report that matters. // When --nokeep_going and there's an earlier error, we should skip this and fail fast. if ((!mainEvaluationResult.hasError() && !hasExclusiveTestsError) || keepGoing) { - ImmutableSet coverageArtifacts = + var coverageArtifacts = coverageReportActionsWrapperSupplier.getCoverageArtifacts( buildResultListener.getAnalyzedTargets(), buildResultListener.getAnalyzedTests()); - eventBus.post(CoverageArtifactsKnownEvent.create(coverageArtifacts)); + eventBus.post( + CoverageArtifactsKnownEvent.create(coverageArtifacts.allCoverageArtifacts())); additionalArtifactsResult = skyframeExecutor.evaluateSkyKeys( - eventHandler, Artifact.keys(coverageArtifacts), keepGoing); + eventHandler, Artifact.keys(coverageArtifacts.allCoverageArtifacts()), keepGoing); eventBus.post(new CoverageActionFinishedEvent()); if (additionalArtifactsResult.hasError()) { detailedExitCodes.add( @@ -780,6 +782,8 @@ public SkyframeAnalysisResult analyzeAndExecuteTargets( bugReporter, /* includeExecutionPhase= */ true) .executionDetailedExitCode()); + } else { + eventBus.post(new CoverageReportEvent(coverageArtifacts.coverageReportArtifacts())); } } } finally { @@ -1481,7 +1485,11 @@ public void reset() { /** Provides the list of coverage artifacts to be built. */ @FunctionalInterface public interface CoverageReportActionsWrapperSupplier { - ImmutableSet getCoverageArtifacts( + record CoverageArtifacts( + ImmutableSet allCoverageArtifacts, + ImmutableSet coverageReportArtifacts) {} + + CoverageArtifacts getCoverageArtifacts( Set configuredTargets, Set allTargetsToTest) throws InterruptedException; }