Make AspectCompleteEvent an instance of BuildEvent
In this way, we can also report the artifacts generated
by aspects in the build event protocol.
For the time being, those events are reported unsolicitly
(i.e., as children of progress events), but they may be linked
to pattern expansion later.
--
Change-Id: I7fb83088d7fdb5424f77bfb78700e8371231b13c
Reviewed-on: https://cr.bazel.build/9370
PiperOrigin-RevId: 150181433
MOS_MIGRATED_REVID=150181433
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
index 763a294..0bdbd9e 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AspectCompleteEvent.java
@@ -13,7 +13,18 @@
// limitations under the License.
package com.google.devtools.build.lib.analysis;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsInOutputGroup;
+import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsToBuild;
+import com.google.devtools.build.lib.buildeventstream.BuildEventId;
+import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
+import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.File;
+import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.OutputGroup;
+import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint;
+import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
+import com.google.devtools.build.lib.buildeventstream.PathConverter;
import com.google.devtools.build.lib.causes.Cause;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -21,25 +32,32 @@
import com.google.devtools.build.lib.skyframe.AspectValue;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.skyframe.SkyValue;
+import java.util.Collection;
-/**
- * This event is fired as soon as a top-level aspect is either built or fails.
- */
-public class AspectCompleteEvent implements SkyValue {
+/** This event is fired as soon as a top-level aspect is either built or fails. */
+public class AspectCompleteEvent implements SkyValue, BuildEventWithOrderConstraint {
private final AspectValue aspectValue;
private final NestedSet<Cause> rootCauses;
+ private final Collection<BuildEventId> postedAfter;
+ private final ArtifactsToBuild artifacts;
- private AspectCompleteEvent(AspectValue aspectValue, NestedSet<Cause> rootCauses) {
+ private AspectCompleteEvent(
+ AspectValue aspectValue, NestedSet<Cause> rootCauses, ArtifactsToBuild artifacts) {
this.aspectValue = aspectValue;
this.rootCauses =
(rootCauses == null) ? NestedSetBuilder.<Cause>emptySet(Order.STABLE_ORDER) : rootCauses;
+ ImmutableList.Builder postedAfterBuilder = ImmutableList.builder();
+ for (Cause cause : getRootCauses()) {
+ postedAfterBuilder.add(BuildEventId.fromCause(cause));
+ }
+ this.postedAfter = postedAfterBuilder.build();
+ this.artifacts = artifacts;
}
- /**
- * Construct a successful target completion event.
- */
- public static AspectCompleteEvent createSuccessful(AspectValue value) {
- return new AspectCompleteEvent(value, null);
+ /** Construct a successful target completion event. */
+ public static AspectCompleteEvent createSuccessful(
+ AspectValue value, ArtifactsToBuild artifacts) {
+ return new AspectCompleteEvent(value, null, artifacts);
}
/**
@@ -47,7 +65,7 @@
*/
public static AspectCompleteEvent createFailed(AspectValue value, NestedSet<Cause> rootCauses) {
Preconditions.checkArgument(!Iterables.isEmpty(rootCauses));
- return new AspectCompleteEvent(value, rootCauses);
+ return new AspectCompleteEvent(value, rootCauses, null);
}
/**
@@ -68,4 +86,42 @@
public Iterable<Cause> getRootCauses() {
return rootCauses;
}
+
+ @Override
+ public BuildEventId getEventId() {
+ return BuildEventId.aspectCompleted(
+ aspectValue.getLabel(), aspectValue.getAspect().getDescriptor().getDescription());
+ }
+
+ @Override
+ public Collection<BuildEventId> postedAfter() {
+ return postedAfter;
+ }
+
+ @Override
+ public Collection<BuildEventId> getChildrenEvents() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public BuildEventStreamProtos.BuildEvent asStreamProto(PathConverter pathConverter) {
+ BuildEventStreamProtos.TargetComplete.Builder builder =
+ BuildEventStreamProtos.TargetComplete.newBuilder();
+ builder.setSuccess(!failed());
+ if (artifacts != null) {
+ for (ArtifactsInOutputGroup artifactsInGroup : artifacts.getAllArtifactsByOutputGroup()) {
+ OutputGroup.Builder groupBuilder = OutputGroup.newBuilder();
+ groupBuilder.setName(artifactsInGroup.getOutputGroup());
+
+ File.Builder fileBuilder = File.newBuilder();
+ for (Artifact artifact : artifactsInGroup.getArtifacts()) {
+ String name = artifact.getFilename();
+ String uri = pathConverter.apply(artifact.getPath());
+ groupBuilder.addOutputFile(fileBuilder.setName(name).setUri(uri).build());
+ }
+ builder.addOutputGroup(groupBuilder.build());
+ }
+ }
+ return GenericBuildEvent.protoChaining(this).setCompleted(builder.build()).build();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventId.java b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventId.java
index 3a89fa5..81f9eac 100644
--- a/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventId.java
+++ b/src/main/java/com/google/devtools/build/lib/buildeventstream/BuildEventId.java
@@ -117,6 +117,16 @@
BuildEventStreamProtos.BuildEventId.newBuilder().setTargetCompleted(targetId).build());
}
+ public static BuildEventId aspectCompleted(Label target, String aspect) {
+ BuildEventStreamProtos.BuildEventId.TargetCompletedId targetId =
+ BuildEventStreamProtos.BuildEventId.TargetCompletedId.newBuilder()
+ .setLabel(target.toString())
+ .setAspect(aspect)
+ .build();
+ return new BuildEventId(
+ BuildEventStreamProtos.BuildEventId.newBuilder().setTargetCompleted(targetId).build());
+ }
+
public static BuildEventId fromCause(Cause cause) {
return new BuildEventId(cause.getIdProto());
}
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 072a7ef..a4592fa 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
@@ -63,6 +63,10 @@
// does not include running the test if the target is a test target.
message TargetCompletedId {
string label = 1;
+
+ // If not empty, the id refers to the completion of the target for a given
+ // aspect.
+ string aspect = 2;
}
// Identifier of an event reporting that an action was completed (not all
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
index bb6fd08..bd8f8cb 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionProgressReceiver.java
@@ -27,6 +27,7 @@
import com.google.devtools.build.lib.skyframe.ActionExecutionInactivityWatchdog;
import com.google.devtools.build.lib.skyframe.ActionExecutionValue;
import com.google.devtools.build.lib.skyframe.AspectCompletionValue;
+import com.google.devtools.build.lib.skyframe.AspectValue;
import com.google.devtools.build.lib.skyframe.SkyFunctions;
import com.google.devtools.build.lib.skyframe.SkyframeActionExecutor;
import com.google.devtools.build.lib.skyframe.TargetCompletionValue;
@@ -120,7 +121,10 @@
} else if (type.equals(SkyFunctions.ASPECT_COMPLETION)) {
AspectCompletionValue value = (AspectCompletionValue) skyValueSupplier.get();
if (value != null) {
- eventBus.post(AspectCompleteEvent.createSuccessful(value.getAspectValue()));
+ AspectValue aspectValue = value.getAspectValue();
+ ArtifactsToBuild artifacts =
+ TopLevelArtifactHelper.getAllArtifactsToBuild(aspectValue, topLevelArtifactContext);
+ eventBus.post(AspectCompleteEvent.createSuccessful(aspectValue, artifacts));
}
} else if (type.equals(SkyFunctions.ACTION_EXECUTION)) {
// Remember all completed actions, even those in error, regardless of having been cached or
diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh
index e9700ac..5a8a0fe 100755
--- a/src/test/shell/integration/build_event_stream_test.sh
+++ b/src/test/shell/integration/build_event_stream_test.sh
@@ -73,6 +73,19 @@
tags = ["tag1", "tag2"]
)
EOF
+cat > simpleaspect.bzl <<EOF
+def _simple_aspect_impl(target, ctx):
+ for orig_out in ctx.rule.attr.outs:
+ aspect_out = ctx.new_file(orig_out.name + ".aspect")
+ ctx.file_action(
+ output=aspect_out,
+ content = "Hello from aspect")
+ return struct(output_groups={
+ "aspect-out" : set([aspect_out]) })
+
+simple_aspect = aspect(implementation=_simple_aspect_impl)
+EOF
+touch BUILD
}
#### TESTS #############################################################
@@ -223,6 +236,17 @@
expect_log 'tag2'
}
+function test_aspect_artifacts() {
+ bazel build --experimental_build_event_text_file=$TEST_log \
+ --aspects=simpleaspect.bzl%simple_aspect \
+ --output_groups=aspect-out \
+ pkg:output_files_and_tags || fail "bazel build failed"
+ expect_log 'aspect.*simple_aspect'
+ expect_log 'name.*aspect-out'
+ expect_log 'name.*out1.txt.aspect'
+ expect_not_log 'aborted'
+}
+
function test_build_only() {
# When building but not testing a test, there won't be a test summary
# (as nothing was tested), so it should not be announced.