Loop in top-level transition logic to configurable query to give more accurate results. Some refactoring done to get custom query code out of BuildView and into resolvers.

PiperOrigin-RevId: 172647838
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
index 02a6dfd..950e21a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/BuildView.java
@@ -40,9 +40,8 @@
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
-import com.google.devtools.build.lib.analysis.config.ComposingSplitTransition;
 import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
-import com.google.devtools.build.lib.analysis.config.DynamicTransitionMapper;
+import com.google.devtools.build.lib.analysis.config.ConfigurationResolver;
 import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
 import com.google.devtools.build.lib.analysis.config.PatchTransition;
 import com.google.devtools.build.lib.analysis.constraints.TopLevelConstraintSemantics;
@@ -80,7 +79,6 @@
 import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey;
 import com.google.devtools.build.lib.skyframe.AspectValue.AspectValueKey;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
-import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue;
 import com.google.devtools.build.lib.skyframe.CoverageReportValue;
 import com.google.devtools.build.lib.skyframe.SkyframeAnalysisResult;
 import com.google.devtools.build.lib.skyframe.SkyframeBuildView;
@@ -495,7 +493,8 @@
 
     // Determine the configurations.
     List<TargetAndConfiguration> topLevelTargetsWithConfigs =
-        nodesForTopLevelTargets(configurations, targets, eventHandler);
+        AnalysisUtils.getTargetsWithConfigs(
+            configurations, targets, eventHandler, ruleClassProvider, skyframeExecutor);
 
     // Report the generated association of targets to configurations
     Multimap<Label, BuildConfiguration> byLabel =
@@ -864,139 +863,6 @@
   }
 
   /**
-   * Given a set of top-level targets and a configuration collection, returns the appropriate
-   * <Target, Configuration> pair for each target.
-   *
-   * <p>Preserves the original input ordering.
-   */
-  private List<TargetAndConfiguration> nodesForTopLevelTargets(
-      BuildConfigurationCollection configurations,
-      Collection<Target> targets,
-      ExtendedEventHandler eventHandler)
-      throws InterruptedException {
-    // We use a hash set here to remove duplicate nodes; this can happen for input files and package
-    // groups.
-    LinkedHashSet<TargetAndConfiguration> nodes = new LinkedHashSet<>(targets.size());
-    for (BuildConfiguration config : configurations.getTargetConfigurations()) {
-      for (Target target : targets) {
-        nodes.add(new TargetAndConfiguration(target, target.isConfigurable() ? config : null));
-      }
-    }
-    return ImmutableList.copyOf(getConfigurations(nodes, eventHandler));
-  }
-
-  /**
-   * If {@link BuildConfiguration.Options#trimConfigurations()} is true, transforms a collection of
-   * <Target, Configuration> pairs by trimming each target's configuration to only the fragments the
-   * target and its transitive dependencies need.
-   *
-   * <p>Else returns configurations that unconditionally include all fragments.
-   *
-   * <p>Preserves the original input order (but merges duplicate nodes that might occur due to
-   * top-level configuration transitions) . Uses original (untrimmed) configurations for targets
-   * that can't be evaluated (e.g. due to loading phase errors).
-   *
-   * <p>This is suitable for feeding {@link ConfiguredTargetValue} keys: as general principle {@link
-   * ConfiguredTarget}s should have exactly as much information in their configurations as they need
-   * to evaluate and no more (e.g. there's no need for Android settings in a C++ configured target).
-   */
-  // TODO(bazel-team): error out early for targets that fail - untrimmed configurations should
-  // never make it through analysis (and especially not seed ConfiguredTargetValues)
-  private LinkedHashSet<TargetAndConfiguration> getConfigurations(
-      Iterable<TargetAndConfiguration> inputs, ExtendedEventHandler eventHandler)
-      throws InterruptedException {
-    Map<Label, Target> labelsToTargets = new LinkedHashMap<>();
-    // We'll get the configs from SkyframeExecutor#getConfigurations, which gets configurations
-    // for deps including transitions. So to satisfy its API we repackage each target as a
-    // Dependency with a NONE transition.
-    Multimap<BuildConfiguration, Dependency> asDeps =
-        ArrayListMultimap.<BuildConfiguration, Dependency>create();
-
-    for (TargetAndConfiguration targetAndConfig : inputs) {
-      labelsToTargets.put(targetAndConfig.getLabel(), targetAndConfig.getTarget());
-      if (targetAndConfig.getConfiguration() != null) {
-        asDeps.put(targetAndConfig.getConfiguration(),
-            Dependency.withTransitionAndAspects(
-                targetAndConfig.getLabel(),
-                getTopLevelTransition(targetAndConfig,
-                    ruleClassProvider.getDynamicTransitionMapper()),
-                // TODO(bazel-team): support top-level aspects
-                AspectCollection.EMPTY));
-      }
-    }
-
-    // Maps <target, originalConfig> pairs to <target, finalConfig> pairs for targets that
-    // could be successfully Skyframe-evaluated.
-    Map<TargetAndConfiguration, TargetAndConfiguration> successfullyEvaluatedTargets =
-        new LinkedHashMap<>();
-    if (!asDeps.isEmpty()) {
-      for (BuildConfiguration fromConfig : asDeps.keySet()) {
-        Multimap<Dependency, BuildConfiguration> trimmedTargets =
-            skyframeExecutor.getConfigurations(eventHandler, fromConfig.getOptions(),
-                asDeps.get(fromConfig));
-        for (Map.Entry<Dependency, BuildConfiguration> trimmedTarget : trimmedTargets.entries()) {
-          Target target = labelsToTargets.get(trimmedTarget.getKey().getLabel());
-          successfullyEvaluatedTargets.put(
-              new TargetAndConfiguration(target, fromConfig),
-              new TargetAndConfiguration(target, trimmedTarget.getValue()));
-        }
-      }
-    }
-
-    LinkedHashSet<TargetAndConfiguration> result = new LinkedHashSet<>();
-    for (TargetAndConfiguration originalInput : inputs) {
-      if (successfullyEvaluatedTargets.containsKey(originalInput)) {
-        // The configuration was successfully trimmed.
-        result.add(successfullyEvaluatedTargets.get(originalInput));
-      } else {
-        // Either the configuration couldn't be determined (e.g. loading phase error) or it's null.
-        result.add(originalInput);
-      }
-    }
-    return result;
-  }
-
-  /**
-   * Returns the transition to apply to the top-level configuration before applying it to this
-   * target. This enables support for rule-triggered top-level configuration hooks.
-   */
-  private static Attribute.Transition getTopLevelTransition(TargetAndConfiguration targetAndConfig,
-      DynamicTransitionMapper dynamicTransitionMapper) {
-    Target target = targetAndConfig.getTarget();
-    BuildConfiguration fromConfig = targetAndConfig.getConfiguration();
-
-    // Top-level transitions (chosen by configuration fragments):
-    Transition topLevelTransition = fromConfig.topLevelConfigurationHook(target);
-    if (topLevelTransition == null) {
-      topLevelTransition = ConfigurationTransition.NONE;
-    }
-
-    // Rule class transitions (chosen by rule class definitions):
-    if (target.getAssociatedRule() == null) {
-      return topLevelTransition;
-    }
-    Rule associatedRule = target.getAssociatedRule();
-    RuleTransitionFactory transitionFactory =
-        associatedRule.getRuleClassObject().getTransitionFactory();
-    if (transitionFactory == null) {
-      return topLevelTransition;
-    }
-    // dynamicTransitionMapper is only needed because of Attribute.ConfigurationTransition.DATA:
-    // this is C++-specific but non-C++ rules declare it. So they can't directly provide the
-    // C++-specific patch transition that implements it.
-    PatchTransition ruleClassTransition = (PatchTransition)
-        dynamicTransitionMapper.map(transitionFactory.buildTransitionFor(associatedRule));
-    if (ruleClassTransition == null) {
-      return topLevelTransition;
-    } else if (topLevelTransition == ConfigurationTransition.NONE) {
-      return ruleClassTransition;
-    } else {
-      return new ComposingSplitTransition(topLevelTransition, ruleClassTransition);
-    }
-  }
-
-
-  /**
    * Gets a configuration for the given target.
    *
    * <p>If {@link BuildConfiguration.Options#trimConfigurations()} is true, the configuration only
@@ -1007,9 +873,16 @@
   public BuildConfiguration getConfigurationForTesting(
       Target target, BuildConfiguration config, ExtendedEventHandler eventHandler)
       throws InterruptedException {
-    return Iterables.getOnlyElement(getConfigurations(
-        ImmutableList.<TargetAndConfiguration>of(new TargetAndConfiguration(target, config)),
-        eventHandler)).getConfiguration();
+    List<TargetAndConfiguration> node =
+        ImmutableList.<TargetAndConfiguration>of(new TargetAndConfiguration(target, config));
+    LinkedHashSet<TargetAndConfiguration> configs =
+        ConfigurationResolver.getConfigurationsFromExecutor(
+            node,
+            AnalysisUtils.targetsToDeps(
+                new LinkedHashSet<TargetAndConfiguration>(node), ruleClassProvider),
+            eventHandler,
+            skyframeExecutor);
+    return configs.iterator().next().getConfiguration();
   }
 
   /**