Clean up Starlark transition processing.

The flags being read by a transition are by definition a subset of all flags, and typically are a much smaller subset, so let's process the smaller set instead of every flag in the configuration.

Work towards platform-based flags: #19409.

PiperOrigin-RevId: 587681561
Change-Id: I8b329ef3e9447db08a0cedb9bb1b5ef60fe6458f
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
index 27bcff1..3742742 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/starlark/FunctionTransitionUtil.java
@@ -14,17 +14,20 @@
 
 package com.google.devtools.build.lib.analysis.starlark;
 
+import static com.google.common.base.Predicates.not;
 import static com.google.common.collect.ImmutableSet.toImmutableSet;
 import static com.google.devtools.build.lib.analysis.config.StarlarkDefinedConfigTransition.COMMAND_LINE_OPTION_PREFIX;
 import static com.google.devtools.build.lib.analysis.config.transitions.ConfigurationTransition.PATCH_TRANSITION_KEY;
 import static java.util.stream.Collectors.joining;
 
 import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
 import com.google.common.base.VerifyException;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
+import com.google.common.collect.Sets.SetView;
 import com.google.common.collect.Streams;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.CoreOptions;
@@ -44,10 +47,10 @@
 import java.lang.reflect.Field;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Stream;
@@ -62,6 +65,10 @@
  */
 public final class FunctionTransitionUtil {
 
+  private static final Predicate<String> IS_NATIVE_OPTION =
+      setting -> setting.startsWith(COMMAND_LINE_OPTION_PREFIX);
+  private static final Predicate<String> IS_STARLARK_OPTION = not(IS_NATIVE_OPTION);
+
   /**
    * Figure out what build settings the given transition changes and apply those changes to the
    * incoming {@link BuildOptions}. For native options, this involves a preprocess step of
@@ -234,52 +241,63 @@
       throws ValidationException {
     ImmutableMap<String, String> inputsCanonicalizedToGiven =
         starlarkTransition.getInputsCanonicalizedToGiven();
-    LinkedHashSet<String> remainingInputs =
-        Sets.newLinkedHashSet(inputsCanonicalizedToGiven.keySet());
 
     ImmutableMap.Builder<String, Object> optionsBuilder = ImmutableMap.builder();
 
-    // Add native options
-    for (Map.Entry<String, OptionInfo> entry : optionInfoMap.entrySet()) {
-      String optionName = entry.getKey();
-      String optionKey = COMMAND_LINE_OPTION_PREFIX + optionName;
+    // Handle native options.
+    starlarkTransition.getInputsCanonicalizedToGiven().keySet().stream()
+        .filter(IS_NATIVE_OPTION)
+        .forEach(
+            setting -> {
+              Optional<Object> result = findNativeOptionValue(buildOptions, optionInfoMap, setting);
+              result.ifPresent(optionValue -> optionsBuilder.put(setting, optionValue));
+            });
 
-      if (!remainingInputs.remove(optionKey)) {
-        // This option was not present in inputs. Skip it.
-        continue;
-      }
-      OptionInfo optionInfo = entry.getValue();
+    // Handle starlark options.
+    starlarkTransition.getInputsCanonicalizedToGiven().keySet().stream()
+        .filter(IS_STARLARK_OPTION)
+        .forEach(
+            setting -> {
+              Object optionValue = findStarlarkOptionValue(buildOptions, setting);
+              // Convert the canonical form to the user requested form that they expect to see.
+              String userRequestedLabelForm = inputsCanonicalizedToGiven.get(setting);
+              optionsBuilder.put(userRequestedLabelForm, optionValue);
+            });
 
-      Field field = optionInfo.getDefinition().getField();
-      FragmentOptions options = buildOptions.get(optionInfo.getOptionClass());
-      try {
-        Object optionValue = field.get(options);
-        // convert nulls here b/c ImmutableMap bans null values
-        optionsBuilder.put(optionKey, optionValue == null ? Starlark.NONE : optionValue);
-      } catch (IllegalAccessException e) {
-        // These exceptions should not happen, but if they do, throw a RuntimeException.
-        throw new IllegalStateException(e);
-      }
-    }
-
-    // Add Starlark options
-    for (Map.Entry<Label, Object> starlarkOption : buildOptions.getStarlarkOptions().entrySet()) {
-      String canonicalLabelForm = starlarkOption.getKey().getUnambiguousCanonicalForm();
-      if (!remainingInputs.remove(canonicalLabelForm)) {
-        continue;
-      }
-      // Convert the canonical form to the user requested form that they expect to see
-      String userRequestedLabelForm = inputsCanonicalizedToGiven.get(canonicalLabelForm);
-      optionsBuilder.put(userRequestedLabelForm, starlarkOption.getValue());
-    }
-
+    ImmutableMap<String, Object> result = optionsBuilder.buildOrThrow();
+    SetView<String> remainingInputs =
+        Sets.difference(ImmutableSet.copyOf(inputsCanonicalizedToGiven.values()), result.keySet());
     if (!remainingInputs.isEmpty()) {
       throw ValidationException.format(
           "transition inputs [%s] do not correspond to valid settings",
           Joiner.on(", ").join(remainingInputs));
     }
 
-    return optionsBuilder.buildOrThrow();
+    return result;
+  }
+
+  private static Optional<Object> findNativeOptionValue(
+      BuildOptions buildOptions, Map<String, OptionInfo> optionInfoMap, String setting) {
+    setting = setting.substring(COMMAND_LINE_OPTION_PREFIX.length());
+    if (!optionInfoMap.containsKey(setting)) {
+      return Optional.empty();
+    }
+    OptionInfo optionInfo = optionInfoMap.get(setting);
+    Field field = optionInfo.getDefinition().getField();
+    FragmentOptions options = buildOptions.get(optionInfo.getOptionClass());
+    try {
+      Object optionValue = field.get(options);
+      // convert nulls here b/c ImmutableMap bans null values
+      return Optional.of(optionValue == null ? Starlark.NONE : optionValue);
+    } catch (IllegalAccessException e) {
+      // These exceptions should not happen, but if they do, throw a RuntimeException.
+      throw new IllegalStateException(e);
+    }
+  }
+
+  private static Object findStarlarkOptionValue(BuildOptions buildOptions, String setting) {
+    Label settingLabel = Label.parseCanonicalUnchecked(setting);
+    return buildOptions.getStarlarkOptions().get(settingLabel);
   }
 
   /**