Allow multiple trimming transition factories to be added.

If multiple trimming transition factories are added, they are composed in
the order they were added.

RELNOTES: None.
PiperOrigin-RevId: 198934666
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index c333d05..4821f6f 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -32,6 +32,7 @@
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration.Fragment;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
+import com.google.devtools.build.lib.analysis.config.ComposingRuleTransitionFactory;
 import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory;
 import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
 import com.google.devtools.build.lib.analysis.config.FragmentOptions;
@@ -413,17 +414,22 @@
     }
 
     /**
-     * Sets the transition factory that produces a trimming transition to be run over all targets
+     * Adds a transition factory that produces a trimming transition to be run over all targets
      * after other transitions.
      *
-     * <p>This is a temporary measure for supporting manual trimming of feature flags, and support
-     * for this transition factory will likely be removed at some point in the future (whenever
-     * automatic trimming is sufficiently workable).
+     * <p>Transitions are run in the order they're added.
+     *
+     * <p>This is a temporary measure for supporting trimming of test rules and manual trimming of
+     * feature flags, and support for this transition factory will likely be removed at some point
+     * in the future (whenever automatic trimming is sufficiently workable).
      */
-    public Builder setTrimmingTransitionFactory(RuleTransitionFactory factory) {
-      Preconditions.checkState(
-          trimmingTransitionFactory == null, "Trimming transition factory already set");
-      trimmingTransitionFactory = Preconditions.checkNotNull(factory);
+    public Builder addTrimmingTransitionFactory(RuleTransitionFactory factory) {
+      if (trimmingTransitionFactory == null) {
+        trimmingTransitionFactory = Preconditions.checkNotNull(factory);
+      } else {
+        trimmingTransitionFactory = new ComposingRuleTransitionFactory(
+            trimmingTransitionFactory, Preconditions.checkNotNull(factory));
+      }
       return this;
     }
 
@@ -435,7 +441,7 @@
     @VisibleForTesting(/* for testing trimming transition factories without relying on prod use */)
     public Builder overrideTrimmingTransitionFactoryForTesting(RuleTransitionFactory factory) {
       trimmingTransitionFactory = null;
-      return this.setTrimmingTransitionFactory(factory);
+      return this.addTrimmingTransitionFactory(factory);
     }
 
     @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRules.java b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRules.java
index 6a5fdae..7531493 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRules.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/config/ConfigRules.java
@@ -32,7 +32,7 @@
 
   @Override
   public void init(ConfiguredRuleClassProvider.Builder builder) {
-    builder.setTrimmingTransitionFactory(
+    builder.addTrimmingTransitionFactory(
         new ConfigFeatureFlagTaggedTrimmingTransitionFactory(BaseRuleClasses.TAGGED_TRIMMING_ATTR));
     builder.addRuleDefinition(new ConfigRuleClasses.ConfigBaseRule());
     builder.addRuleDefinition(new ConfigRuleClasses.ConfigSettingRule());
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsWithTrimmedConfigurationsTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsWithTrimmedConfigurationsTest.java
index 3f5f37c..cc438ce 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsWithTrimmedConfigurationsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/ConfigurationsForTargetsWithTrimmedConfigurationsTest.java
@@ -310,6 +310,54 @@
   }
 
   @Test
+  public void trimmingTransitionsAreComposedInOrderOfAdding() throws Exception {
+    RuleTransitionFactory firstTrimmingTransitionFactory =
+        (rule) ->
+            new AddArgumentToTestArgsTransition(
+                "first trimming transition for " + rule.getLabel().toString());
+    RuleTransitionFactory secondTrimmingTransitionFactory =
+        (rule) ->
+            new AddArgumentToTestArgsTransition(
+                "second trimming transition for " + rule.getLabel().toString());
+    ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
+    TestRuleClassProvider.addStandardRules(builder);
+    builder.addRuleDefinition(TestAspects.BASE_RULE);
+    builder.addRuleDefinition(TEST_BASE_RULE);
+    builder.overrideTrimmingTransitionFactoryForTesting(firstTrimmingTransitionFactory);
+    builder.addTrimmingTransitionFactory(secondTrimmingTransitionFactory);
+    useRuleClassProvider(builder.build());
+    scratch.file(
+        "a/skylark.bzl",
+        "def _impl(ctx):",
+        "  return",
+        "skylark_rule = rule(",
+        "  implementation = _impl,",
+        "  attrs = {",
+        "    'deps': attr.label_list(),",
+        "    '_base': attr.label(default = '//a:base'),",
+        "  }",
+        ")");
+    scratch.file(
+        "a/BUILD",
+        "load(':skylark.bzl', 'skylark_rule')",
+        // ensure that all Skylark rules get the TestConfiguration fragment
+        "test_base(name = 'base')",
+        // skylark rules get trimmed
+        "skylark_rule(name = 'skylark_solo', deps = [':base'])");
+
+    ConfiguredTarget configuredTarget;
+    BuildConfiguration config;
+
+    configuredTarget = Iterables.getOnlyElement(update("//a:skylark_solo").getTargetsToBuild());
+    config = getConfiguration(configuredTarget);
+    assertThat(config.getFragment(TestConfiguration.class).getTestArguments())
+        .containsExactly(
+            "first trimming transition for //a:skylark_solo",
+            "second trimming transition for //a:skylark_solo")
+        .inOrder();
+  }
+
+  @Test
   public void testRuleClassTransition() throws Exception {
     setRulesAvailableInTests(TestAspects.BASE_RULE, TEST_BASE_RULE, ATTRIBUTE_TRANSITION_RULE,
         RULE_CLASS_TRANSITION_RULE);