Define an outgoing rule transition interface.

This lets a parent choose a transition for its dep based
on the dep's rule class.

Implement (experimental) dynamic Android resource filtering
trimming with this.

PiperOrigin-RevId: 163259052
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 014248e..802ca94 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -2160,6 +2160,16 @@
       return;
     }
 
+    // Apply the parent rule's outgoing transition if it has one.
+    RuleTransitionFactory transitionFactory =
+        fromRule.getRuleClassObject().getOutgoingTransitionFactory();
+    if (transitionFactory != null) {
+      Transition transition = transitionFactory.buildTransitionFor(toTarget.getAssociatedRule());
+      if (transition != null) {
+        transitionApplier.applyTransition(transition);
+      }
+    }
+
     // TODO(gregce): make the below transitions composable (i.e. take away the "else" clauses) once
     // the static config code path is removed. They can be mixed freely with dynamic configurations.
     if (attribute.hasSplitConfigurationTransition()) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
index 29a7fd9..ae9ec03 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java
@@ -491,6 +491,7 @@
     private ImplicitOutputsFunction implicitOutputsFunction = ImplicitOutputsFunction.NONE;
     private Configurator<?, ?> configurator = NO_CHANGE;
     private RuleTransitionFactory transitionFactory;
+    private RuleTransitionFactory outgoingTransitionFactory;
     private ConfiguredTargetFactory<?, ?> configuredTargetFactory = null;
     private PredicateWithMessage<Rule> validityPredicate =
         PredicatesWithMessage.<Rule>alwaysTrue();
@@ -515,7 +516,7 @@
      * Constructs a new {@code RuleClassBuilder} using all attributes from all
      * parent rule classes. An attribute cannot exist in more than one parent.
      *
-     * <p>The rule type affects the the allowed names and the required
+     * <p>The rule type affects the allowed names and the required
      * attributes (see {@link RuleClassType}).
      *
      * @throws IllegalArgumentException if an attribute with the same name exists
@@ -609,6 +610,7 @@
           isConfigMatcher,
           configurator,
           transitionFactory,
+          outgoingTransitionFactory,
           configuredTargetFactory,
           validityPredicate,
           preferredDependencyPredicate,
@@ -752,11 +754,13 @@
     }
 
     /**
-     * Applies the given transition to all incoming edges for this rule class.  Does not work with
-     * static configurations.
+     * Applies the given transition to all incoming edges for this rule class.
      *
-     * <p>Note that the given transition must be a PatchTransition instance.  We use the more
-     * general Transtion here because PatchTransition is not available in this package.
+     * <p>The given transition must be a PatchTransition.  We use the more general Transition
+     * here because PatchTransition is not available in this package.
+     *
+     * <p>If you need the transition to depend on the rule it's being applied to, use
+     * {@link #cfg(RuleTransitionFactory)}.
      */
     public Builder cfg(Transition transition) {
       Preconditions.checkState(type != RuleClassType.ABSTRACT,
@@ -768,6 +772,12 @@
       return this;
     }
 
+    /**
+     * Applies the given transition factory to all incoming edges for this rule class.
+     *
+     * <p>Unlike{@link #cfg(Transition)}, the factory can examine the rule when deciding what
+     * transition to use.
+     */
     public Builder cfg(RuleTransitionFactory transitionFactory) {
       Preconditions.checkState(type != RuleClassType.ABSTRACT,
           "Setting not inherited property (cfg) of abstract rule class '%s'", name);
@@ -778,6 +788,22 @@
       return this;
     }
 
+    /**
+     * Applies the given transition factory to all deps of this rule class.
+     *
+     * <p>This means that for each dep, the factory can examine the dep's {@link Rule} to
+     * determine the right transition for that dep.
+     */
+    public Builder depsCfg(RuleTransitionFactory outgoingTransitionFactory) {
+      Preconditions.checkState(type != RuleClassType.ABSTRACT,
+          "Setting not inherited property (configureDeps) of abstract rule class '%s'", name);
+      Preconditions.checkState(this.outgoingTransitionFactory == null,
+          "Property configureDeps has already been set");
+      Preconditions.checkNotNull(outgoingTransitionFactory);
+      this.outgoingTransitionFactory = outgoingTransitionFactory;
+      return this;
+    }
+
     public Builder factory(ConfiguredTargetFactory<?, ?> factory) {
       this.configuredTargetFactory = factory;
       return this;
@@ -1073,6 +1099,11 @@
   private final RuleTransitionFactory transitionFactory;
 
   /**
+   * A factory which will produce custom configuration transitions for each dep of this rule.
+   */
+  private final RuleTransitionFactory outgoingTransitionFactory;
+
+  /**
    * The factory that creates configured targets from this rule.
    */
   private final ConfiguredTargetFactory<?, ?> configuredTargetFactory;
@@ -1163,6 +1194,7 @@
       boolean isConfigMatcher,
       Configurator<?, ?> configurator,
       RuleTransitionFactory transitionFactory,
+      RuleTransitionFactory outgoingRuleTransitionFactory,
       ConfiguredTargetFactory<?, ?> configuredTargetFactory,
       PredicateWithMessage<Rule> validityPredicate,
       Predicate<String> preferredDependencyPredicate,
@@ -1188,6 +1220,7 @@
     this.isConfigMatcher = isConfigMatcher;
     this.configurator = Preconditions.checkNotNull(configurator);
     this.transitionFactory = transitionFactory;
+    this.outgoingTransitionFactory = outgoingRuleTransitionFactory;
     this.configuredTargetFactory = configuredTargetFactory;
     this.validityPredicate = validityPredicate;
     this.preferredDependencyPredicate = preferredDependencyPredicate;
@@ -1263,6 +1296,10 @@
     return transitionFactory;
   }
 
+  public RuleTransitionFactory getOutgoingTransitionFactory() {
+    return outgoingTransitionFactory;
+  }
+
   @SuppressWarnings("unchecked")
   public <CT, RC> ConfiguredTargetFactory<CT, RC> getConfiguredTargetFactory() {
     return (ConfiguredTargetFactory<CT, RC>) configuredTargetFactory;
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index c8164cc..51db707 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -28,6 +28,7 @@
 import static com.google.devtools.build.lib.util.FileTypeSet.ANY_FILE;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.analysis.BaseRuleClasses;
@@ -48,6 +49,7 @@
 import com.google.devtools.build.lib.packages.RuleClass;
 import com.google.devtools.build.lib.packages.RuleClass.Builder;
 import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.RuleTransitionFactory;
 import com.google.devtools.build.lib.packages.TriState;
 import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidAaptVersion;
 import com.google.devtools.build.lib.rules.android.AndroidConfiguration.AndroidManifestMerger;
@@ -287,6 +289,25 @@
     }
   }
 
+  /**
+   * Turns of dynamic resource filtering for non-Android targets. This prevents unnecessary
+   * build graph bloat. For example, there's no point analyzing distinct cc_library tarets for
+   * different resource filter configurations because cc_library semantics doesn't care about
+   * filters.
+   */
+  public static final RuleTransitionFactory REMOVE_DYNAMIC_RESOURCE_FILTERING =
+      new RuleTransitionFactory() {
+        /** Dependencies of these rule class types need to keep resource filtering info. */
+        private final ImmutableSet<String> keepFilterRuleClasses =
+            ImmutableSet.of("android_binary", "android_library");
+
+        @Override
+        public Attribute.Transition buildTransitionFor(Rule depRule) {
+          return keepFilterRuleClasses.contains(depRule.getRuleClass())
+              ? null : ResourceFilter.REMOVE_DYNAMICALLY_CONFIGURED_RESOURCE_FILTERING_TRANSITION;
+        }
+      };
+
   public static final FileType ANDROID_IDL = FileType.of(".aidl");
 
   public static final String[] ALLOWED_DEPENDENCIES = {