Command line aspect-on-aspect

This CL supports aspect-on-aspect for command line aspects. Command line aspects specified via `--aspects` option will support a top-level aspect requiring aspect providers via `required_aspect_providers` to get their values from other top-level aspects advertising it that come before it in the `--aspects` list.

PiperOrigin-RevId: 381862973
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
index 1e8a0ef..2a8445a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisFailureEvent.java
@@ -30,7 +30,7 @@
 import com.google.devtools.build.lib.causes.Cause;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
-import com.google.devtools.build.lib.skyframe.AspectValueKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
 import java.util.Collection;
 import javax.annotation.Nullable;
@@ -40,7 +40,7 @@
  * target cannot be completed because of an error in one of its dependencies.
  */
 public class AnalysisFailureEvent implements BuildEvent {
-  @Nullable private final AspectValueKey failedAspect;
+  @Nullable private final AspectKey failedAspect;
   private final ConfiguredTargetKey failedTarget;
   private final BuildEventId configuration;
   private final NestedSet<Cause> rootCauses;
@@ -48,12 +48,12 @@
   public AnalysisFailureEvent(
       ActionLookupKey failedTarget, BuildEventId configuration, NestedSet<Cause> rootCauses) {
     Preconditions.checkArgument(
-        failedTarget instanceof ConfiguredTargetKey || failedTarget instanceof AspectValueKey);
+        failedTarget instanceof ConfiguredTargetKey || failedTarget instanceof AspectKey);
     if (failedTarget instanceof ConfiguredTargetKey) {
       this.failedAspect = null;
       this.failedTarget = (ConfiguredTargetKey) failedTarget;
     } else {
-      this.failedAspect = (AspectValueKey) failedTarget;
+      this.failedAspect = (AspectKey) failedTarget;
       this.failedTarget = failedAspect.getBaseConfiguredTargetKey();
     }
     if (configuration != null) {
@@ -65,7 +65,7 @@
   }
 
   public AnalysisFailureEvent(
-      AspectValueKey failedAspect, BuildEventId configuration, NestedSet<Cause> rootCauses) {
+      AspectKey failedAspect, BuildEventId configuration, NestedSet<Cause> rootCauses) {
     this.failedAspect = failedAspect;
     this.failedTarget = failedAspect.getBaseConfiguredTargetKey();
     if (configuration != null) {
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 bec43a1..f835927 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
@@ -55,13 +55,12 @@
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.ExtendedEventHandler;
 import com.google.devtools.build.lib.packages.AspectClass;
-import com.google.devtools.build.lib.packages.AspectDescriptor;
-import com.google.devtools.build.lib.packages.AspectParameters;
 import com.google.devtools.build.lib.packages.Attribute;
 import com.google.devtools.build.lib.packages.NativeAspectClass;
 import com.google.devtools.build.lib.packages.NoSuchPackageException;
 import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.packages.TargetUtils;
 import com.google.devtools.build.lib.pkgcache.PackageManager;
@@ -75,6 +74,7 @@
 import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns.Code;
 import com.google.devtools.build.lib.skyframe.AspectValueKey;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
 import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
 import com.google.devtools.build.lib.skyframe.CoverageReportValue;
@@ -86,7 +86,6 @@
 import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.util.RegexFilter;
 import com.google.devtools.build.skyframe.WalkableGraph;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -275,10 +274,7 @@
             .map(TargetAndConfiguration::getConfiguredTargetKey)
             .collect(Collectors.toList());
 
-    Multimap<Pair<Label, String>, BuildConfiguration> aspectConfigurations =
-        ArrayListMultimap.create();
-
-    List<AspectValueKey> aspectKeys = new ArrayList<>();
+    ImmutableList.Builder<AspectClass> aspectClassesBuilder = ImmutableList.builder();
     for (String aspect : aspects) {
       // Syntax: label%aspect
       int delimiterPosition = aspect.indexOf('%');
@@ -318,38 +314,14 @@
               createFailureDetail(errorMessage, Analysis.Code.ASPECT_LABEL_SYNTAX_ERROR),
               e);
         }
-
         String starlarkFunctionName = aspect.substring(delimiterPosition + 1);
-        for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
-          aspectConfigurations.put(
-              Pair.of(targetSpec.getLabel(), aspect), targetSpec.getConfiguration());
-          aspectKeys.add(
-              AspectValueKey.createStarlarkAspectKey(
-                  targetSpec.getLabel(),
-                  // For invoking top-level aspects, use the top-level configuration for both the
-                  // aspect and the base target while the top-level configuration is untrimmed.
-                  targetSpec.getConfiguration(),
-                  targetSpec.getConfiguration(),
-                  starlarkFileLabel,
-                  starlarkFunctionName));
-        }
+        aspectClassesBuilder.add(new StarlarkAspectClass(starlarkFileLabel, starlarkFunctionName));
       } else {
         final NativeAspectClass aspectFactoryClass =
             ruleClassProvider.getNativeAspectClassMap().get(aspect);
 
         if (aspectFactoryClass != null) {
-          for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
-            // For invoking top-level aspects, use the top-level configuration for both the
-            // aspect and the base target while the top-level configuration is untrimmed.
-            BuildConfiguration configuration = targetSpec.getConfiguration();
-            aspectConfigurations.put(Pair.of(targetSpec.getLabel(), aspect), configuration);
-            aspectKeys.add(
-                AspectValueKey.createAspectKey(
-                    targetSpec.getLabel(),
-                    configuration,
-                    new AspectDescriptor(aspectFactoryClass, AspectParameters.EMPTY),
-                    configuration));
-          }
+          aspectClassesBuilder.add(aspectFactoryClass);
         } else {
           String errorMessage = "Aspect '" + aspect + "' is unknown";
           throw new ViewCreationFailedException(
@@ -358,6 +330,25 @@
       }
     }
 
+    Multimap<Pair<Label, String>, BuildConfiguration> aspectConfigurations =
+        ArrayListMultimap.create();
+    ImmutableList<AspectClass> aspectClasses = aspectClassesBuilder.build();
+    ImmutableList.Builder<TopLevelAspectsKey> aspectsKeys = ImmutableList.builder();
+    for (TargetAndConfiguration targetSpec : topLevelTargetsWithConfigs) {
+      BuildConfiguration configuration = targetSpec.getConfiguration();
+      for (AspectClass aspectClass : aspectClasses) {
+        aspectConfigurations.put(
+            Pair.of(targetSpec.getLabel(), aspectClass.getName()), configuration);
+      }
+      // For invoking top-level aspects, use the top-level configuration for both the
+      // aspect and the base target while the top-level configuration is untrimmed.
+      if (!aspectClasses.isEmpty()) {
+        aspectsKeys.add(
+            AspectValueKey.createTopLevelAspectsKey(
+                aspectClasses, targetSpec.getLabel(), configuration));
+      }
+    }
+
     for (Pair<Label, String> target : aspectConfigurations.keys()) {
       eventBus.post(
           new AspectConfiguredEvent(
@@ -382,7 +373,7 @@
           skyframeBuildView.configureTargets(
               eventHandler,
               topLevelCtKeys,
-              aspectKeys,
+              aspectsKeys.build(),
               Suppliers.memoize(configurationLookupSupplier),
               topLevelOptions,
               eventBus,
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
index 5e1c1ce..d39d604 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildRequestOptions.java
@@ -297,11 +297,18 @@
       effectTags = {OptionEffectTag.UNKNOWN},
       allowMultiple = true,
       help =
-          "Comma-separated list of aspects to be applied to top-level targets. All aspects "
-              + "are applied to all top-level targets independently. Aspects are specified in "
-              + "the form <bzl-file-label>%<aspect_name>, "
-              + "for example '//tools:my_def.bzl%my_aspect', where 'my_aspect' is a top-level "
-              + "value from from a file tools/my_def.bzl")
+          "Comma-separated list of aspects to be applied to top-level targets. All aspects are"
+              + " applied to all top-level targets. If aspect <code>some_aspect</code> specifies"
+              + " required aspect providers via <code>required_aspect_providers</code>,"
+              + " <code>some_aspect</code> will run after every aspect that was mentioned before it"
+              + " in the aspects list and whose advertised providers satisfy"
+              + " <code>some_aspect</code> required aspect providers. <code>some_aspect</code> will"
+              + " then have access to the values of those aspects' providers. Aspects that do not"
+              + " have such dependency will run independently on the top-level targets."
+              + ""
+              + " Aspects are specified in the form <bzl-file-label>%<aspect_name>, for example"
+              + " '//tools:my_def.bzl%my_aspect', where 'my_aspect' is a top-level value from a"
+              + " file tools/my_def.bzl")
   public List<String> aspects;
 
   public BuildRequestOptions() throws OptionsParsingException {}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java b/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
index cb0678d..9bde830 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StarlarkAspectClass.java
@@ -64,4 +64,9 @@
   public final int hashCode() {
     return Objects.hash(getExtensionLabel(), getExportedName());
   }
+
+  @Override
+  public String toString() {
+    return getName();
+  }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
index f6ecb9c..fddca3f 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectValueKey.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
-import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -30,29 +29,15 @@
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import javax.annotation.Nullable;
 
-/** A base class for keys that have AspectValue as a Sky value. */
-public abstract class AspectValueKey implements ActionLookupKey {
+/** A wrapper class for sky keys needed to compute sky values for aspects. */
+public final class AspectValueKey {
+
+  private AspectValueKey() {}
 
   private static final Interner<AspectKey> aspectKeyInterner = BlazeInterners.newWeakInterner();
-  private static final Interner<StarlarkAspectLoadingKey> starlarkAspectKeyInterner =
+  private static final Interner<TopLevelAspectsKey> topLevelAspectsKeyInterner =
       BlazeInterners.newWeakInterner();
 
-  /**
-   * Gets the name of the aspect that would be returned by the corresponding value's {@code
-   * aspectValue.getAspect().getAspectClass().getName()}, if the value could be produced.
-   *
-   * <p>Only needed for reporting errors in BEP when the key's AspectValue fails evaluation.
-   */
-  public abstract String getAspectName();
-
-  public abstract String getDescription();
-
-  @Nullable
-  abstract BuildConfigurationValue.Key getAspectConfigurationKey();
-
-  /** Returns the key for the base configured target for this aspect. */
-  public abstract ConfiguredTargetKey getBaseConfiguredTargetKey();
-
   public static AspectKey createAspectKey(
       Label label,
       @Nullable BuildConfiguration baseConfiguration,
@@ -67,6 +52,15 @@
   }
 
   public static AspectKey createAspectKey(
+      AspectDescriptor aspectDescriptor,
+      ImmutableList<AspectKey> baseKeys,
+      BuildConfigurationValue.Key aspectConfigurationKey,
+      ConfiguredTargetKey baseConfiguredTargetKey) {
+    return AspectKey.createAspectKey(
+        baseConfiguredTargetKey, baseKeys, aspectDescriptor, aspectConfigurationKey);
+  }
+
+  public static AspectKey createAspectKey(
       Label label,
       @Nullable BuildConfiguration baseConfiguration,
       AspectDescriptor aspectDescriptor,
@@ -78,28 +72,24 @@
         aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration));
   }
 
-  public static StarlarkAspectLoadingKey createStarlarkAspectKey(
+  public static TopLevelAspectsKey createTopLevelAspectsKey(
+      ImmutableList<AspectClass> topLevelAspectsClasses,
       Label targetLabel,
-      @Nullable BuildConfiguration aspectConfiguration,
-      @Nullable BuildConfiguration targetConfiguration,
-      Label starlarkFileLabel,
-      String starlarkExportName) {
-    return StarlarkAspectLoadingKey.createInternal(
+      @Nullable BuildConfiguration configuration) {
+    return TopLevelAspectsKey.createInternal(
+        topLevelAspectsClasses,
         targetLabel,
-        aspectConfiguration == null ? null : BuildConfigurationValue.key(aspectConfiguration),
         ConfiguredTargetKey.builder()
             .setLabel(targetLabel)
-            .setConfiguration(targetConfiguration)
-            .build(),
-        starlarkFileLabel,
-        starlarkExportName);
+            .setConfiguration(configuration)
+            .build());
   }
 
   // Specific subtypes of aspect keys.
 
   /** Represents an aspect applied to a particular target. */
   @AutoCodec
-  public static final class AspectKey extends AspectValueKey {
+  public static final class AspectKey implements ActionLookupKey {
     private final ConfiguredTargetKey baseConfiguredTargetKey;
     private final ImmutableList<AspectKey> baseKeys;
     @Nullable private final BuildConfigurationValue.Key aspectConfigurationKey;
@@ -141,7 +131,12 @@
       return SkyFunctions.ASPECT;
     }
 
-    @Override
+    /**
+     * Gets the name of the aspect that would be returned by the corresponding value's {@code
+     * aspectValue.getAspect().getAspectClass().getName()}, if the value could be produced.
+     *
+     * <p>Only needed for reporting errors in BEP when the key's AspectValue fails evaluation.
+     */
     public String getAspectName() {
       return aspectDescriptor.getDescription();
     }
@@ -178,11 +173,9 @@
       return baseKeys;
     }
 
-    @Override
     public String getDescription() {
       if (baseKeys.isEmpty()) {
-        return String.format("%s of %s",
-            aspectDescriptor.getAspectClass().getName(), getLabel());
+        return String.format("%s of %s", aspectDescriptor.getAspectClass().getName(), getLabel());
       } else {
         return String.format(
             "%s on top of %s", aspectDescriptor.getAspectClass().getName(), baseKeys);
@@ -207,13 +200,11 @@
      * base target's configuration.
      */
     @Nullable
-    @Override
     BuildConfigurationValue.Key getAspectConfigurationKey() {
       return aspectConfigurationKey;
     }
 
     /** Returns the key for the base configured target for this aspect. */
-    @Override
     public ConfiguredTargetKey getBaseConfiguredTargetKey() {
       return baseConfiguredTargetKey;
     }
@@ -280,70 +271,46 @@
     }
   }
 
-  /** The key for a Starlark aspect. */
+  /** The key for top level aspects specified by --aspects option on a top level target. */
   @AutoCodec
-  public static final class StarlarkAspectLoadingKey extends AspectValueKey {
+  public static final class TopLevelAspectsKey implements ActionLookupKey {
+    private final ImmutableList<AspectClass> topLevelAspectsClasses;
     private final Label targetLabel;
-    private final BuildConfigurationValue.Key aspectConfigurationKey;
     private final ConfiguredTargetKey baseConfiguredTargetKey;
-    private final Label starlarkFileLabel;
-    private final String starlarkValueName;
     private final int hashCode;
 
     @AutoCodec.Instantiator
     @AutoCodec.VisibleForSerialization
-    static StarlarkAspectLoadingKey createInternal(
+    static TopLevelAspectsKey createInternal(
+        ImmutableList<AspectClass> topLevelAspectsClasses,
         Label targetLabel,
-        BuildConfigurationValue.Key aspectConfigurationKey,
-        ConfiguredTargetKey baseConfiguredTargetKey,
-        Label starlarkFileLabel,
-        String starlarkValueName) {
-      return starlarkAspectKeyInterner.intern(
-          new StarlarkAspectLoadingKey(
+        ConfiguredTargetKey baseConfiguredTargetKey) {
+      return topLevelAspectsKeyInterner.intern(
+          new TopLevelAspectsKey(
+              topLevelAspectsClasses,
               targetLabel,
-              aspectConfigurationKey,
               baseConfiguredTargetKey,
-              starlarkFileLabel,
-              starlarkValueName,
-              Objects.hashCode(
-                  targetLabel,
-                  aspectConfigurationKey,
-                  baseConfiguredTargetKey,
-                  starlarkFileLabel,
-                  starlarkValueName)));
+              Objects.hashCode(topLevelAspectsClasses, targetLabel, baseConfiguredTargetKey)));
     }
 
-    private StarlarkAspectLoadingKey(
+    private TopLevelAspectsKey(
+        ImmutableList<AspectClass> topLevelAspectsClasses,
         Label targetLabel,
-        BuildConfigurationValue.Key aspectConfigurationKey,
         ConfiguredTargetKey baseConfiguredTargetKey,
-        Label starlarkFileLabel,
-        String starlarkValueName,
         int hashCode) {
+      this.topLevelAspectsClasses = topLevelAspectsClasses;
       this.targetLabel = targetLabel;
-      this.aspectConfigurationKey = aspectConfigurationKey;
       this.baseConfiguredTargetKey = baseConfiguredTargetKey;
-      this.starlarkFileLabel = starlarkFileLabel;
-      this.starlarkValueName = starlarkValueName;
       this.hashCode = hashCode;
     }
 
     @Override
     public SkyFunctionName functionName() {
-      return SkyFunctions.LOAD_STARLARK_ASPECT;
+      return SkyFunctions.TOP_LEVEL_ASPECTS;
     }
 
-    String getStarlarkValueName() {
-      return starlarkValueName;
-    }
-
-    Label getStarlarkFileLabel() {
-      return starlarkFileLabel;
-    }
-
-    @Override
-    public String getAspectName() {
-      return String.format("%s%%%s", starlarkFileLabel, starlarkValueName);
+    ImmutableList<AspectClass> getTopLevelAspectsClasses() {
+      return topLevelAspectsClasses;
     }
 
     @Override
@@ -351,24 +318,14 @@
       return targetLabel;
     }
 
-    @Override
-    public String getDescription() {
-      // Starlark aspects are referred to on command line with <file>%<value ame>
-      return String.format("%s%%%s of %s", starlarkFileLabel, starlarkValueName, targetLabel);
-    }
-
-    @Nullable
-    @Override
-    BuildConfigurationValue.Key getAspectConfigurationKey() {
-      return aspectConfigurationKey;
-    }
-
-    /** Returns the key for the base configured target for this aspect. */
-    @Override
-    public ConfiguredTargetKey getBaseConfiguredTargetKey() {
+    ConfiguredTargetKey getBaseConfiguredTargetKey() {
       return baseConfiguredTargetKey;
     }
 
+    String getDescription() {
+      return topLevelAspectsClasses + " on " + getLabel();
+    }
+
     @Override
     public int hashCode() {
       return hashCode;
@@ -379,35 +336,14 @@
       if (o == this) {
         return true;
       }
-      if (!(o instanceof StarlarkAspectLoadingKey)) {
+      if (!(o instanceof TopLevelAspectsKey)) {
         return false;
       }
-      StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o;
+      TopLevelAspectsKey that = (TopLevelAspectsKey) o;
       return hashCode == that.hashCode
           && Objects.equal(targetLabel, that.targetLabel)
-          && Objects.equal(aspectConfigurationKey, that.aspectConfigurationKey)
           && Objects.equal(baseConfiguredTargetKey, that.baseConfiguredTargetKey)
-          && Objects.equal(starlarkFileLabel, that.starlarkFileLabel)
-          && Objects.equal(starlarkValueName, that.starlarkValueName);
-    }
-
-    @Override
-    public String toString() {
-      return MoreObjects.toStringHelper(this)
-          .add("targetLabel", targetLabel)
-          .add("aspectConfigurationKey", aspectConfigurationKey)
-          .add("baseConfiguredTargetKey", baseConfiguredTargetKey)
-          .add("starlarkFileLabel", starlarkFileLabel)
-          .add("starlarkValueName", starlarkValueName)
-          .toString();
-    }
-
-    AspectKey toAspectKey(AspectClass aspectClass) {
-      return AspectKey.createAspectKey(
-          baseConfiguredTargetKey,
-          ImmutableList.of(),
-          new AspectDescriptor(aspectClass, AspectParameters.EMPTY),
-          aspectConfigurationKey);
+          && Objects.equal(topLevelAspectsClasses, that.topLevelAspectsClasses);
     }
   }
 }
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 33006ba..52c40f2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -37,6 +37,7 @@
         "ExternalFilesHelper.java",
         "ExternalPackageFunction.java",
         "FileStateFunction.java",
+        "LoadStarlarkAspectFunction.java",
         "LocalRepositoryLookupFunction.java",
         "NonRuleConfiguredTargetValue.java",
         "PackageFunction.java",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java
new file mode 100644
index 0000000..4d81910
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/LoadStarlarkAspectFunction.java
@@ -0,0 +1,169 @@
+// Copyright 2021 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.skyframe;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Interner;
+import com.google.devtools.build.lib.causes.LabelCause;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
+import com.google.devtools.build.lib.packages.StarlarkAspect;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis;
+import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
+import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
+import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import javax.annotation.Nullable;
+
+/**
+ * SkyFunction to load aspects from Starlark extensions and return StarlarkAspect.
+ *
+ * <p>Used for loading top-level aspects. At top level, in {@link
+ * com.google.devtools.build.lib.analysis.BuildView}, we cannot invoke two SkyFunctions one after
+ * another, so BuildView calls this function to do the work.
+ */
+public class LoadStarlarkAspectFunction implements SkyFunction {
+  private static final Interner<StarlarkAspectLoadingKey> starlarkAspectLoadingKeyInterner =
+      BlazeInterners.newWeakInterner();
+
+  LoadStarlarkAspectFunction() {}
+
+  @Nullable
+  @Override
+  public SkyValue compute(SkyKey skyKey, Environment env)
+      throws LoadStarlarkAspectFunctionException, InterruptedException {
+    StarlarkAspectLoadingKey aspectLoadingKey = (StarlarkAspectLoadingKey) skyKey.argument();
+
+    Label extensionLabel = aspectLoadingKey.getAspectClass().getExtensionLabel();
+    String exportedName = aspectLoadingKey.getAspectClass().getExportedName();
+    StarlarkAspect starlarkAspect;
+    try {
+      starlarkAspect = AspectFunction.loadStarlarkAspect(env, extensionLabel, exportedName);
+      if (starlarkAspect == null) {
+        return null;
+      }
+      if (!starlarkAspect.getParamAttributes().isEmpty()) {
+        String msg =
+            String.format(
+                "Cannot instantiate parameterized aspect %s at the top level.",
+                starlarkAspect.getName());
+        throw new AspectCreationException(
+            msg,
+            new LabelCause(
+                extensionLabel,
+                createDetailedCode(msg, Code.PARAMETERIZED_TOP_LEVEL_ASPECT_INVALID)));
+      }
+    } catch (AspectCreationException e) {
+      throw new LoadStarlarkAspectFunctionException(e);
+    }
+
+    return new StarlarkAspectLoadingValue(starlarkAspect);
+  }
+
+  @Nullable
+  @Override
+  public String extractTag(SkyKey skyKey) {
+    return null;
+  }
+
+  private static DetailedExitCode createDetailedCode(String msg, Code code) {
+    return DetailedExitCode.of(
+        FailureDetail.newBuilder()
+            .setMessage(msg)
+            .setAnalysis(Analysis.newBuilder().setCode(code))
+            .build());
+  }
+
+  /** Exceptions thrown from LoadStarlarkAspectFunction. */
+  public static class LoadStarlarkAspectFunctionException extends SkyFunctionException {
+    public LoadStarlarkAspectFunctionException(AspectCreationException cause) {
+      super(cause, Transience.PERSISTENT);
+    }
+  }
+
+  public static StarlarkAspectLoadingKey createStarlarkAspectLoadingKey(
+      StarlarkAspectClass aspectClass) {
+    return StarlarkAspectLoadingKey.createInternal(aspectClass);
+  }
+
+  /** Skykey for loading Starlark aspect. */
+  @AutoCodec
+  public static final class StarlarkAspectLoadingKey implements SkyKey {
+    private final StarlarkAspectClass aspectClass;
+    private final int hashCode;
+
+    @AutoCodec.Instantiator
+    @AutoCodec.VisibleForSerialization
+    static StarlarkAspectLoadingKey createInternal(StarlarkAspectClass aspectClass) {
+      return starlarkAspectLoadingKeyInterner.intern(
+          new StarlarkAspectLoadingKey(aspectClass, java.util.Objects.hashCode(aspectClass)));
+    }
+
+    private StarlarkAspectLoadingKey(StarlarkAspectClass aspectClass, int hashCode) {
+      this.aspectClass = aspectClass;
+      this.hashCode = hashCode;
+    }
+
+    @Override
+    public SkyFunctionName functionName() {
+      return SkyFunctions.LOAD_STARLARK_ASPECT;
+    }
+
+    StarlarkAspectClass getAspectClass() {
+      return aspectClass;
+    }
+
+    @Override
+    public int hashCode() {
+      return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (!(o instanceof StarlarkAspectLoadingKey)) {
+        return false;
+      }
+      StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o;
+      return hashCode == that.hashCode && Objects.equal(aspectClass, that.aspectClass);
+    }
+
+    @Override
+    public String toString() {
+      return aspectClass.toString();
+    }
+  }
+
+  /** SkyValue for {@code StarlarkAspectLoadingKey} holds the loaded {@code StarlarkAspect}. */
+  public static class StarlarkAspectLoadingValue implements SkyValue {
+    private final StarlarkAspect starlarkAspect;
+
+    public StarlarkAspectLoadingValue(StarlarkAspect starlarkAspect) {
+      this.starlarkAspect = starlarkAspect;
+    }
+
+    public StarlarkAspect getAspect() {
+      return starlarkAspect;
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
index cb07796..46443e4 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java
@@ -88,6 +88,8 @@
   public static final SkyFunctionName ASPECT = SkyFunctionName.createHermetic("ASPECT");
   static final SkyFunctionName LOAD_STARLARK_ASPECT =
       SkyFunctionName.createHermetic("LOAD_STARLARK_ASPECT");
+  static final SkyFunctionName TOP_LEVEL_ASPECTS =
+      SkyFunctionName.createHermetic("TOP_LEVEL_ASPECTS");
   public static final SkyFunctionName TARGET_COMPLETION =
       SkyFunctionName.create(
           "TARGET_COMPLETION", ShareabilityOfValue.NEVER, FunctionHermeticity.HERMETIC);
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 7b4007d..c6d559b 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
@@ -88,7 +88,9 @@
 import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
 import com.google.devtools.build.lib.skyframe.ArtifactConflictFinder.ConflictException;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
 import com.google.devtools.build.lib.skyframe.SkyframeExecutor.TopLevelActionConflictReport;
+import com.google.devtools.build.lib.skyframe.ToplevelStarlarkAspectFunction.TopLevelAspectsValue;
 import com.google.devtools.build.lib.util.DetailedExitCode;
 import com.google.devtools.build.lib.util.OrderedSetMultimap;
 import com.google.devtools.build.lib.util.Pair;
@@ -380,7 +382,7 @@
   public SkyframeAnalysisResult configureTargets(
       ExtendedEventHandler eventHandler,
       List<ConfiguredTargetKey> ctKeys,
-      List<AspectValueKey> aspectKeys,
+      ImmutableList<TopLevelAspectsKey> topLevelAspectsKey,
       Supplier<Map<BuildConfigurationValue.Key, BuildConfiguration>> configurationLookupSupplier,
       TopLevelArtifactContext topLevelArtifactContextForConflictPruning,
       EventBus eventBus,
@@ -397,7 +399,7 @@
           skyframeExecutor.configureTargets(
               eventHandler,
               ctKeys,
-              aspectKeys,
+              topLevelAspectsKey,
               keepGoing,
               numThreads,
               cpuHeavySkyKeysThreadPoolSize);
@@ -405,21 +407,33 @@
       enableAnalysis(false);
     }
 
-    Map<AspectKey, ConfiguredAspect> aspects = Maps.newHashMapWithExpectedSize(aspectKeys.size());
+    int numOfAspects = 0;
+    if (!topLevelAspectsKey.isEmpty()) {
+      numOfAspects =
+          topLevelAspectsKey.size() * topLevelAspectsKey.get(0).getTopLevelAspectsClasses().size();
+    }
+    Map<AspectKey, ConfiguredAspect> aspects = Maps.newHashMapWithExpectedSize(numOfAspects);
     Root singleSourceRoot = skyframeExecutor.getForcedSingleSourceRootIfNoExecrootSymlinkCreation();
     NestedSetBuilder<Package> packages =
         singleSourceRoot == null ? NestedSetBuilder.stableOrder() : null;
-    for (AspectValueKey aspectKey : aspectKeys) {
-      AspectValue value = (AspectValue) result.get(aspectKey);
+    ImmutableList.Builder<AspectKey> aspectKeysBuilder = ImmutableList.builder();
+
+    for (TopLevelAspectsKey key : topLevelAspectsKey) {
+      TopLevelAspectsValue value = (TopLevelAspectsValue) result.get(key);
       if (value == null) {
         // Skip aspects that couldn't be applied to targets.
         continue;
       }
-      aspects.put(value.getKey(), value.getConfiguredAspect());
-      if (packages != null) {
-        packages.addTransitive(value.getTransitivePackagesForPackageRootResolution());
+      for (SkyValue val : value.getTopLevelAspectsValues()) {
+        AspectValue aspectValue = (AspectValue) val;
+        aspects.put(aspectValue.getKey(), aspectValue.getConfiguredAspect());
+        if (packages != null) {
+          packages.addTransitive(aspectValue.getTransitivePackagesForPackageRootResolution());
+        }
+        aspectKeysBuilder.add(aspectValue.getKey());
       }
     }
+    ImmutableList<AspectKey> aspectKeys = aspectKeysBuilder.build();
 
     Collection<ConfiguredTarget> cts = Lists.newArrayListWithCapacity(ctKeys.size());
     for (ConfiguredTargetKey value : ctKeys) {
@@ -561,7 +575,7 @@
           BuildConfigurationValue.Key configKey =
               ctKey instanceof ConfiguredTargetKey
                   ? ((ConfiguredTargetKey) ctKey).getConfigurationKey()
-                  : ((AspectValueKey) ctKey).getAspectConfigurationKey();
+                  : ((AspectKey) ctKey).getAspectConfigurationKey();
           eventBus.post(
               new AnalysisFailureEvent(
                   ctKey,
@@ -597,13 +611,9 @@
               .collect(toImmutableList());
 
       aspects =
-          aspectKeys.stream()
-              .filter(topLevelActionConflictReport::isErrorFree)
-              .map(result::get)
-              .map(AspectValue.class::cast)
-              .collect(
-                  ImmutableMap.toImmutableMap(
-                      AspectValue::getKey, AspectValue::getConfiguredAspect));
+          aspects.entrySet().stream()
+              .filter(e -> topLevelActionConflictReport.isErrorFree(e.getKey()))
+              .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue));
     }
 
     return new SkyframeAnalysisResult(
@@ -744,15 +754,14 @@
           .reportCycles(errorInfo.getCycleInfo(), errorKey, eventHandler);
       Exception cause = errorInfo.getException();
       Preconditions.checkState(cause != null || !errorInfo.getCycleInfo().isEmpty(), errorInfo);
-
-      if (errorKey.argument() instanceof AspectValueKey) {
+      if (errorKey.argument() instanceof TopLevelAspectsKey) {
         // We skip Aspects in the keepGoing case; the failures should already have been reported to
         // the event handler.
         if (!keepGoing && noKeepGoingException == null) {
-          AspectValueKey aspectKey = (AspectValueKey) errorKey.argument();
+          TopLevelAspectsKey aspectKey = (TopLevelAspectsKey) errorKey.argument();
           String errorMsg =
               String.format(
-                  "Analysis of aspect '%s' failed; build aborted", aspectKey.getDescription());
+                  "Analysis of aspects '%s' failed; build aborted", aspectKey.getDescription());
           noKeepGoingException = createViewCreationFailedException(cause, errorMsg);
         }
         continue;
@@ -773,7 +782,7 @@
       }
       Preconditions.checkState(
           errorKey.argument() instanceof ConfiguredTargetKey,
-          "expected '%s' to be a AspectValueKey or ConfiguredTargetKey",
+          "expected '%s' to be a TopLevelAspectsKey or ConfiguredTargetKey",
           errorKey.argument());
       ConfiguredTargetKey label = (ConfiguredTargetKey) errorKey.argument();
       Label topLevelLabel = label.getLabel();
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index 3ef335b..8219b72 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -159,6 +159,7 @@
 import com.google.devtools.build.lib.server.FailureDetails.TargetPatterns;
 import com.google.devtools.build.lib.skyframe.ArtifactConflictFinder.ConflictException;
 import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
 import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.FileDirtinessChecker;
 import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
 import com.google.devtools.build.lib.skyframe.MetadataConsumerForMetrics.FilesMetricConsumer;
@@ -565,7 +566,8 @@
             new BuildViewProvider(),
             ruleClassProvider,
             shouldStoreTransitivePackagesInLoadingAndAnalysis()));
-    map.put(SkyFunctions.LOAD_STARLARK_ASPECT, new ToplevelStarlarkAspectFunction());
+    map.put(SkyFunctions.LOAD_STARLARK_ASPECT, new LoadStarlarkAspectFunction());
+    map.put(SkyFunctions.TOP_LEVEL_ASPECTS, new ToplevelStarlarkAspectFunction());
     map.put(SkyFunctions.ACTION_LOOKUP_CONFLICT_FINDING, new ActionLookupConflictFindingFunction());
     map.put(
         SkyFunctions.TOP_LEVEL_ACTION_LOOKUP_CONFLICT_FINDING,
@@ -2319,7 +2321,7 @@
   EvaluationResult<ActionLookupValue> configureTargets(
       ExtendedEventHandler eventHandler,
       List<ConfiguredTargetKey> values,
-      List<AspectValueKey> aspectKeys,
+      ImmutableList<TopLevelAspectsKey> aspectKeys,
       boolean keepGoing,
       int numThreads,
       int cpuHeavySkyKeysThreadPoolSize)
@@ -3054,7 +3056,7 @@
   }
 
   final AnalysisTraversalResult getActionLookupValuesInBuild(
-      List<ConfiguredTargetKey> topLevelCtKeys, List<AspectValueKey> aspectKeys)
+      List<ConfiguredTargetKey> topLevelCtKeys, ImmutableList<AspectKey> aspectKeys)
       throws InterruptedException {
     AnalysisTraversalResult result = new AnalysisTraversalResult();
     if (!isAnalysisIncremental()) {
@@ -3074,7 +3076,7 @@
     for (ConfiguredTargetKey key : topLevelCtKeys) {
       findActionsRecursively(walkableGraph, key, seen, result);
     }
-    for (AspectValueKey key : aspectKeys) {
+    for (AspectKey key : aspectKeys) {
       findActionsRecursively(walkableGraph, key, seen, result);
     }
     return result;
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java b/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
index cf8f828..693bde7 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/TargetCycleReporter.java
@@ -39,7 +39,7 @@
       Predicates.or(
           SkyFunctions.isSkyFunction(SkyFunctions.CONFIGURED_TARGET),
           SkyFunctions.isSkyFunction(SkyFunctions.ASPECT),
-          SkyFunctions.isSkyFunction(SkyFunctions.LOAD_STARLARK_ASPECT),
+          SkyFunctions.isSkyFunction(SkyFunctions.TOP_LEVEL_ASPECTS),
           SkyFunctions.isSkyFunction(TransitiveTargetKey.NAME),
           SkyFunctions.isSkyFunction(SkyFunctions.PREPARE_ANALYSIS_PHASE));
 
@@ -65,8 +65,6 @@
       return ((ConfiguredTargetKey) key.argument()).prettyPrint();
     } else if (key instanceof AspectKey) {
       return ((AspectKey) key.argument()).prettyPrint();
-    } else if (key instanceof AspectValueKey) {
-      return ((AspectValueKey) key).getDescription();
     } else {
       return getLabel(key).toString();
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
index a858e35..d230637 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToplevelStarlarkAspectFunction.java
@@ -14,22 +14,37 @@
 
 package com.google.devtools.build.lib.skyframe;
 
-import com.google.devtools.build.lib.causes.LabelCause;
-import com.google.devtools.build.lib.cmdline.Label;
+import com.google.common.collect.ImmutableList;
+import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
+import com.google.devtools.build.lib.actions.ActionLookupValue;
+import com.google.devtools.build.lib.analysis.AspectCollection;
+import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.Aspect;
+import com.google.devtools.build.lib.packages.AspectClass;
+import com.google.devtools.build.lib.packages.AspectDescriptor;
+import com.google.devtools.build.lib.packages.AspectParameters;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
 import com.google.devtools.build.lib.packages.StarlarkAspect;
-import com.google.devtools.build.lib.server.FailureDetails.Analysis;
-import com.google.devtools.build.lib.server.FailureDetails.Analysis.Code;
-import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
-import com.google.devtools.build.lib.skyframe.AspectValueKey.StarlarkAspectLoadingKey;
-import com.google.devtools.build.lib.util.DetailedExitCode;
+import com.google.devtools.build.lib.packages.StarlarkAspectClass;
+import com.google.devtools.build.lib.packages.StarlarkDefinedAspect;
+import com.google.devtools.build.lib.packages.StarlarkNativeAspect;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.AspectValueKey.TopLevelAspectsKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingValue;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
+import java.util.HashMap;
+import java.util.Map;
 import javax.annotation.Nullable;
 
 /**
- * SkyFunction to load aspects from Starlark extensions and calculate their values.
+ * SkyFunction to load top level aspects, build the dependency relation between them based on the
+ * providers they advertise and provide using {@link AspectCollection} and runs the obtained aspects
+ * path on the top level target.
  *
  * <p>Used for loading top-level aspects. At top level, in {@link
  * com.google.devtools.build.lib.analysis.BuildView}, we cannot invoke two SkyFunctions one after
@@ -41,34 +56,33 @@
   @Nullable
   @Override
   public SkyValue compute(SkyKey skyKey, Environment env)
-      throws LoadStarlarkAspectFunctionException, InterruptedException {
-    StarlarkAspectLoadingKey aspectLoadingKey = (StarlarkAspectLoadingKey) skyKey.argument();
-    String starlarkValueName = aspectLoadingKey.getStarlarkValueName();
-    Label starlarkFileLabel = aspectLoadingKey.getStarlarkFileLabel();
+      throws TopLevelStarlarkAspectFunctionException, InterruptedException {
+    TopLevelAspectsKey topLevelAspectsKey = (TopLevelAspectsKey) skyKey.argument();
 
-    StarlarkAspect starlarkAspect;
-    try {
-      starlarkAspect = AspectFunction.loadStarlarkAspect(env, starlarkFileLabel, starlarkValueName);
-      if (starlarkAspect == null) {
-        return null;
-      }
-      if (!starlarkAspect.getParamAttributes().isEmpty()) {
-        String msg =
-            String.format(
-                "Cannot instantiate parameterized aspect %s at the top level.",
-                starlarkAspect.getName());
-        throw new AspectCreationException(
-            msg,
-            new LabelCause(
-                starlarkFileLabel,
-                createDetailedCode(msg, Code.PARAMETERIZED_TOP_LEVEL_ASPECT_INVALID)));
-      }
-    } catch (AspectCreationException e) {
-      throw new LoadStarlarkAspectFunctionException(e);
+    ImmutableList<Aspect> topLevelAspects =
+        getTopLevelAspects(env, topLevelAspectsKey.getTopLevelAspectsClasses());
+    if (topLevelAspects == null) {
+      return null; // some aspects are not loaded
     }
-    SkyKey aspectKey = aspectLoadingKey.toAspectKey(starlarkAspect.getAspectClass());
 
-    return env.getValue(aspectKey);
+    AspectCollection aspectCollection;
+    try {
+      aspectCollection = AspectCollection.create(topLevelAspects);
+    } catch (AspectCycleOnPathException e) {
+      env.getListener().handle(Event.error(e.getMessage()));
+      throw new TopLevelStarlarkAspectFunctionException(
+          new AspectCreationException(e.getMessage(), topLevelAspectsKey.getLabel()));
+    }
+
+    ImmutableList<AspectKey> aspectKeys =
+        getTopLevelAspectsKeys(aspectCollection, topLevelAspectsKey.getBaseConfiguredTargetKey());
+
+    Map<SkyKey, SkyValue> result = env.getValues(aspectKeys);
+    if (env.valuesMissing()) {
+      return null; // some aspects keys are not evaluated
+    }
+
+    return new TopLevelAspectsValue(result);
   }
 
   @Nullable
@@ -77,18 +91,118 @@
     return null;
   }
 
-  private static DetailedExitCode createDetailedCode(String msg, Code code) {
-    return DetailedExitCode.of(
-        FailureDetail.newBuilder()
-            .setMessage(msg)
-            .setAnalysis(Analysis.newBuilder().setCode(code))
-            .build());
+  @Nullable
+  private static ImmutableList<Aspect> getTopLevelAspects(
+      Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
+      throws InterruptedException {
+    ImmutableList.Builder<Aspect> topLevelAspects = ImmutableList.builder();
+
+    ImmutableList.Builder<StarlarkAspectLoadingKey> aspectLoadingKeys = ImmutableList.builder();
+    for (AspectClass aspectClass : topLevelAspectsClasses) {
+      if (aspectClass instanceof StarlarkAspectClass) {
+        aspectLoadingKeys.add(
+            LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey(
+                (StarlarkAspectClass) aspectClass));
+      }
+    }
+
+    Map<SkyKey, SkyValue> loadedAspects = env.getValues(aspectLoadingKeys.build());
+    if (env.valuesMissing()) {
+      return null;
+    }
+
+    for (AspectClass aspectClass : topLevelAspectsClasses) {
+      if (aspectClass instanceof StarlarkAspectClass) {
+        StarlarkAspectLoadingValue aspectLoadingValue =
+            (StarlarkAspectLoadingValue)
+                loadedAspects.get(
+                    LoadStarlarkAspectFunction.createStarlarkAspectLoadingKey(
+                        (StarlarkAspectClass) aspectClass));
+        StarlarkAspect starlarkAspect = aspectLoadingValue.getAspect();
+        if (starlarkAspect instanceof StarlarkDefinedAspect) {
+          StarlarkDefinedAspect starlarkDefinedAspect = (StarlarkDefinedAspect) starlarkAspect;
+          topLevelAspects.add(
+              Aspect.forStarlark(
+                  starlarkDefinedAspect.getAspectClass(),
+                  starlarkDefinedAspect.getDefinition(AspectParameters.EMPTY),
+                  AspectParameters.EMPTY,
+                  /** inheritedRequiredProviders = */
+                  null,
+                  /** inheritedAttributeAspects = */
+                  null));
+        } else {
+          topLevelAspects.add(Aspect.forNative(((StarlarkNativeAspect) starlarkAspect)));
+        }
+      } else {
+        topLevelAspects.add(Aspect.forNative((NativeAspectClass) aspectClass));
+      }
+    }
+
+    return topLevelAspects.build();
+  }
+
+  private static ImmutableList<AspectKey> getTopLevelAspectsKeys(
+      AspectCollection aspectCollection, ConfiguredTargetKey topLevelTargetKey) {
+    Map<AspectDescriptor, AspectKey> result = new HashMap<>();
+    for (AspectCollection.AspectDeps aspectDeps : aspectCollection.getUsedAspects()) {
+      buildAspectKey(aspectDeps, result, topLevelTargetKey);
+    }
+    return ImmutableList.copyOf(result.values());
+  }
+
+  private static AspectKey buildAspectKey(
+      AspectCollection.AspectDeps aspectDeps,
+      Map<AspectDescriptor, AspectKey> result,
+      ConfiguredTargetKey topLevelTargetKey) {
+    if (result.containsKey(aspectDeps.getAspect())) {
+      return result.get(aspectDeps.getAspect());
+    }
+
+    ImmutableList.Builder<AspectKey> dependentAspects = ImmutableList.builder();
+    for (AspectCollection.AspectDeps path : aspectDeps.getUsedAspects()) {
+      dependentAspects.add(buildAspectKey(path, result, topLevelTargetKey));
+    }
+
+    AspectKey aspectKey =
+        AspectValueKey.createAspectKey(
+            aspectDeps.getAspect(),
+            dependentAspects.build(),
+            topLevelTargetKey.getConfigurationKey(),
+            topLevelTargetKey);
+    result.put(aspectKey.getAspectDescriptor(), aspectKey);
+    return aspectKey;
   }
 
   /** Exceptions thrown from ToplevelStarlarkAspectFunction. */
-  public static class LoadStarlarkAspectFunctionException extends SkyFunctionException {
-    public LoadStarlarkAspectFunctionException(AspectCreationException cause) {
+  public static class TopLevelStarlarkAspectFunctionException extends SkyFunctionException {
+    public TopLevelStarlarkAspectFunctionException(AspectCreationException cause) {
       super(cause, Transience.PERSISTENT);
     }
   }
+
+  /**
+   * SkyValue for {@code TopLevelAspectsKey} wraps a list of the {@code AspectValue} of the top
+   * level aspects applied on the same top level target.
+   */
+  public static class TopLevelAspectsValue implements ActionLookupValue {
+    private final Map<SkyKey, SkyValue> topLevelAspectsMap;
+
+    public TopLevelAspectsValue(Map<SkyKey, SkyValue> topLevelAspectsMap) {
+      this.topLevelAspectsMap = topLevelAspectsMap;
+    }
+
+    public ImmutableList<SkyValue> getTopLevelAspectsValues() {
+      return ImmutableList.copyOf(topLevelAspectsMap.values());
+    }
+
+    public SkyValue get(SkyKey skyKey) {
+      return topLevelAspectsMap.get(skyKey);
+    }
+
+    @Override
+    public ImmutableList<ActionAnalysisMetadata> getActions() {
+      // return topLevelAspectsMap.values().stream().
+      return ImmutableList.of();
+    }
+  }
 }