Declare order constraint in BEP

In the build event protocol, provide order guarantee to facilitate use:
All the root causes referenced by a TargetCompleteEvent need to come
before that event. Note that, by causality, the root causes have to
be already knows, hence the root cause event is either generated already
or about to be generated.

--
Change-Id: I2bbe9b1a832c23217104a41e21d2f6ff13211c9a
Reviewed-on: https://cr.bazel.build/7371
MOS_MIGRATED_REVID=140018440
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
index 453127c..e2ebbb8 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TargetCompleteEvent.java
@@ -16,9 +16,9 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.buildeventstream.BuildEvent;
 import com.google.devtools.build.lib.buildeventstream.BuildEventId;
 import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos;
+import com.google.devtools.build.lib.buildeventstream.BuildEventWithOrderConstraint;
 import com.google.devtools.build.lib.buildeventstream.GenericBuildEvent;
 import com.google.devtools.build.lib.causes.Cause;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -30,15 +30,22 @@
 import java.util.Collection;
 
 /** This event is fired as soon as a target is either built or fails. */
-public final class TargetCompleteEvent implements SkyValue, BuildEvent {
+public final class TargetCompleteEvent implements SkyValue, BuildEventWithOrderConstraint {
 
   private final ConfiguredTarget target;
   private final NestedSet<Cause> rootCauses;
+  private final Collection<BuildEventId> postedAfter;
 
   private TargetCompleteEvent(ConfiguredTarget target, NestedSet<Cause> rootCauses) {
     this.target = target;
     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();
   }
 
   /**
@@ -98,4 +105,9 @@
         BuildEventStreamProtos.TargetComplete.newBuilder().setSuccess(!failed()).build();
     return GenericBuildEvent.protoChaining(this).setCompleted(complete).build();
   }
+
+  @Override
+  public Collection<BuildEventId> postedAfter() {
+    return postedAfter;
+  }
 }
diff --git a/src/test/shell/integration/build_event_stream_test.sh b/src/test/shell/integration/build_event_stream_test.sh
index 7a420c7..9d16b69 100755
--- a/src/test/shell/integration/build_event_stream_test.sh
+++ b/src/test/shell/integration/build_event_stream_test.sh
@@ -41,6 +41,11 @@
   name = "suite",
   tests = ["true"],
 )
+genrule(
+  name = "fails_to_build",
+  outs = ["fails_to_build.txt"],
+  cmd = "false",
+)
 EOF
 }
 
@@ -87,4 +92,19 @@
   [ -f ${outdir}/test_multiple_transports.bin ] || fail "Missing expected file test_multiple_transports.bin"
 }
 
+function test_root_cause_early() {
+  (bazel build --experimental_build_event_text_file=$TEST_log \
+         pkg:fails_to_build && fail "bazel test failed") || true
+  # We expect precisely one action being reported (the failed one) and
+  # precisely on report on a completed target; moreover, the action has
+  # to be reported first.
+  expect_log_once '^action'
+  expect_log_once '^completed'
+  expect_not_log 'success'
+  local naction=`grep -n '^action' $TEST_log | cut -f 1 -d :`
+  local ncomplete=`grep -n '^completed' $TEST_log | cut -f 1 -d :`
+  [ $naction -lt $ncomplete ] \
+      || fail "failed action not before compelted target"
+}
+
 run_suite "Integration tests for the build event stream"