Optionally propagate aspects through rule output dependencies

Starlark-defined aspects can opt into this new feature by being defined with a new parameter on the aspect() function, "apply_to_generating_rules".
This behavior is guarded by a new experimental flag, --experimental_aspect_output_propagation.

RELNOTES: None.
PiperOrigin-RevId: 269571978
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
index bbe9db3..35e02b2 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/AspectDefinition.java
@@ -75,6 +75,7 @@
   @Nullable private final ImmutableSet<String> restrictToAttributes;
   @Nullable private final ConfigurationFragmentPolicy configurationFragmentPolicy;
   private final boolean applyToFiles;
+  private final boolean applyToGeneratingRules;
 
   public AdvertisedProviderSet getAdvertisedProviders() {
     return advertisedProviders;
@@ -90,7 +91,8 @@
       ImmutableSet<Label> requiredToolchains,
       @Nullable ImmutableSet<String> restrictToAttributes,
       @Nullable ConfigurationFragmentPolicy configurationFragmentPolicy,
-      boolean applyToFiles) {
+      boolean applyToFiles,
+      boolean applyToGeneratingRules) {
     this.aspectClass = aspectClass;
     this.advertisedProviders = advertisedProviders;
     this.requiredProviders = requiredProviders;
@@ -101,6 +103,7 @@
     this.restrictToAttributes = restrictToAttributes;
     this.configurationFragmentPolicy = configurationFragmentPolicy;
     this.applyToFiles = applyToFiles;
+    this.applyToGeneratingRules = applyToGeneratingRules;
   }
 
   public String getName() {
@@ -169,6 +172,14 @@
     return applyToFiles;
   }
 
+  /**
+   * Returns whether this aspect should, when it would be applied to an output file, instead apply
+   * to the generating rule of that output file.
+   */
+  public boolean applyToGeneratingRules() {
+    return applyToGeneratingRules;
+  }
+
   public static boolean satisfies(Aspect aspect, AdvertisedProviderSet advertisedProviderSet) {
     return aspect.getDefinition().getRequiredProviders().isSatisfiedBy(advertisedProviderSet);
   }
@@ -234,6 +245,7 @@
     private final ConfigurationFragmentPolicy.Builder configurationFragmentPolicy =
         new ConfigurationFragmentPolicy.Builder();
     private boolean applyToFiles = false;
+    private boolean applyToGeneratingRules = false;
     private final List<Label> requiredToolchains = new ArrayList<>();
 
     public Builder(AspectClass aspectClass) {
@@ -259,7 +271,7 @@
       requiredProviders.addNativeSet(ImmutableSet.copyOf(providers));
       return this;
     }
-    
+
     /**
      * Asserts that this aspect can only be evaluated for rules that supply all of the specified
      * Skylark providers.
@@ -456,6 +468,18 @@
       return this;
     }
 
+    /**
+     * Sets whether this aspect should, when it would be applied to an output file, instead apply to
+     * the generating rule of that output file.
+     *
+     * <p>Default is <code>false</code>. Currently only supported for aspects which do not have a
+     * "required providers" list.
+     */
+    public Builder applyToGeneratingRules(boolean applyToGeneratingRules) {
+      this.applyToGeneratingRules = applyToGeneratingRules;
+      return this;
+    }
+
     /** Adds the given toolchains as requirements for this aspect. */
     public Builder addRequiredToolchains(Label... toolchainLabels) {
       Iterables.addAll(this.requiredToolchains, Lists.newArrayList(toolchainLabels));
@@ -473,16 +497,24 @@
      * <p>The builder object is reusable afterwards.
      */
     public AspectDefinition build() {
+      RequiredProviders requiredProviders = this.requiredProviders.build();
+      if (applyToGeneratingRules && !requiredProviders.acceptsAny()) {
+        throw new IllegalStateException(
+            "An aspect cannot simultaneously have required providers "
+                + "and apply to generating rules.");
+      }
+
       return new AspectDefinition(
           aspectClass,
           advertisedProviders.build(),
-          requiredProviders.build(),
+          requiredProviders,
           requiredAspectProviders.build(),
           ImmutableMap.copyOf(attributes),
           ImmutableSet.copyOf(requiredToolchains),
           propagateAlongAttributes == null ? null : ImmutableSet.copyOf(propagateAlongAttributes),
           configurationFragmentPolicy.build(),
-          applyToFiles);
+          applyToFiles,
+          applyToGeneratingRules);
     }
   }
 }