Plumbs transition key data through to ConfiguredTargetAndData and makes getSplitPrerequisiteConfiguredTargetAndTargets use transition keys instead of reading CPU information from BuildOptions.

RELNOTES: None
PiperOrigin-RevId: 299425217
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/Dependency.java b/src/main/java/com/google/devtools/build/lib/analysis/Dependency.java
index 5ed5883..7dc403c 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/Dependency.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/Dependency.java
@@ -29,19 +29,20 @@
  * <p>The dep's configuration can be specified in one of two ways:
  *
  * <p>Explicit configurations: includes the target and the configuration of the dependency
- * configured target and any aspects that may be required, as well as the configurations for
- * these aspects.
+ * configured target and any aspects that may be required, as well as the configurations for these
+ * aspects and an optional transition key. {@link Dependency#getTransitionKey} provides some more
+ * context on transition keys.
  *
- * <p>Configuration transitions: includes the target and the desired configuration transitions
- * that should be applied to produce the dependency's configuration. It's the caller's
- * responsibility to construct an actual configuration out of that. A set of aspects is also
- * included; the caller must also construct configurations for each of these.
+ * <p>Configuration transitions: includes the target and the desired configuration transitions that
+ * should be applied to produce the dependency's configuration. It's the caller's responsibility to
+ * construct an actual configuration out of that. A set of aspects is also included; the caller must
+ * also construct configurations for each of these.
  *
  * <p>Note that the presence of an aspect here does not necessarily mean that it will be created.
  * They will be filtered based on the {@link TransitiveInfoProvider} instances their associated
- * configured targets have (we cannot do that here because the configured targets are not
- * available yet). No error or warning is reported in this case, because it is expected that rules
- * sometimes over-approximate the providers they supply in their definitions.
+ * configured targets have (we cannot do that here because the configured targets are not available
+ * yet). No error or warning is reported in this case, because it is expected that rules sometimes
+ * over-approximate the providers they supply in their definitions.
  */
 public abstract class Dependency {
 
@@ -62,9 +63,11 @@
    */
   public static Dependency withConfiguration(Label label, BuildConfiguration configuration) {
     return new ExplicitConfigurationDependency(
-        label, configuration,
+        label,
+        configuration,
         AspectCollection.EMPTY,
-        ImmutableMap.<AspectDescriptor, BuildConfiguration>of());
+        ImmutableMap.<AspectDescriptor, BuildConfiguration>of(),
+        null);
   }
 
   /**
@@ -80,8 +83,30 @@
     for (AspectDescriptor aspect : aspects.getAllAspects()) {
       aspectBuilder.put(aspect, configuration);
     }
-    return new ExplicitConfigurationDependency(label, configuration, aspects,
-        aspectBuilder.build());
+    return new ExplicitConfigurationDependency(
+        label, configuration, aspects, aspectBuilder.build(), null);
+  }
+
+  /**
+   * Creates a new {@link Dependency} with the given configuration, aspects, and transition key. The
+   * configuration is also applied to all aspects. This should be preferred over {@link
+   * Dependency#withConfigurationAndAspects} if the {@code configuration} was derived from a {@link
+   * ConfigurationTransition}, and so there is a corresponding transition key.
+   *
+   * <p>The configuration and aspects must not be {@code null}.
+   */
+  public static Dependency withConfigurationAspectsAndTransitionKey(
+      Label label,
+      BuildConfiguration configuration,
+      AspectCollection aspects,
+      @Nullable String transitionKey) {
+    ImmutableMap.Builder<AspectDescriptor, BuildConfiguration> aspectBuilder =
+        new ImmutableMap.Builder<>();
+    for (AspectDescriptor aspect : aspects.getAllAspects()) {
+      aspectBuilder.put(aspect, configuration);
+    }
+    return new ExplicitConfigurationDependency(
+        label, configuration, aspects, aspectBuilder.build(), transitionKey);
   }
 
   /**
@@ -96,7 +121,7 @@
       AspectCollection aspects,
       Map<AspectDescriptor, BuildConfiguration> aspectConfigurations) {
     return new ExplicitConfigurationDependency(
-        label, configuration, aspects, ImmutableMap.copyOf(aspectConfigurations));
+        label, configuration, aspects, ImmutableMap.copyOf(aspectConfigurations), null);
   }
 
   /**
@@ -159,6 +184,14 @@
   public abstract BuildConfiguration getAspectConfiguration(AspectDescriptor aspect);
 
   /**
+   * Returns the key of a configuration transition, if exists, associated with this dependency. See
+   * {@link ConfigurationTransition#apply} for more details.
+   *
+   * @throws IllegalStateException if {@link #hasExplicitConfiguration()} returns false.
+   */
+  public abstract String getTransitionKey();
+
+  /**
    * Implementation of a dependency with no configuration, suitable for, e.g., source files or
    * visibility.
    */
@@ -194,6 +227,12 @@
       return null;
     }
 
+    @Nullable
+    @Override
+    public String getTransitionKey() {
+      return null;
+    }
+
     @Override
     public int hashCode() {
       return label.hashCode();
@@ -221,15 +260,19 @@
     private final BuildConfiguration configuration;
     private final AspectCollection aspects;
     private final ImmutableMap<AspectDescriptor, BuildConfiguration> aspectConfigurations;
+    @Nullable private final String transitionKey;
 
     public ExplicitConfigurationDependency(
-        Label label, BuildConfiguration configuration,
+        Label label,
+        BuildConfiguration configuration,
         AspectCollection aspects,
-        ImmutableMap<AspectDescriptor, BuildConfiguration> aspectConfigurations) {
+        ImmutableMap<AspectDescriptor, BuildConfiguration> aspectConfigurations,
+        @Nullable String transitionKey) {
       super(label);
       this.configuration = Preconditions.checkNotNull(configuration);
       this.aspects = Preconditions.checkNotNull(aspects);
       this.aspectConfigurations = Preconditions.checkNotNull(aspectConfigurations);
+      this.transitionKey = transitionKey;
     }
 
     @Override
@@ -258,9 +301,15 @@
       return aspectConfigurations.get(aspect);
     }
 
+    @Nullable
+    @Override
+    public String getTransitionKey() {
+      return transitionKey;
+    }
+
     @Override
     public int hashCode() {
-      return Objects.hash(label, configuration, aspectConfigurations);
+      return Objects.hash(label, configuration, aspectConfigurations, transitionKey);
     }
 
     @Override
@@ -325,6 +374,12 @@
     }
 
     @Override
+    public String getTransitionKey() {
+      throw new IllegalStateException(
+          "This dependency has a transition, not an explicit configuration.");
+    }
+
+    @Override
     public int hashCode() {
       return Objects.hash(label, transition, aspects);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
index b1b13d0..65f99ac 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleContext.java
@@ -49,7 +49,6 @@
 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.ConfigMatchingProvider;
-import com.google.devtools.build.lib.analysis.config.CoreOptions;
 import com.google.devtools.build.lib.analysis.config.FragmentCollection;
 import com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition;
 import com.google.devtools.build.lib.analysis.config.transitions.NoTransition;
@@ -856,7 +855,7 @@
   }
 
   /**
-   * Returns the prerequisites keyed by the CPU of their configurations. If the split transition
+   * Returns the prerequisites keyed by their configuration transition keys. If the split transition
    * is not active (e.g. split() returned an empty list), the key is an empty Optional.
    */
   public Map<Optional<String>, ? extends List<? extends TransitiveInfoCollection>>
@@ -891,8 +890,8 @@
   }
 
   /**
-   * Returns the prerequisites keyed by the CPU of their configurations. If the split transition is
-   * not active (e.g. split() returned an empty list), the key is an empty Optional.
+   * Returns the prerequisites keyed by their transition keys. If the split transition is not active
+   * (e.g. split() returned an empty list), the key is an empty Optional.
    */
   public Map<Optional<String>, List<ConfiguredTargetAndData>>
       getSplitPrerequisiteConfiguredTargetAndTargets(String attributeName) {
@@ -913,28 +912,23 @@
     List<ConfiguredTargetAndData> deps = getConfiguredTargetAndTargetDeps(attributeName);
 
     if (SplitTransition.equals(fromOptions, splitOptions.values())) {
-      // The split transition is not active. Defer the decision on which CPU to use.
+      // The split transition is not active.
       return ImmutableMap.of(Optional.<String>absent(), deps);
     }
 
-    Set<String> cpus = new HashSet<>();
-    for (BuildOptions options : splitOptions.values()) {
-      // This method should only be called when the split config is enabled on the command line, in
-      // which case this cpu can't be null.
-      cpus.add(options.get(CoreOptions.class).cpu);
-    }
-
     // Use an ImmutableListMultimap.Builder here to preserve ordering.
     ImmutableListMultimap.Builder<Optional<String>, ConfiguredTargetAndData> result =
         ImmutableListMultimap.builder();
     for (ConfiguredTargetAndData t : deps) {
-      if (t.getConfiguration() != null) {
-        result.put(Optional.of(t.getConfiguration().getCpu()), t);
-      } else {
-        // Source files don't have a configuration, so we add them to all architecture entries.
-        for (String cpu : cpus) {
-          result.put(Optional.of(cpu), t);
+      if (t.getTransitionKey() == null
+          || t.getTransitionKey().equals(ConfigurationTransition.PATCH_TRANSITION_KEY)) {
+        // The target doesn't have a specific transition key associated. This likely means it's a
+        // non-configurable target, e.g. files, package groups. Pan out to all available keys.
+        for (String key : splitOptions.keySet()) {
+          result.put(Optional.of(key), t);
         }
+      } else {
+        result.put(Optional.of(t.getTransitionKey()), t);
       }
     }
     return Multimaps.asMap(result.build());
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java
index b8732d9..b1303d9 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/ConfigurationResolver.java
@@ -53,6 +53,7 @@
 import com.google.devtools.build.lib.skyframe.TransitiveTargetKey;
 import com.google.devtools.build.lib.skyframe.TransitiveTargetValue;
 import com.google.devtools.build.lib.util.OrderedSetMultimap;
+import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
@@ -121,9 +122,10 @@
       throws ConfiguredTargetFunction.DependencyEvaluationException, InterruptedException {
 
     // Maps each Skyframe-evaluated BuildConfiguration to the dependencies that need that
-    // configuration. For cases where Skyframe isn't needed to get the configuration (e.g. when
-    // we just re-used the original rule's configuration), we should skip this outright.
-    Multimap<SkyKey, Map.Entry<DependencyKind, Dependency>> keysToEntries =
+    // configuration paired with a transition key corresponding to the BuildConfiguration. For cases
+    // where Skyframe isn't needed to get the configuration (e.g. when we just re-used the original
+    // rule's configuration), we should skip this outright.
+    Multimap<SkyKey, Pair<Map.Entry<DependencyKind, Dependency>, String>> keysToEntries =
         LinkedListMultimap.create();
 
     // Stores the result of applying a transition to the current configuration using a
@@ -282,8 +284,8 @@
         putOnlyEntry(
             resolvedDeps,
             dependencyEdge,
-            Dependency.withConfigurationAndAspects(
-                dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects()));
+            Dependency.withConfigurationAspectsAndTransitionKey(
+                dep.getLabel(), ctgValue.getConfiguration(), dep.getAspects(), null));
         continue;
       }
 
@@ -297,15 +299,16 @@
       }
 
       try {
-        for (BuildOptions options : toOptions.values()) {
+        for (Map.Entry<String, BuildOptions> optionsEntry : toOptions.entrySet()) {
           if (sameFragments) {
             keysToEntries.put(
                 BuildConfigurationValue.keyWithPlatformMapping(
                     platformMappingValue,
                     defaultBuildOptions,
                     currentConfiguration.fragmentClasses(),
-                    BuildOptions.diffForReconstruction(defaultBuildOptions, options)),
-                depsEntry);
+                    BuildOptions.diffForReconstruction(
+                        defaultBuildOptions, optionsEntry.getValue())),
+                new Pair<>(depsEntry, optionsEntry.getKey()));
 
           } else {
             keysToEntries.put(
@@ -313,8 +316,9 @@
                     platformMappingValue,
                     defaultBuildOptions,
                     depFragments,
-                    BuildOptions.diffForReconstruction(defaultBuildOptions, options)),
-                depsEntry);
+                    BuildOptions.diffForReconstruction(
+                        defaultBuildOptions, optionsEntry.getValue())),
+                new Pair<>(depsEntry, optionsEntry.getKey()));
           }
         }
       } catch (OptionsParsingException e) {
@@ -355,8 +359,8 @@
         }
         BuildConfiguration trimmedConfig =
             ((BuildConfigurationValue) valueOrException.get()).getConfiguration();
-        for (Map.Entry<DependencyKind, Dependency> info : keysToEntries.get(key)) {
-          Dependency originalDep = info.getValue();
+        for (Pair<Map.Entry<DependencyKind, Dependency>, String> info : keysToEntries.get(key)) {
+          Dependency originalDep = info.first.getValue();
           if (trimmedConfig.trimConfigurationsRetroactively()
               && !originalDep.getAspects().isEmpty()) {
             String message =
@@ -368,10 +372,10 @@
             throw new ConfiguredTargetFunction.DependencyEvaluationException(
                 new InvalidConfigurationException(message));
           }
-          DependencyEdge attr = new DependencyEdge(info.getKey(), originalDep.getLabel());
+          DependencyEdge attr = new DependencyEdge(info.first.getKey(), originalDep.getLabel());
           Dependency resolvedDep =
-              Dependency.withConfigurationAndAspects(
-                  originalDep.getLabel(), trimmedConfig, originalDep.getAspects());
+              Dependency.withConfigurationAspectsAndTransitionKey(
+                  originalDep.getLabel(), trimmedConfig, originalDep.getAspects(), info.second);
           Attribute attribute = attr.dependencyKind.getAttribute();
           if (attribute != null && attribute.getTransitionFactory().isSplit()) {
             resolvedDeps.put(attr, resolvedDep);