Roll forward of https://github.com/bazelbuild/bazel/commit/943c83aa58731c4f9561d79c458f254427a8f24c: Command line aspect-on-aspect

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.

NEW:
- Add `incompatible_ignore_duplicate_top_level_aspects` flag to allow duplicates in `--aspects` list. The flag is set to true by default, otherwise a validation error will be thrown in case of duplicates in top-level aspects.
- Fix the error reporting for duplicate native aspects in `--aspects` list to be reported as a SkyFunction exception instead of crashing with assertion error.

Automated rollback of commit 7b4f9826d2d38ac7d071a4ada7b8a40a7a78226d.

*** Reason for rollback ***

Guard the validation against duplicate aspects in `--aspects` list by a flag to avoid breaking builds with duplicate aspects.

*** Original change description ***

Automated rollback of commit 7649f610c45190735fd7de433b15679b21b2d91b.

*** Reason for rollback ***

The added validation to prevent duplicate aspects in --aspects list breaks //production/datapush/modular/implementations/build:buildtarget_test

*** Original change description ***

Roll forward of https://github.com/bazelbuild/bazel/commit/943c83aa58731c4f9561d79c458f254427a8f24c: Command line aspect-on-aspect

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_a...

***

PiperOrigin-RevId: 389217989
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/AnalysisOptions.java b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
index e8662a2..a39ddb6 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/AnalysisOptions.java
@@ -166,4 +166,19 @@
               + " be used. Example value: \"HOST_CPUS*0.5\".",
       converter = CpuResourceConverter.class)
   public int oomSensitiveSkyFunctionsSemaphoreSize;
+
+  @Option(
+      name = "incompatible_ignore_duplicate_top_level_aspects",
+      defaultValue = "true",
+      documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+      metadataTags = {
+        OptionMetadataTag.INCOMPATIBLE_CHANGE,
+        OptionMetadataTag.TRIGGERED_BY_ALL_INCOMPATIBLE_CHANGES
+      },
+      effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS},
+      help =
+          "If true, remove duplicates from --aspects list by keeping only the first occurrence of"
+              + " every aspect. Otherwise, throw validation error if duplicate aspects are"
+              + " encountered.")
+  public boolean ignoreDuplicateTopLevelAspects;
 }
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..2e289f3 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,11 @@
             .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();
+    if (viewOptions.ignoreDuplicateTopLevelAspects) {
+      // remove duplicates from aspects list
+      aspects = ImmutableSet.copyOf(aspects).asList();
+    }
     for (String aspect : aspects) {
       // Syntax: label%aspect
       int delimiterPosition = aspect.indexOf('%');
@@ -318,38 +318,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 +334,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 +377,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 6b62bd1..c25a910 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
@@ -305,11 +305,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 2b9f2a3..bf0b414 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 int hashCode() {
     return Objects.hash(extensionLabel, exportedName);
   }
+
+  @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 391a5b3..f97e08d 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,
@@ -87,33 +72,48 @@
         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());
+  }
+
+  /** Common superclass for {@link AspectKey} and {@link TopLevelAspectsKey}. */
+  public abstract static class AspectBaseKey implements ActionLookupKey {
+    private final ConfiguredTargetKey baseConfiguredTargetKey;
+    private final int hashCode;
+
+    private AspectBaseKey(ConfiguredTargetKey baseConfiguredTargetKey, int hashCode) {
+      this.baseConfiguredTargetKey = baseConfiguredTargetKey;
+      this.hashCode = hashCode;
+    }
+
+    /** Returns the key for the base configured target for this aspect. */
+    public final ConfiguredTargetKey getBaseConfiguredTargetKey() {
+      return baseConfiguredTargetKey;
+    }
+
+    @Override
+    public final int hashCode() {
+      return hashCode;
+    }
   }
 
   // Specific subtypes of aspect keys.
 
   /** Represents an aspect applied to a particular target. */
   @AutoCodec
-  public static final class AspectKey extends AspectValueKey {
-    private final ConfiguredTargetKey baseConfiguredTargetKey;
+  public static final class AspectKey extends AspectBaseKey {
     private final ImmutableList<AspectKey> baseKeys;
     @Nullable private final BuildConfigurationValue.Key aspectConfigurationKey;
     private final AspectDescriptor aspectDescriptor;
-    private final int hashCode;
 
     private AspectKey(
         ConfiguredTargetKey baseConfiguredTargetKey,
@@ -121,11 +121,10 @@
         AspectDescriptor aspectDescriptor,
         @Nullable BuildConfigurationValue.Key aspectConfigurationKey,
         int hashCode) {
+      super(baseConfiguredTargetKey, hashCode);
       this.baseKeys = baseKeys;
       this.aspectConfigurationKey = aspectConfigurationKey;
-      this.baseConfiguredTargetKey = baseConfiguredTargetKey;
       this.aspectDescriptor = aspectDescriptor;
-      this.hashCode = hashCode;
     }
 
     @AutoCodec.VisibleForSerialization
@@ -150,14 +149,19 @@
       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();
     }
 
     @Override
     public Label getLabel() {
-      return baseConfiguredTargetKey.getLabel();
+      return getBaseConfiguredTargetKey().getLabel();
     }
 
     public AspectClass getAspectClass() {
@@ -187,7 +191,6 @@
       return baseKeys;
     }
 
-    @Override
     public String getDescription() {
       if (baseKeys.isEmpty()) {
         return String.format("%s of %s", aspectDescriptor.getAspectClass().getName(), getLabel());
@@ -215,22 +218,10 @@
      * 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;
-    }
-
-    @Override
-    public int hashCode() {
-      return hashCode;
-    }
-
     @Override
     public boolean equals(Object other) {
       if (this == other) {
@@ -240,10 +231,10 @@
         return false;
       }
       AspectKey that = (AspectKey) other;
-      return hashCode == that.hashCode
+      return hashCode() == that.hashCode()
           && Objects.equal(baseKeys, that.baseKeys)
           && Objects.equal(aspectConfigurationKey, that.aspectConfigurationKey)
-          && Objects.equal(baseConfiguredTargetKey, that.baseConfiguredTargetKey)
+          && Objects.equal(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
           && Objects.equal(aspectDescriptor, that.aspectDescriptor);
     }
 
@@ -266,7 +257,7 @@
           + " "
           + aspectConfigurationKey
           + " "
-          + baseConfiguredTargetKey
+          + getBaseConfiguredTargetKey()
           + " "
           + aspectDescriptor.getParameters();
     }
@@ -280,7 +271,7 @@
       return createAspectKey(
           ConfiguredTargetKey.builder()
               .setLabel(label)
-              .setConfigurationKey(baseConfiguredTargetKey.getConfigurationKey())
+              .setConfigurationKey(getBaseConfiguredTargetKey().getConfigurationKey())
               .build(),
           newBaseKeys.build(),
           aspectDescriptor,
@@ -288,70 +279,43 @@
     }
   }
 
-  /** 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 extends AspectBaseKey {
+    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) {
+      super(baseConfiguredTargetKey, 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
@@ -359,27 +323,8 @@
       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() {
-      return baseConfiguredTargetKey;
-    }
-
-    @Override
-    public int hashCode() {
-      return hashCode;
+    String getDescription() {
+      return topLevelAspectsClasses + " on " + getLabel();
     }
 
     @Override
@@ -387,35 +332,14 @@
       if (o == this) {
         return true;
       }
-      if (!(o instanceof StarlarkAspectLoadingKey)) {
+      if (!(o instanceof TopLevelAspectsKey)) {
         return false;
       }
-      StarlarkAspectLoadingKey that = (StarlarkAspectLoadingKey) o;
-      return hashCode == that.hashCode
+      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(getBaseConfiguredTargetKey(), that.getBaseConfiguredTargetKey())
+          && 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 39518ce..ac2ee83 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -26,6 +26,7 @@
         "BazelSkyframeExecutorConstants.java",
         "BuildConfigurationFunction.java",
         "BuildInfoCollectionFunction.java",
+        "BuildTopLevelAspectsDetailsFunction.java",
         "BzlLoadFunction.java",
         "BzlmodRepoRuleFunction.java",
         "CompletionFunction.java",
@@ -38,6 +39,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/BuildTopLevelAspectsDetailsFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java
new file mode 100644
index 0000000..eb68e55
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildTopLevelAspectsDetailsFunction.java
@@ -0,0 +1,302 @@
+// 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.ImmutableList;
+import com.google.common.collect.Interner;
+import com.google.devtools.build.lib.analysis.AspectCollection;
+import com.google.devtools.build.lib.analysis.AspectCollection.AspectCycleOnPathException;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
+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.AspectsListBuilder;
+import com.google.devtools.build.lib.packages.NativeAspectClass;
+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.AspectValueKey.AspectKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingKey;
+import com.google.devtools.build.lib.skyframe.LoadStarlarkAspectFunction.StarlarkAspectLoadingValue;
+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 java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import javax.annotation.Nullable;
+import net.starlark.java.eval.EvalException;
+
+/**
+ * SkyFunction to load top level aspects, build the dependency relation between them based on the
+ * aspects required by the top level aspects and the aspect providers they require and advertise
+ * using {@link AspectCollection}.
+ *
+ * <p>This is needed to compute the relationship between top-level aspects once for all top-level
+ * targets in the command. The {@link SkyValue} of this function contains a list of {@link
+ * AspectDetails} objects which contain the aspect descriptor and a list of the used aspects this
+ * aspect depends on. Then {@link ToplevelStarlarkAspectFunction} adds the target information to
+ * create {@link AspectKey}.
+ */
+public class BuildTopLevelAspectsDetailsFunction implements SkyFunction {
+  BuildTopLevelAspectsDetailsFunction() {}
+
+  private static final Interner<BuildTopLevelAspectsDetailsKey>
+      buildTopLevelAspectsDetailsKeyInterner = BlazeInterners.newWeakInterner();
+
+  @Nullable
+  @Override
+  public SkyValue compute(SkyKey skyKey, Environment env)
+      throws BuildTopLevelAspectsDetailsFunctionException, InterruptedException {
+    BuildTopLevelAspectsDetailsKey topLevelAspectsDetailsKey =
+        (BuildTopLevelAspectsDetailsKey) skyKey.argument();
+
+    ImmutableList<Aspect> topLevelAspects =
+        getTopLevelAspects(env, topLevelAspectsDetailsKey.getTopLevelAspectsClasses());
+
+    if (topLevelAspects == null) {
+      return null; // some aspects are not loaded
+    }
+
+    AspectCollection aspectCollection;
+    try {
+      aspectCollection = AspectCollection.create(topLevelAspects);
+    } catch (AspectCycleOnPathException e) {
+      // This exception should never happen because aspects duplicates are not allowed in top-level
+      // aspects and their existence should have been caught and reported by `getTopLevelAspects()`.
+      env.getListener().handle(Event.error(e.getMessage()));
+      throw new BuildTopLevelAspectsDetailsFunctionException(
+          new TopLevelAspectsDetailsBuildFailedException(e.getMessage()));
+    }
+    return new BuildTopLevelAspectsDetailsValue(getTopLevelAspectsDetails(aspectCollection));
+  }
+
+  @Nullable
+  @Override
+  public String extractTag(SkyKey skyKey) {
+    return null;
+  }
+
+  @Nullable
+  private static ImmutableList<Aspect> getTopLevelAspects(
+      Environment env, ImmutableList<AspectClass> topLevelAspectsClasses)
+      throws InterruptedException, BuildTopLevelAspectsDetailsFunctionException {
+    AspectsListBuilder aspectsList = new AspectsListBuilder();
+
+    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();
+        try {
+          starlarkAspect.attachToAspectsList(
+              /** baseAspectName= */
+              null,
+              aspectsList,
+              /** inheritedRequiredProviders= */
+              ImmutableList.of(),
+              /** inheritedAttributeAspects= */
+              ImmutableList.of(),
+              /** allowAspectsParameters= */
+              false);
+        } catch (EvalException e) {
+          env.getListener().handle(Event.error(e.getMessage()));
+          throw new BuildTopLevelAspectsDetailsFunctionException(
+              new TopLevelAspectsDetailsBuildFailedException(e.getMessage()));
+        }
+      } else {
+        try {
+          aspectsList.addAspect((NativeAspectClass) aspectClass);
+        } catch (AssertionError ex) {
+          env.getListener().handle(Event.error(ex.getMessage()));
+          throw new BuildTopLevelAspectsDetailsFunctionException(
+              new TopLevelAspectsDetailsBuildFailedException(ex.getMessage()));
+        }
+      }
+    }
+    return aspectsList.buildAspects();
+  }
+
+  private static Collection<AspectDetails> getTopLevelAspectsDetails(
+      AspectCollection aspectCollection) {
+    Map<AspectDescriptor, AspectDetails> result = new HashMap<>();
+    for (AspectCollection.AspectDeps aspectDeps : aspectCollection.getUsedAspects()) {
+      buildAspectDetails(aspectDeps, result);
+    }
+    return result.values();
+  }
+
+  private static AspectDetails buildAspectDetails(
+      AspectCollection.AspectDeps aspectDeps, Map<AspectDescriptor, AspectDetails> result) {
+    if (result.containsKey(aspectDeps.getAspect())) {
+      return result.get(aspectDeps.getAspect());
+    }
+
+    ImmutableList.Builder<AspectDetails> dependentAspects = ImmutableList.builder();
+    for (AspectCollection.AspectDeps path : aspectDeps.getUsedAspects()) {
+      dependentAspects.add(buildAspectDetails(path, result));
+    }
+
+    AspectDetails aspectDetails =
+        new AspectDetails(dependentAspects.build(), aspectDeps.getAspect());
+    result.put(aspectDetails.getAspectDescriptor(), aspectDetails);
+    return aspectDetails;
+  }
+
+  public static BuildTopLevelAspectsDetailsKey createBuildTopLevelAspectsDetailsKey(
+      ImmutableList<AspectClass> aspectClasses) {
+    return BuildTopLevelAspectsDetailsKey.createInternal(aspectClasses);
+  }
+
+  /** Exceptions thrown from BuildTopLevelAspectsDetailsFunction. */
+  public static class BuildTopLevelAspectsDetailsFunctionException extends SkyFunctionException {
+    public BuildTopLevelAspectsDetailsFunctionException(
+        TopLevelAspectsDetailsBuildFailedException cause) {
+      super(cause, Transience.PERSISTENT);
+    }
+  }
+
+  static final class TopLevelAspectsDetailsBuildFailedException extends Exception
+      implements SaneAnalysisException {
+    private final DetailedExitCode detailedExitCode;
+
+    private TopLevelAspectsDetailsBuildFailedException(String errorMessage) {
+      super(errorMessage);
+      this.detailedExitCode =
+          DetailedExitCode.of(
+              FailureDetail.newBuilder()
+                  .setMessage(errorMessage)
+                  .setAnalysis(Analysis.newBuilder().setCode(Code.ASPECT_CREATION_FAILED))
+                  .build());
+    }
+
+    @Override
+    public DetailedExitCode getDetailedExitCode() {
+      return detailedExitCode;
+    }
+  }
+
+  /**
+   * Details of the top-level aspects including the {@link AspectDescriptor} and a list of the
+   * aspects it depends on. This is used to build the {@link AspectKey} when combined with
+   * configured target details.
+   */
+  public static final class AspectDetails {
+    private final ImmutableList<AspectDetails> usedAspects;
+    private final AspectDescriptor aspectDescriptor;
+
+    AspectDetails(ImmutableList<AspectDetails> usedAspects, AspectDescriptor aspectDescriptor) {
+      this.usedAspects = usedAspects;
+      this.aspectDescriptor = aspectDescriptor;
+    }
+
+    public AspectDescriptor getAspectDescriptor() {
+      return aspectDescriptor;
+    }
+
+    public ImmutableList<AspectDetails> getUsedAspects() {
+      return usedAspects;
+    }
+  }
+
+  /** SkyKey for building top-level aspects details. */
+  public static final class BuildTopLevelAspectsDetailsKey implements SkyKey {
+    private final ImmutableList<AspectClass> topLevelAspectsClasses;
+    private final int hashCode;
+
+    @AutoCodec.Instantiator
+    @AutoCodec.VisibleForSerialization
+    static BuildTopLevelAspectsDetailsKey createInternal(
+        ImmutableList<AspectClass> topLevelAspectsClasses) {
+      return buildTopLevelAspectsDetailsKeyInterner.intern(
+          new BuildTopLevelAspectsDetailsKey(
+              topLevelAspectsClasses, java.util.Objects.hashCode(topLevelAspectsClasses)));
+    }
+
+    private BuildTopLevelAspectsDetailsKey(
+        ImmutableList<AspectClass> topLevelAspectsClasses, int hashCode) {
+      this.topLevelAspectsClasses = topLevelAspectsClasses;
+      this.hashCode = hashCode;
+    }
+
+    @Override
+    public SkyFunctionName functionName() {
+      return SkyFunctions.BUILD_TOP_LEVEL_ASPECTS_DETAILS;
+    }
+
+    ImmutableList<AspectClass> getTopLevelAspectsClasses() {
+      return topLevelAspectsClasses;
+    }
+
+    @Override
+    public int hashCode() {
+      return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (o == this) {
+        return true;
+      }
+      if (!(o instanceof BuildTopLevelAspectsDetailsKey)) {
+        return false;
+      }
+      BuildTopLevelAspectsDetailsKey that = (BuildTopLevelAspectsDetailsKey) o;
+      return hashCode == that.hashCode
+          && Objects.equal(topLevelAspectsClasses, that.topLevelAspectsClasses);
+    }
+  }
+
+  /**
+   * SkyValue for {@code BuildTopLevelAspectsDetailsKey} wraps a list of the {@code AspectDetails}
+   * of the top level aspects.
+   */
+  public static final class BuildTopLevelAspectsDetailsValue implements SkyValue {
+    private final ImmutableList<AspectDetails> aspectsDetails;
+
+    private BuildTopLevelAspectsDetailsValue(Collection<AspectDetails> aspectsDetails) {
+      this.aspectsDetails = ImmutableList.copyOf(aspectsDetails);
+    }
+
+    public ImmutableList<AspectDetails> getAspectsDetails() {
+      return aspectsDetails;
+    }
+  }
+}
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..c94ce04 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,10 @@
   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");
+  static final SkyFunctionName BUILD_TOP_LEVEL_ASPECTS_DETAILS =
+      SkyFunctionName.createHermetic("BUILD_TOP_LEVEL_ASPECTS_DETAILS");
   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 c718762..792dc66 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(
@@ -745,17 +755,15 @@
           .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();
           failedAspectLabel = aspectKey.getBaseConfiguredTargetKey();
-
           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;
@@ -776,7 +784,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 d5e9066..74521b1 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
@@ -162,6 +162,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;
@@ -574,7 +575,10 @@
             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.BUILD_TOP_LEVEL_ASPECTS_DETAILS, new BuildTopLevelAspectsDetailsFunction());
     map.put(SkyFunctions.ACTION_LOOKUP_CONFLICT_FINDING, new ActionLookupConflictFindingFunction());
     map.put(
         SkyFunctions.TOP_LEVEL_ACTION_LOOKUP_CONFLICT_FINDING,
@@ -2345,7 +2349,7 @@
   EvaluationResult<ActionLookupValue> configureTargets(
       ExtendedEventHandler eventHandler,
       List<ConfiguredTargetKey> values,
-      List<AspectValueKey> aspectKeys,
+      ImmutableList<TopLevelAspectsKey> aspectKeys,
       boolean keepGoing,
       int numThreads,
       int cpuHeavySkyKeysThreadPoolSize)
@@ -3082,7 +3086,7 @@
   }
 
   final AnalysisTraversalResult getActionLookupValuesInBuild(
-      List<ConfiguredTargetKey> topLevelCtKeys, List<AspectValueKey> aspectKeys)
+      List<ConfiguredTargetKey> topLevelCtKeys, ImmutableList<AspectKey> aspectKeys)
       throws InterruptedException {
     AnalysisTraversalResult result = new AnalysisTraversalResult();
     if (!isAnalysisIncremental()) {
@@ -3102,7 +3106,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..4fd3bb1 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,26 @@
 
 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.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.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.packages.AspectDescriptor;
+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.BuildTopLevelAspectsDetailsFunction.AspectDetails;
+import com.google.devtools.build.lib.skyframe.BuildTopLevelAspectsDetailsFunction.BuildTopLevelAspectsDetailsValue;
 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.Collection;
+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 run the aspects path obtained from top-level aspects on the list of top-level
+ * targets.
  *
  * <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 +45,29 @@
   @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);
+    BuildTopLevelAspectsDetailsValue topLevelAspectsDetails =
+        (BuildTopLevelAspectsDetailsValue)
+            env.getValue(
+                BuildTopLevelAspectsDetailsFunction.createBuildTopLevelAspectsDetailsKey(
+                    topLevelAspectsKey.getTopLevelAspectsClasses()));
+    if (topLevelAspectsDetails == null) {
+      return null; // some aspects details are not ready
     }
-    SkyKey aspectKey = aspectLoadingKey.toAspectKey(starlarkAspect.getAspectClass());
 
-    return env.getValue(aspectKey);
+    Collection<AspectKey> aspectsKeys =
+        getTopLevelAspectsKeys(
+            topLevelAspectsDetails.getAspectsDetails(),
+            topLevelAspectsKey.getBaseConfiguredTargetKey());
+
+    Map<SkyKey, SkyValue> result = env.getValues(aspectsKeys);
+    if (env.valuesMissing()) {
+      return null; // some aspects keys are not evaluated
+    }
+
+    return new TopLevelAspectsValue(result.values());
   }
 
   @Nullable
@@ -77,18 +76,63 @@
     return null;
   }
 
-  private static DetailedExitCode createDetailedCode(String msg, Code code) {
-    return DetailedExitCode.of(
-        FailureDetail.newBuilder()
-            .setMessage(msg)
-            .setAnalysis(Analysis.newBuilder().setCode(code))
-            .build());
+  private static Collection<AspectKey> getTopLevelAspectsKeys(
+      ImmutableList<AspectDetails> aspectsDetails, ConfiguredTargetKey topLevelTargetKey) {
+    Map<AspectDescriptor, AspectKey> result = new HashMap<>();
+    for (AspectDetails aspect : aspectsDetails) {
+      buildAspectKey(aspect, result, topLevelTargetKey);
+    }
+    return result.values();
+  }
+
+  private static AspectKey buildAspectKey(
+      AspectDetails aspect,
+      Map<AspectDescriptor, AspectKey> result,
+      ConfiguredTargetKey topLevelTargetKey) {
+    if (result.containsKey(aspect.getAspectDescriptor())) {
+      return result.get(aspect.getAspectDescriptor());
+    }
+
+    ImmutableList.Builder<AspectKey> dependentAspects = ImmutableList.builder();
+    for (AspectDetails depAspect : aspect.getUsedAspects()) {
+      dependentAspects.add(buildAspectKey(depAspect, result, topLevelTargetKey));
+    }
+
+    AspectKey aspectKey =
+        AspectValueKey.createAspectKey(
+            aspect.getAspectDescriptor(),
+            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 ImmutableList<SkyValue> topLevelAspectsValues;
+
+    public TopLevelAspectsValue(Collection<SkyValue> topLevelAspectsValues) {
+      this.topLevelAspectsValues = ImmutableList.copyOf(topLevelAspectsValues);
+    }
+
+    public ImmutableList<SkyValue> getTopLevelAspectsValues() {
+      return topLevelAspectsValues;
+    }
+
+    @Override
+    public ImmutableList<ActionAnalysisMetadata> getActions() {
+      return ImmutableList.of();
+    }
+  }
 }