Do not request `PlatformMappingValue` if `--platforms` refers to a `platform` rule with non-empty flags.

When `--platforms` refers to a `platform` rule with non-empty flags, the `PlatformMappingValue` is irrelevant. Avoid requesting it to minimize Skyframe edges.

PiperOrigin-RevId: 672531227
Change-Id: I448cc110e588aa8b2d06ee55473b204b54b120c6
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/producers/BuildConfigurationKeyProducer.java b/src/main/java/com/google/devtools/build/lib/analysis/producers/BuildConfigurationKeyProducer.java
index 36e40b8..c27f116 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/producers/BuildConfigurationKeyProducer.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/producers/BuildConfigurationKeyProducer.java
@@ -32,14 +32,26 @@
 import javax.annotation.Nullable;
 
 /**
- * Creates the needed {@link BuildConfigurationKey} instances for a single {@link BuildOptions}.
+ * Creates the needed {@link BuildConfigurationKey} instance for a single {@link BuildOptions},
+ * including merging in any platform-based flags or a platform mapping.
  *
- * <p>This includes merging in platform mappings and platform-based flags.
+ * <p>Platform-based flags and platform mappings are mutually exclusive: only one will be applied if
+ * they are present. Trying to mix and match would be possible but confusing, especially if they try
+ * to change the same flag. The logic is:
+ *
+ * <ul>
+ *   <li>If {@link PlatformOptions#platforms} specifies a target platform, look up the {@link
+ *       PlatformValue}. If it specifies {@linkplain PlatformValue#parsedFlags flags}, use {@link
+ *       ParsedFlagsValue#mergeWith}.
+ *   <li>If {@link PlatformOptions#platforms} does not specify a target platform, or if the target
+ *       platform does not specify {@linkplain PlatformValue#parsedFlags flags}, look up the {@link
+ *       PlatformMappingValue} and use {@link PlatformMappingValue#map}.
+ * </ul>
  *
  * @param <C> The type of the context variable that the producer will pass via the {@link
  *     ResultSink} so that consumers can identify which options are which.
  */
-public class BuildConfigurationKeyProducer<C>
+public final class BuildConfigurationKeyProducer<C>
     implements StateMachine,
         ValueOrExceptionSink<PlatformMappingException>,
         PlatformProducer.ResultSink {
@@ -64,8 +76,8 @@
   private final BuildOptions options;
 
   // -------------------- Internal State --------------------
+  private PlatformValue targetPlatformValue;
   private PlatformMappingValue platformMappingValue;
-  private Optional<ParsedFlagsValue> platformFlags = Optional.empty();
 
   BuildConfigurationKeyProducer(
       ResultSink<C> sink,
@@ -82,28 +94,62 @@
 
   @Override
   public StateMachine step(Tasks tasks) throws InterruptedException {
-    BuildConfigurationKey result = cache.get(this.options);
+    BuildConfigurationKey result = cache.get(options);
     if (result != null) {
-      this.sink.acceptTransitionedConfiguration(this.context, result);
-      return this.runAfter;
+      sink.acceptTransitionedConfiguration(context, result);
+      return runAfter;
     }
 
     // Short-circuit if there are no platform options.
-    if (!this.options.contains(PlatformOptions.class)) {
-      return finishConfigurationKeyProcessing(BuildConfigurationKey.create(this.options));
+    if (!options.contains(PlatformOptions.class)) {
+      return finishConfigurationKeyProcessing(BuildConfigurationKey.create(options));
     }
 
-    // Find platform mappings and platform-based flags for merging.
-    findPlatformMapping(tasks);
-    findTargetPlatformInfo(tasks);
-    return this::applyFlags;
+    List<Label> targetPlatforms = options.get(PlatformOptions.class).platforms;
+    if (targetPlatforms.size() == 1) {
+      // TODO: https://github.com/bazelbuild/bazel/issues/19807 - We define this flag to only use
+      //  the first value and ignore any subsequent ones. Remove this check as part of cleanup.
+      tasks.enqueue(
+          new PlatformProducer(targetPlatforms.getFirst(), this, this::checkTargetPlatformFlags));
+      return runAfter;
+    } else {
+      return mergeFromPlatformMapping(tasks);
+    }
   }
 
-  private void findPlatformMapping(Tasks tasks) {
+  private StateMachine checkTargetPlatformFlags(Tasks tasks) {
+    if (targetPlatformValue == null) {
+      return DONE; // Error.
+    }
+    Optional<ParsedFlagsValue> parsedFlags = targetPlatformValue.parsedFlags();
+    if (parsedFlags.isPresent()) {
+      BuildOptions updatedOptions = parsedFlags.get().mergeWith(options);
+      return finishConfigurationKeyProcessing(BuildConfigurationKey.create(updatedOptions));
+    } else {
+      return mergeFromPlatformMapping(tasks);
+    }
+  }
+
+  private StateMachine mergeFromPlatformMapping(Tasks tasks) {
     PathFragment platformMappingsPath = options.get(PlatformOptions.class).platformMappings;
-    PlatformMappingValue.Key platformMappingValueKey =
-        PlatformMappingValue.Key.create(platformMappingsPath);
-    tasks.lookUp(platformMappingValueKey, PlatformMappingException.class, this);
+    tasks.lookUp(
+        PlatformMappingValue.Key.create(platformMappingsPath),
+        PlatformMappingException.class,
+        this);
+    return this::applyPlatformMapping;
+  }
+
+  private StateMachine applyPlatformMapping(Tasks tasks) {
+    if (platformMappingValue == null) {
+      return DONE; // Error.
+    }
+    try {
+      BuildOptions updatedOptions = platformMappingValue.map(options);
+      return finishConfigurationKeyProcessing(BuildConfigurationKey.create(updatedOptions));
+    } catch (OptionsParsingException e) {
+      sink.acceptOptionsParsingError(e);
+      return runAfter;
+    }
   }
 
   // Handles results from the PlatformMappingValueKey lookup.
@@ -119,23 +165,14 @@
 
     if (exception != null) {
       sink.acceptPlatformMappingError(exception);
-    } else if (value instanceof PlatformMappingValue platformMappingValue) {
-      this.platformMappingValue = platformMappingValue;
-    }
-  }
-
-  private void findTargetPlatformInfo(Tasks tasks) {
-    List<Label> targetPlatforms = options.get(PlatformOptions.class).platforms;
-    if (targetPlatforms.size() == 1) {
-      // TODO: https://github.com/bazelbuild/bazel/issues/19807 - We define this flag to only use
-      // the first value and ignore any subsequent ones. Remove this check as part of cleanup.
-      tasks.enqueue(new PlatformProducer(targetPlatforms.getFirst(), this, StateMachine.DONE));
+    } else {
+      this.platformMappingValue = (PlatformMappingValue) value;
     }
   }
 
   @Override
   public void acceptPlatformValue(PlatformValue value) {
-    this.platformFlags = value.parsedFlags();
+    this.targetPlatformValue = value;
   }
 
   @Override
@@ -148,49 +185,9 @@
     sink.acceptOptionsParsingError(error);
   }
 
-  private StateMachine applyFlags(Tasks tasks) {
-    if (this.platformMappingValue == null) {
-      return DONE; // There was an error.
-    }
-
-    try {
-      BuildConfigurationKey newConfigurationKey = applyFlagsForOptions(options);
-      return finishConfigurationKeyProcessing(newConfigurationKey);
-    } catch (OptionsParsingException e) {
-      sink.acceptOptionsParsingError(e);
-      return runAfter;
-    }
-  }
-
   private StateMachine finishConfigurationKeyProcessing(BuildConfigurationKey newConfigurationKey) {
     cache.put(this.options, newConfigurationKey);
     sink.acceptTransitionedConfiguration(this.context, newConfigurationKey);
     return this.runAfter;
   }
-
-  /**
-   * Apply discovered flags from platforms or platform mappings to the given options, and return the
-   * {@link BuildConfigurationKey}.
-   *
-   * <p>Platform-based flags and platform mappings are mutually exclusive: only one will be applied
-   * if they are present. Trying to mix and match would be possible but confusing, especially if
-   * they try to change the same flag.
-   */
-  private BuildConfigurationKey applyFlagsForOptions(BuildOptions options)
-      throws OptionsParsingException {
-    // Does the target platform provide any flags?
-    if (this.platformFlags.isPresent()) {
-      BuildOptions updatedOptions = this.platformFlags.get().mergeWith(options);
-      return BuildConfigurationKey.create(updatedOptions);
-    }
-
-    // Is there a platform mapping?
-    if (this.platformMappingValue != null) {
-      BuildOptions mappedOptions = this.platformMappingValue.map(options);
-      return BuildConfigurationKey.create(mappedOptions);
-    }
-
-    // Just use the original options.
-    return BuildConfigurationKey.create(options);
-  }
 }