Add a new concept of failure causes

Not all possible reasons for failure are uniquely identified
by a label. Therefore, add a new data type describing possible
root causes of failures and use it.

The new type is added in causes/*.java and coresponds to Haskell's
one-line definition

  data Cause = LabelCause Label | ActionCause Path Label deriving Show

With future clean up of other failure causes inadequately described
by a label, we expect that type to be extended by new constructors
(i.e., new classes implementing Cause).

--
Change-Id: I6fec74c78cec6abb9c10e32743b05a792888fead
Reviewed-on: https://bazel-review.googlesource.com/#/c/6617
MOS_MIGRATED_REVID=137156390
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 111786e..0478519 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -22,6 +22,7 @@
         "//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader:srcs",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/proto:srcs",
         "//src/main/java/com/google/devtools/build/lib/buildeventstream/transports:srcs",
+        "//src/main/java/com/google/devtools/build/lib/causes:srcs",
         "//src/main/java/com/google/devtools/build/lib/cmdline:srcs",
         "//src/main/java/com/google/devtools/build/lib/query2:srcs",
         "//src/main/java/com/google/devtools/build/lib/remote:srcs",
@@ -520,6 +521,7 @@
         ":util",
         ":vfs",
         "//src/main/java/com/google/devtools/build/lib/actions",
+        "//src/main/java/com/google/devtools/build/lib/causes",
         "//src/main/java/com/google/devtools/build/lib/cmdline",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/common/options",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
index 88efb97..315163b 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionExecutionException.java
@@ -13,14 +13,14 @@
 // limitations under the License.
 package com.google.devtools.build.lib.actions;
 
-import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.causes.ActionFailed;
+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;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.util.ExitCode;
-
 import javax.annotation.Nullable;
 
 /**
@@ -31,7 +31,7 @@
 public class ActionExecutionException extends Exception {
 
   private final Action action;
-  private final NestedSet<Label> rootCauses;
+  private final NestedSet<Cause> rootCauses;
   private final boolean catastrophe;
   @Nullable private final ExitCode exitCode;
 
@@ -79,8 +79,8 @@
     this.exitCode = exitCode;
   }
 
-  public ActionExecutionException(String message, Action action,
-      NestedSet<Label> rootCauses, boolean catastrophe) {
+  public ActionExecutionException(
+      String message, Action action, NestedSet<Cause> rootCauses, boolean catastrophe) {
     super(message);
     this.action = action;
     this.rootCauses = rootCauses;
@@ -88,8 +88,12 @@
     this.exitCode = null;
   }
 
-  public ActionExecutionException(String message, Throwable cause, Action action,
-      NestedSet<Label> rootCauses, boolean catastrophe) {
+  public ActionExecutionException(
+      String message,
+      Throwable cause,
+      Action action,
+      NestedSet<Cause> rootCauses,
+      boolean catastrophe) {
     super(message, cause);
     this.action = action;
     this.rootCauses = rootCauses;
@@ -97,8 +101,13 @@
     this.exitCode = null;
   }
 
-  public ActionExecutionException(String message, Throwable cause, Action action,
-      NestedSet<Label> rootCauses, boolean catastrophe, ExitCode exitCode) {
+  public ActionExecutionException(
+      String message,
+      Throwable cause,
+      Action action,
+      NestedSet<Cause> rootCauses,
+      boolean catastrophe,
+      ExitCode exitCode) {
     super(message, cause);
     this.action = action;
     this.rootCauses = rootCauses;
@@ -106,10 +115,12 @@
     this.exitCode = exitCode;
   }
 
-  static NestedSet<Label> rootCausesFromAction(Action action) {
+  static NestedSet<Cause> rootCausesFromAction(Action action) {
     return action == null || action.getOwner() == null || action.getOwner().getLabel() == null
-        ? NestedSetBuilder.<Label>emptySet(Order.STABLE_ORDER)
-        : NestedSetBuilder.create(Order.STABLE_ORDER, action.getOwner().getLabel());
+        ? NestedSetBuilder.<Cause>emptySet(Order.STABLE_ORDER)
+        : NestedSetBuilder.<Cause>create(
+            Order.STABLE_ORDER,
+            new ActionFailed(action.getPrimaryOutput().getPath(), action.getOwner().getLabel()));
   }
 
   /**
@@ -120,10 +131,10 @@
   }
 
   /**
-   * Return the root causes that should be reported. Usually the owner of the action, but it can
-   * be the label of a missing artifact.
+   * Return the root causes that should be reported. Usually the owner of the action, but it can be
+   * the label of a missing artifact.
    */
-  public NestedSet<Label> getRootCauses() {
+  public NestedSet<Cause> getRootCauses() {
     return rootCauses;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BUILD b/src/main/java/com/google/devtools/build/lib/actions/BUILD
index 931c266..92e9a6a 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/actions/BUILD
@@ -21,6 +21,7 @@
         "//src/main/java/com/google/devtools/build/lib:unix",
         "//src/main/java/com/google/devtools/build/lib:util",
         "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/causes",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/common/options",
         "//src/main/protobuf:extra_actions_base_java_proto",
diff --git a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
index e7d8ff0..0ac8530 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/BuildFailedException.java
@@ -15,10 +15,9 @@
 package com.google.devtools.build.lib.actions;
 
 import com.google.common.collect.ImmutableList;
-import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.causes.Cause;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.util.ExitCode;
-
 import javax.annotation.Nullable;
 
 /**
@@ -42,7 +41,7 @@
 public class BuildFailedException extends Exception {
   private final boolean catastrophic;
   private final Action action;
-  private final Iterable<Label> rootCauses;
+  private final Iterable<Cause> rootCauses;
   private final boolean errorAlreadyShown;
   @Nullable private final ExitCode exitCode;
 
@@ -51,29 +50,37 @@
   }
 
   public BuildFailedException(String message) {
-    this(message, false, null, ImmutableList.<Label>of());
+    this(message, false, null, ImmutableList.<Cause>of());
   }
 
   public BuildFailedException(String message, ExitCode exitCode) {
-    this(message, false, null, ImmutableList.<Label>of(), false, exitCode);
+    this(message, false, null, ImmutableList.<Cause>of(), false, exitCode);
   }
 
   public BuildFailedException(String message, boolean catastrophic) {
-    this(message, catastrophic, null, ImmutableList.<Label>of());
+    this(message, catastrophic, null, ImmutableList.<Cause>of());
   }
 
-  public BuildFailedException(String message, boolean catastrophic,
-      Action action, Iterable<Label> rootCauses) {
+  public BuildFailedException(
+      String message, boolean catastrophic, Action action, Iterable<Cause> rootCauses) {
     this(message, catastrophic, action, rootCauses, false);
   }
 
-  public BuildFailedException(String message, boolean catastrophic,
-      Action action, Iterable<Label> rootCauses, boolean errorAlreadyShown) {
+  public BuildFailedException(
+      String message,
+      boolean catastrophic,
+      Action action,
+      Iterable<Cause> rootCauses,
+      boolean errorAlreadyShown) {
     this(message, catastrophic, action, rootCauses, errorAlreadyShown, null);
   }
 
-  public BuildFailedException(String message, boolean catastrophic,
-      Action action, Iterable<Label> rootCauses, boolean errorAlreadyShown,
+  public BuildFailedException(
+      String message,
+      boolean catastrophic,
+      Action action,
+      Iterable<Cause> rootCauses,
+      boolean errorAlreadyShown,
       ExitCode exitCode) {
     super(message);
     this.catastrophic = catastrophic;
@@ -91,7 +98,7 @@
     return action;
   }
 
-  public Iterable<Label> getRootCauses() {
+  public Iterable<Cause> getRootCauses() {
     return rootCauses;
   }
 
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 661449e..763a294 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
@@ -14,7 +14,7 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.cmdline.Label;
+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;
 import com.google.devtools.build.lib.collect.nestedset.Order;
@@ -27,12 +27,12 @@
  */
 public class AspectCompleteEvent implements SkyValue {
   private final AspectValue aspectValue;
-  private final NestedSet<Label> rootCauses;
+  private final NestedSet<Cause> rootCauses;
 
-  private AspectCompleteEvent(AspectValue aspectValue, NestedSet<Label> rootCauses) {
+  private AspectCompleteEvent(AspectValue aspectValue, NestedSet<Cause> rootCauses) {
     this.aspectValue = aspectValue;
     this.rootCauses =
-        (rootCauses == null) ? NestedSetBuilder.<Label>emptySet(Order.STABLE_ORDER) : rootCauses;
+        (rootCauses == null) ? NestedSetBuilder.<Cause>emptySet(Order.STABLE_ORDER) : rootCauses;
   }
 
   /**
@@ -45,7 +45,7 @@
   /**
    * Construct a target completion event for a failed target, with the given non-empty root causes.
    */
-  public static AspectCompleteEvent createFailed(AspectValue value, NestedSet<Label> rootCauses) {
+  public static AspectCompleteEvent createFailed(AspectValue value, NestedSet<Cause> rootCauses) {
     Preconditions.checkArgument(!Iterables.isEmpty(rootCauses));
     return new AspectCompleteEvent(value, rootCauses);
   }
@@ -64,10 +64,8 @@
     return !rootCauses.isEmpty();
   }
 
-  /**
-   * Get the root causes of the target. May be empty.
-   */
-  public Iterable<Label> getRootCauses() {
+  /** Get the root causes of the target. May be empty. */
+  public Iterable<Cause> getRootCauses() {
     return rootCauses;
   }
 }
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 81150a8..6fea59f 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
@@ -15,7 +15,7 @@
 package com.google.devtools.build.lib.analysis;
 
 import com.google.common.collect.Iterables;
-import com.google.devtools.build.lib.cmdline.Label;
+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;
 import com.google.devtools.build.lib.collect.nestedset.Order;
@@ -28,13 +28,12 @@
 public final class TargetCompleteEvent implements SkyValue {
 
   private final ConfiguredTarget target;
-  private final NestedSet<Label> rootCauses;
+  private final NestedSet<Cause> rootCauses;
 
-  private TargetCompleteEvent(ConfiguredTarget target, NestedSet<Label> rootCauses) {
+  private TargetCompleteEvent(ConfiguredTarget target, NestedSet<Cause> rootCauses) {
     this.target = target;
-    this.rootCauses = (rootCauses == null)
-        ? NestedSetBuilder.<Label>emptySet(Order.STABLE_ORDER)
-        : rootCauses;
+    this.rootCauses =
+        (rootCauses == null) ? NestedSetBuilder.<Cause>emptySet(Order.STABLE_ORDER) : rootCauses;
   }
 
   /**
@@ -47,7 +46,7 @@
   /**
    * Construct a target completion event for a failed target, with the given non-empty root causes.
    */
-  public static TargetCompleteEvent createFailed(ConfiguredTarget ct, NestedSet<Label> rootCauses) {
+  public static TargetCompleteEvent createFailed(ConfiguredTarget ct, NestedSet<Cause> rootCauses) {
     Preconditions.checkArgument(!Iterables.isEmpty(rootCauses));
     return new TargetCompleteEvent(ct, rootCauses);
   }
@@ -66,10 +65,8 @@
     return !rootCauses.isEmpty();
   }
 
-  /**
-   * Get the root causes of the target. May be empty.
-   */
-  public Iterable<Label> getRootCauses() {
+  /** Get the root causes of the target. May be empty. */
+  public Iterable<Cause> getRootCauses() {
     return rootCauses;
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/causes/ActionFailed.java b/src/main/java/com/google/devtools/build/lib/causes/ActionFailed.java
new file mode 100644
index 0000000..85d47ee
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/causes/ActionFailed.java
@@ -0,0 +1,41 @@
+// Copyright 2016 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.causes;
+
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.vfs.Path;
+
+/**
+ * Class describing a {@link Cause} that is associated with an action. It is uniquely determined by
+ * the path to the primary output. For reference, a Label is attached as well.
+ */
+public class ActionFailed implements Cause {
+  private final Path path;
+  private final Label label;
+
+  public ActionFailed(Path path, Label label) {
+    this.path = path;
+    this.label = label;
+  }
+
+  @Override
+  public String toString() {
+    return path.toString();
+  }
+
+  @Override
+  public Label getLabel() {
+    return label;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/causes/BUILD b/src/main/java/com/google/devtools/build/lib/causes/BUILD
new file mode 100644
index 0000000..699391c
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/causes/BUILD
@@ -0,0 +1,15 @@
+package(default_visibility = ["//src:__subpackages__"])
+
+filegroup(
+    name = "srcs",
+    srcs = glob(["**"]),
+)
+
+java_library(
+    name = "causes",
+    srcs = glob(["*.java"]),
+    deps = [
+        "//src/main/java/com/google/devtools/build/lib:vfs",
+        "//src/main/java/com/google/devtools/build/lib/cmdline",
+    ],
+)
diff --git a/src/main/java/com/google/devtools/build/lib/causes/Cause.java b/src/main/java/com/google/devtools/build/lib/causes/Cause.java
new file mode 100644
index 0000000..4fb491a
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/causes/Cause.java
@@ -0,0 +1,25 @@
+// Copyright 2016 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.causes;
+
+import com.google.devtools.build.lib.cmdline.Label;
+
+/**
+ * Interface for classes identifying root causes for a target to fail to build.
+ */
+public interface Cause {
+
+  /** Return the label associated with the failure. */
+  Label getLabel();
+}
diff --git a/src/main/java/com/google/devtools/build/lib/causes/LabelCause.java b/src/main/java/com/google/devtools/build/lib/causes/LabelCause.java
new file mode 100644
index 0000000..240bbb5
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/causes/LabelCause.java
@@ -0,0 +1,35 @@
+// Copyright 2016 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.causes;
+
+import com.google.devtools.build.lib.cmdline.Label;
+
+/** Class describing a {@link Cause} that can uniquely be described by a {@link Label}. */
+public class LabelCause implements Cause {
+  private final Label label;
+
+  public LabelCause(Label label) {
+    this.label = label;
+  }
+
+  @Override
+  public String toString() {
+    return label.toString();
+  }
+
+  @Override
+  public Label getLabel() {
+    return label;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
index cce4a82..a21caa8 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionExecutionFunction.java
@@ -31,7 +31,8 @@
 import com.google.devtools.build.lib.actions.PackageRootResolutionException;
 import com.google.devtools.build.lib.actions.PackageRootResolver;
 import com.google.devtools.build.lib.actions.Root;
-import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.causes.Cause;
+import com.google.devtools.build.lib.causes.LabelCause;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.events.Event;
@@ -581,7 +582,7 @@
     // evaluator "error bubbling", we may get one last chance at reporting errors even though
     // some deps are still missing.
     boolean populateInputData = !env.valuesMissing();
-    NestedSetBuilder<Label> rootCauses = NestedSetBuilder.stableOrder();
+    NestedSetBuilder<Cause> rootCauses = NestedSetBuilder.stableOrder();
     Map<Artifact, FileArtifactValue> inputArtifactData =
         new HashMap<>(populateInputData ? inputDeps.size() : 0);
     Map<Artifact, Collection<Artifact>> expandedArtifacts =
@@ -622,7 +623,7 @@
       } catch (MissingInputFileException e) {
         missingCount++;
         if (input.getOwner() != null) {
-          rootCauses.add(input.getOwner());
+          rootCauses.add(new LabelCause(input.getOwner()));
         }
       } catch (ActionExecutionException e) {
         actionFailures++;
@@ -646,9 +647,14 @@
     }
 
     if (missingCount > 0) {
-      for (Label missingInput : rootCauses.build()) {
-        env.getListener().handle(Event.error(action.getOwner().getLocation(), String.format(
-            "%s: missing input file '%s'", action.getOwner().getLabel(), missingInput)));
+      for (Cause missingInput : rootCauses.build()) {
+        env.getListener()
+            .handle(
+                Event.error(
+                    action.getOwner().getLocation(),
+                    String.format(
+                        "%s: missing input file '%s'",
+                        action.getOwner().getLabel(), missingInput.getLabel())));
       }
       throw new ActionExecutionException(missingCount + " input file(s) do not exist", action,
           rootCauses.build(), /*catastrophe=*/false);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
index 6a3e6e4..03fdeb2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/CompletionFunction.java
@@ -24,6 +24,8 @@
 import com.google.devtools.build.lib.analysis.TopLevelArtifactContext;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactHelper.ArtifactsToBuild;
+import com.google.devtools.build.lib.causes.Cause;
+import com.google.devtools.build.lib.causes.LabelCause;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -71,10 +73,8 @@
      */
     ArtifactsToBuild getAllArtifactsToBuild(TValue value, TopLevelArtifactContext context);
 
-    /**
-     * Creates an event reporting an absent input artifact.
-     */
-    Event getRootCauseError(TValue value, Label rootCause);
+    /** Creates an event reporting an absent input artifact. */
+    Event getRootCauseError(TValue value, Cause rootCause);
 
     /**
      * Creates an error message reporting {@code missingCount} missing input files.
@@ -86,10 +86,8 @@
      */
     TResult createResult(TValue value);
 
-    /**
-     * Creates a failed completion value.
-     */
-    SkyValue createFailed(TValue value, NestedSet<Label> rootCauses);
+    /** Creates a failed completion value. */
+    SkyValue createFailed(TValue value, NestedSet<Cause> rootCauses);
 
     /**
      * Extracts a tag given the {@link SkyKey}.
@@ -122,7 +120,7 @@
     }
 
     @Override
-    public Event getRootCauseError(ConfiguredTargetValue ctValue, Label rootCause) {
+    public Event getRootCauseError(ConfiguredTargetValue ctValue, Cause rootCause) {
       return Event.error(
           ctValue.getConfiguredTarget().getTarget().getLocation(),
           String.format(
@@ -146,7 +144,7 @@
     }
 
     @Override
-    public SkyValue createFailed(ConfiguredTargetValue value, NestedSet<Label> rootCauses) {
+    public SkyValue createFailed(ConfiguredTargetValue value, NestedSet<Cause> rootCauses) {
       return TargetCompleteEvent.createFailed(value.getConfiguredTarget(), rootCauses);
     }
 
@@ -179,7 +177,7 @@
     }
 
     @Override
-    public Event getRootCauseError(AspectValue value, Label rootCause) {
+    public Event getRootCauseError(AspectValue value, Cause rootCause) {
       return Event.error(
           value.getLocation(),
           String.format(
@@ -206,7 +204,7 @@
     }
 
     @Override
-    public SkyValue createFailed(AspectValue value, NestedSet<Label> rootCauses) {
+    public SkyValue createFailed(AspectValue value, NestedSet<Cause> rootCauses) {
       return AspectCompleteEvent.createFailed(value, rootCauses);
     }
 
@@ -253,7 +251,7 @@
     int missingCount = 0;
     ActionExecutionException firstActionExecutionException = null;
     MissingInputFileException missingInputException = null;
-    NestedSetBuilder<Label> rootCausesBuilder = NestedSetBuilder.stableOrder();
+    NestedSetBuilder<Cause> rootCausesBuilder = NestedSetBuilder.stableOrder();
     for (Map.Entry<SkyKey, ValueOrException2<MissingInputFileException, ActionExecutionException>>
         depsEntry : inputDeps.entrySet()) {
       Artifact input = ArtifactSkyKey.artifact(depsEntry.getKey());
@@ -263,8 +261,9 @@
         missingCount++;
         final Label inputOwner = input.getOwner();
         if (inputOwner != null) {
-          rootCausesBuilder.add(inputOwner);
-          env.getListener().handle(completor.getRootCauseError(value, inputOwner));
+          Cause cause = new LabelCause(inputOwner);
+          rootCausesBuilder.add(cause);
+          env.getListener().handle(completor.getRootCauseError(value, cause));
         }
       } catch (ActionExecutionException e) {
         rootCausesBuilder.addTransitive(e.getRootCauses());
@@ -278,7 +277,7 @@
       missingInputException = completor.getMissingFilesException(value, missingCount);
     }
 
-    NestedSet<Label> rootCauses = rootCausesBuilder.build();
+    NestedSet<Cause> rootCauses = rootCausesBuilder.build();
     if (!rootCauses.isEmpty()) {
       eventBusRef.get().post(completor.createFailed(value, rootCauses));
       if (firstActionExecutionException != null) {