Refactor root cause reporting in ConfiguredTargetFunction

We now track Causes instead of plain Labels, which will allow us to do better reporting in the future. Add basic tests.

PiperOrigin-RevId: 198380468
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index 29ffee6..1e39c76 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -38,10 +38,13 @@
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget;
 import com.google.devtools.build.lib.analysis.configuredtargets.MergedConfiguredTarget.DuplicateException;
+import com.google.devtools.build.lib.buildeventstream.BuildEventStreamProtos.BuildEventId.ConfigurationId;
+import com.google.devtools.build.lib.causes.AnalysisFailedCause;
+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;
-import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.StoredEventHandler;
 import com.google.devtools.build.lib.packages.Aspect;
@@ -149,7 +152,8 @@
       throw new AspectCreationException(
           String.format(
               "%s from %s is not a skylark-defined aspect",
-              skylarkValueName, extensionLabel.toString()));
+              skylarkValueName, extensionLabel.toString()),
+          extensionLabel);
     } else {
       return (SkylarkDefinedAspect) skylarkAspect;
     }
@@ -202,7 +206,7 @@
         | ConversionException
         | InconsistentFilesystemException e) {
       env.getListener().handle(Event.error(e.getMessage()));
-      throw new AspectCreationException(e.getMessage());
+      throw new AspectCreationException(e.getMessage(), extensionLabel);
     }
   }
 
@@ -211,7 +215,7 @@
   public SkyValue compute(SkyKey skyKey, Environment env)
       throws AspectFunctionException, InterruptedException {
     SkyframeBuildView view = buildViewProvider.getSkyframeBuildView();
-    NestedSetBuilder<Label> transitiveRootCauses = NestedSetBuilder.stableOrder();
+    NestedSetBuilder<Cause> transitiveRootCauses = NestedSetBuilder.stableOrder();
     AspectKey key = (AspectKey) skyKey.argument();
     ConfiguredAspectFactory aspectFactory;
     Aspect aspect;
@@ -274,7 +278,8 @@
       baseConfiguredTargetValue =
           (ConfiguredTargetValue) baseAndAspectValues.get(key.getBaseConfiguredTargetKey()).get();
     } catch (ConfiguredValueCreationException e) {
-      throw new AspectFunctionException(new AspectCreationException(e.getRootCauses()));
+      throw new AspectFunctionException(
+          new AspectCreationException(e.getMessage(), e.getRootCauses()));
     }
 
     if (aspectHasConfiguration) {
@@ -364,7 +369,8 @@
                     associatedConfiguredTargetAndData.getTarget().getLocation(), e.getMessage()));
 
         throw new AspectFunctionException(
-            new AspectCreationException(e.getMessage(), associatedTarget.getLabel()));
+            new AspectCreationException(
+                e.getMessage(), associatedTarget.getLabel(), aspectConfiguration));
       }
     }
     associatedConfiguredTargetAndData =
@@ -416,7 +422,8 @@
                 key.getAspectConfigurationKey());
       } catch (ToolchainContextException e) {
         // TODO(katre): better error handling
-        throw new AspectCreationException(e.getMessage());
+        throw new AspectCreationException(
+            e.getMessage(), new LabelCause(key.getLabel(), e.getMessage()));
       }
       if (env.valuesMissing()) {
         return null;
@@ -438,7 +445,7 @@
                 transitiveRootCauses,
                 defaultBuildOptions);
       } catch (ConfiguredTargetFunctionException e) {
-        throw new AspectCreationException(e.getMessage());
+        throw new AspectCreationException(e.getMessage(), key.getLabel(), aspectConfiguration);
       }
       if (depValueMap == null) {
         return null;
@@ -463,17 +470,18 @@
     } catch (DependencyEvaluationException e) {
       if (e.getCause() instanceof ConfiguredValueCreationException) {
         ConfiguredValueCreationException cause = (ConfiguredValueCreationException) e.getCause();
-        throw new AspectFunctionException(new AspectCreationException(
-            cause.getMessage(), cause.getAnalysisRootCause()));
+        throw new AspectFunctionException(
+            new AspectCreationException(cause.getMessage(), cause.getRootCauses()));
       } else if (e.getCause() instanceof InconsistentAspectOrderException) {
         InconsistentAspectOrderException cause = (InconsistentAspectOrderException) e.getCause();
-        throw new AspectFunctionException(new AspectCreationException(
-            cause.getMessage()));
+        throw new AspectFunctionException(
+            new AspectCreationException(cause.getMessage(), key.getLabel(), aspectConfiguration));
       } else {
         // Cast to InvalidConfigurationException as a consistency check. If you add any
         // DependencyEvaluationException constructors, you may need to change this code, too.
         InvalidConfigurationException cause = (InvalidConfigurationException) e.getCause();
-        throw new AspectFunctionException(new AspectCreationException(cause.getMessage()));
+        throw new AspectFunctionException(
+            new AspectCreationException(cause.getMessage(), key.getLabel(), aspectConfiguration));
       }
     } catch (AspectCreationException e) {
       throw new AspectFunctionException(e);
@@ -623,11 +631,11 @@
     events.replayOn(env.getListener());
     if (events.hasErrors()) {
       analysisEnvironment.disable(associatedTarget.getTarget());
+      String msg = "Analysis of target '"
+          + associatedTarget.getTarget().getLabel()
+          + "' failed; build aborted";
       throw new AspectFunctionException(
-          new AspectCreationException(
-              "Analysis of target '"
-                  + associatedTarget.getTarget().getLabel()
-                  + "' failed; build aborted"));
+          new AspectCreationException(msg, key.getLabel(), aspectConfiguration));
     }
     Preconditions.checkState(!analysisEnvironment.hasErrors(),
         "Analysis environment hasError() but no errors reported");
@@ -672,41 +680,36 @@
    * An exception indicating that there was a problem creating an aspect.
    */
   public static final class AspectCreationException extends Exception {
-    /** Targets in the transitive closure that failed to load. May be empty. */
-    private final NestedSet<Label> loadingRootCauses;
+    private static ConfigurationId toId(BuildConfiguration config) {
+      return config == null ? null : config.getEventId().asStreamProto().getConfiguration();
+    }
 
-    /**
-     * The target for which analysis failed, if any. We can't represent aspects with labels, so if
-     * the aspect analysis fails, this will be {@code null}.
-     */
-    @Nullable private final Label analysisRootCause;
+    private final NestedSet<Cause> causes;
 
-    public AspectCreationException(String message, Label analysisRootCause) {
+    public AspectCreationException(String message, NestedSet<Cause> causes) {
       super(message);
-      this.loadingRootCauses = NestedSetBuilder.<Label>emptySet(Order.STABLE_ORDER);
-      this.analysisRootCause = analysisRootCause;
+      this.causes = causes;
     }
 
-    public AspectCreationException(String message, NestedSet<Label> loadingRootCauses) {
-      super(message);
-      this.loadingRootCauses = loadingRootCauses;
-      this.analysisRootCause = null;
+    public AspectCreationException(
+        String message, Label currentTarget, @Nullable BuildConfiguration configuration) {
+      this(
+          message,
+          NestedSetBuilder.<Cause>stableOrder()
+              .add(new AnalysisFailedCause(currentTarget, toId(configuration), message))
+              .build());
     }
 
-    public AspectCreationException(NestedSet<Label> loadingRootCauses) {
-      this("Loading failed", loadingRootCauses);
+    public AspectCreationException(String message, Label currentTarget) {
+      this(message, currentTarget, null);
     }
 
-    public AspectCreationException(String message) {
-      this(message, (Label) null);
+    public AspectCreationException(String message, Cause cause) {
+      this(message, NestedSetBuilder.<Cause>stableOrder().add(cause).build());
     }
 
-    public NestedSet<Label> getRootCauses() {
-      return loadingRootCauses;
-    }
-
-    @Nullable public Label getAnalysisRootCause() {
-      return analysisRootCause;
+    public NestedSet<Cause> getCauses() {
+      return causes;
     }
   }