Deprecate TransitiveInfoCollection#getConfiguration(), adding two new methods, TransitiveInfoCollection#getConfigurationKey() and ConfiguredTarget#getConfigurationChecksum(). These methods currently delegate to #getConfiguration(), but in the future they won't. I hope to get rid of #getConfigurationChecksum(), but I may have to fold the checksum into BuildConfigurationValue.Key or leave it as a separate field in ConfiguredTarget.

Transform a representative (random?) selection of #getConfiguration calls, to show that it's pretty much possible everywhere.

PiperOrigin-RevId: 190474978
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 deaddcb..cd94353 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
@@ -81,6 +81,7 @@
 import com.google.devtools.build.lib.skyframe.AspectValue;
 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.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
 import com.google.devtools.build.lib.skyframe.CoverageReportValue;
@@ -613,7 +614,10 @@
     }
 
     Set<ConfiguredTarget> targetsToSkip =
-        new TopLevelConstraintSemantics(skyframeExecutor.getPackageManager(), eventHandler)
+        new TopLevelConstraintSemantics(
+                skyframeExecutor.getPackageManager(),
+                input -> skyframeExecutor.getConfiguration(eventHandler, input),
+                eventHandler)
             .checkTargetEnvironmentRestrictions(skyframeAnalysisResult.getConfiguredTargets());
 
     AnalysisResult result =
@@ -975,7 +979,7 @@
           throws EvalException, InvalidConfigurationException, InterruptedException,
               InconsistentAspectOrderException {
     return getConfiguredTargetAndDataDirectPrerequisitesForTesting(
-        eventHandler, ct, ct.getConfiguration(), configurations);
+        eventHandler, ct, ct.getConfigurationKey(), configurations);
   }
 
   @VisibleForTesting
@@ -987,14 +991,17 @@
           throws EvalException, InvalidConfigurationException, InterruptedException,
               InconsistentAspectOrderException {
     return getConfiguredTargetAndDataDirectPrerequisitesForTesting(
-        eventHandler, ct.getConfiguredTarget(), ct.getConfiguration(), configurations);
+        eventHandler,
+        ct.getConfiguredTarget(),
+        ct.getConfiguredTarget().getConfigurationKey(),
+        configurations);
   }
 
   private Collection<ConfiguredTargetAndData>
       getConfiguredTargetAndDataDirectPrerequisitesForTesting(
           ExtendedEventHandler eventHandler,
           ConfiguredTarget ct,
-          BuildConfiguration configuration,
+          BuildConfigurationValue.Key configuration,
           BuildConfigurationCollection configurations)
           throws EvalException, InvalidConfigurationException, InterruptedException,
               InconsistentAspectOrderException {
@@ -1066,7 +1073,7 @@
           Iterable<BuildOptions> buildOptions,
           BuildOptions defaultBuildOptions) {
         Preconditions.checkArgument(
-            ct.getConfiguration().fragmentClasses().equals(fragments),
+            fragments.fragmentClasses().equals(ct.getConfigurationKey().getFragments()),
             "Mismatch: %s %s",
             ct,
             fragments);
@@ -1085,7 +1092,9 @@
     }
 
     DependencyResolver dependencyResolver = new SilentDependencyResolver();
-    TargetAndConfiguration ctgNode = new TargetAndConfiguration(target, ct.getConfiguration());
+    TargetAndConfiguration ctgNode =
+        new TargetAndConfiguration(
+            target, skyframeExecutor.getConfiguration(eventHandler, ct.getConfigurationKey()));
     return dependencyResolver.dependentNodeMap(
         ctgNode,
         configurations.getHostConfiguration(),
@@ -1135,7 +1144,7 @@
 
     ImmutableMultimap<Dependency, ConfiguredTargetAndData> cts =
         skyframeExecutor.getConfiguredTargetMapForTesting(
-            eventHandler, target.getConfiguration(), ImmutableSet.copyOf(depNodeNames.values()));
+            eventHandler, target.getConfigurationKey(), ImmutableSet.copyOf(depNodeNames.values()));
 
     OrderedSetMultimap<Attribute, ConfiguredTargetAndData> result = OrderedSetMultimap.create();
     for (Map.Entry<Attribute, Dependency> entry : depNodeNames.entries()) {
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTarget.java
index b44dfd0..c7c0866 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredTarget.java
@@ -55,6 +55,10 @@
   @Nullable
   BuildConfiguration getConfiguration();
 
+  default String getConfigurationChecksum() {
+    return getConfiguration().checksum();
+  }
+
   /**
    * Returns keys for a legacy Skylark provider.
    *
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
index 0727e22..a2e3734 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/TransitiveInfoCollection.java
@@ -16,11 +16,14 @@
 
 import com.google.devtools.build.lib.actions.Artifact;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.configuredtargets.InputFileConfiguredTarget;
+import com.google.devtools.build.lib.analysis.configuredtargets.PackageGroupConfiguredTarget;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.packages.RequiredProviders;
+import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
@@ -94,13 +97,25 @@
    */
   Label getLabel();
 
+  /** Deprecated! Use {@link #getConfigurationKey} instead. */
+  @Deprecated
+  @Nullable
+  BuildConfiguration getConfiguration();
+
   /**
-   * <p>Returns the {@link BuildConfiguration} for which this transitive info collection is defined.
-   * Configuration is defined for all configured targets with exception of {@link
-   * InputFileConfiguredTarget} and {@link PackageGroupConfiguredTarget} for which it is always
-   * <b>null</b>.</p>
+   * Returns the {@link BuildConfigurationValue.Key} naming the {@link BuildConfiguration} for which
+   * this transitive info collection is defined. Configuration is defined for all configured targets
+   * with exception of {@link InputFileConfiguredTarget} and {@link PackageGroupConfiguredTarget}
+   * for which it is always <b>null</b>.
    */
-  @Nullable BuildConfiguration getConfiguration();
+  @Nullable
+  default BuildConfigurationValue.Key getConfigurationKey() {
+    BuildConfiguration configuration = getConfiguration();
+    return configuration == null
+        ? null
+        : BuildConfigurationValue.key(
+            configuration.fragmentClasses(), configuration.getBuildOptionsDiff());
+  }
 
   /**
    * Checks whether this {@link TransitiveInfoCollection} satisfies given {@link RequiredProviders}.
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
index 825ba5a..9bd4119 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/AbstractConfiguredTarget.java
@@ -89,7 +89,7 @@
 
   @Override
   public String toString() {
-    return "ConfiguredTarget(" + getLabel() + ", " + getConfiguration() + ")";
+    return "ConfiguredTarget(" + getLabel() + ", " + getConfigurationChecksum() + ")";
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/InputFileConfiguredTarget.java b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/InputFileConfiguredTarget.java
index 42397c0..0cae3d3 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/InputFileConfiguredTarget.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/configuredtargets/InputFileConfiguredTarget.java
@@ -54,7 +54,7 @@
   public InputFileConfiguredTarget(
       TargetContext targetContext, InputFile inputFile, Artifact artifact) {
     this(inputFile.getLabel(), targetContext.getVisibility(), artifact, makeLicenses(inputFile));
-    Preconditions.checkArgument(getConfiguration() == null, getLabel());
+    Preconditions.checkArgument(getConfigurationKey() == null, getLabel());
     Preconditions.checkArgument(targetContext.getTarget() == inputFile, getLabel());
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
index 4e1f099..130d4ef 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/constraints/TopLevelConstraintSemantics.java
@@ -34,10 +34,12 @@
 import com.google.devtools.build.lib.packages.NoSuchTargetException;
 import com.google.devtools.build.lib.packages.Target;
 import com.google.devtools.build.lib.pkgcache.PackageManager;
+import com.google.devtools.build.lib.skyframe.BuildConfigurationValue.Key;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 
@@ -51,17 +53,21 @@
  */
 public class TopLevelConstraintSemantics {
   private final PackageManager packageManager;
+  private final Function<Key, BuildConfiguration> configurationProvider;
   private final ExtendedEventHandler eventHandler;
 
   /**
    * Constructor with helper classes for loading targets.
    *
-  * @param packageManager object for retrieving loaded targets
-  * @param eventHandler the build's event handler
-  */
-  public TopLevelConstraintSemantics(PackageManager packageManager,
+   * @param packageManager object for retrieving loaded targets
+   * @param eventHandler the build's event handler
+   */
+  public TopLevelConstraintSemantics(
+      PackageManager packageManager,
+      Function<Key, BuildConfiguration> configurationProvider,
       ExtendedEventHandler eventHandler) {
     this.packageManager = packageManager;
+    this.configurationProvider = configurationProvider;
     this.eventHandler = eventHandler;
   }
 
@@ -84,7 +90,7 @@
    *     environment declared through {@link BuildConfiguration.Options#targetEnvironments}
    */
   public Set<ConfiguredTarget> checkTargetEnvironmentRestrictions(
-      Iterable<ConfiguredTarget> topLevelTargets)
+      ImmutableList<ConfiguredTarget> topLevelTargets)
       throws ViewCreationFailedException, InterruptedException {
     ImmutableSet.Builder<ConfiguredTarget> badTargets = ImmutableSet.builder();
     // Maps targets that are missing *explicitly* required environments to the set of environments
@@ -93,7 +99,7 @@
     // continues while skipping them.
     Multimap<ConfiguredTarget, Label> exceptionInducingTargets = ArrayListMultimap.create();
     for (ConfiguredTarget topLevelTarget : topLevelTargets) {
-      BuildConfiguration config = topLevelTarget.getConfiguration();
+      BuildConfiguration config = configurationProvider.apply(topLevelTarget.getConfigurationKey());
       Target target = null;
       try {
         target = packageManager.getTarget(eventHandler, topLevelTarget.getLabel());
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
index 6f25f38..00138b3 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/BuildTool.java
@@ -218,7 +218,9 @@
         reportTargets(analysisResult);
 
         for (ConfiguredTarget target : analysisResult.getTargetsToSkip()) {
-          BuildConfiguration config = target.getConfiguration();
+          BuildConfiguration config =
+              env.getSkyframeExecutor()
+                  .getConfiguration(env.getReporter(), target.getConfigurationKey());
           Label label = target.getLabel();
           env.getEventBus().post(
               new AbortedEvent(
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java
index 8bf7a0b..46c96d8 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/CqueryBuildTool.java
@@ -141,10 +141,12 @@
                 hostConfiguration)
             : new ThreadSafeOutputFormatterCallback<ConfiguredTarget>() {
               @Override
-              public void processOutput(Iterable<ConfiguredTarget> partialResult)
-                  throws IOException, InterruptedException {
+              public void processOutput(Iterable<ConfiguredTarget> partialResult) {
                 for (ConfiguredTarget configuredTarget : partialResult) {
-                  BuildConfiguration config = configuredTarget.getConfiguration();
+                  BuildConfiguration config =
+                      env.getSkyframeExecutor()
+                          .getConfiguration(
+                              env.getReporter(), configuredTarget.getConfigurationKey());
                   StringBuilder output =
                       new StringBuilder()
                           .append(
diff --git a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
index 7adb3b5..3b238d9 100644
--- a/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
+++ b/src/main/java/com/google/devtools/build/lib/buildtool/ExecutionTool.java
@@ -367,14 +367,15 @@
     // deleted instead.
     Set<BuildConfiguration> targetConfigurations =
         request.getBuildOptions().useTopLevelTargetsForSymlinks()
-        ? analysisResult
-            .getTargetsToBuild()
-            .stream()
-            .map(ConfiguredTarget::getConfiguration)
-            .filter(configuration -> configuration != null)
-            .distinct()
-            .collect(toImmutableSet())
-        : ImmutableSet.copyOf(configurations.getTargetConfigurations());
+            ? analysisResult
+                .getTargetsToBuild()
+                .stream()
+                .map(ConfiguredTarget::getConfigurationKey)
+                .filter(configuration -> configuration != null)
+                .distinct()
+                .map((key) -> env.getSkyframeExecutor().getConfiguration(env.getReporter(), key))
+                .collect(toImmutableSet())
+            : ImmutableSet.copyOf(configurations.getTargetConfigurations());
     String productName = runtime.getProductName();
     String workspaceName = env.getWorkspaceName();
     OutputDirectoryLinksUtils.createOutputDirectoryLinks(
diff --git a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java
index 547d7d1..9daf252 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/ConfiguredTargetQueryEnvironment.java
@@ -53,6 +53,7 @@
 import com.google.devtools.build.lib.query2.engine.Uniquifier;
 import com.google.devtools.build.lib.query2.output.QueryOptions;
 import com.google.devtools.build.lib.rules.AliasConfiguredTarget;
+import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetKey;
 import com.google.devtools.build.lib.skyframe.ConfiguredTargetValue;
 import com.google.devtools.build.lib.skyframe.GraphBackedRecursivePackageProvider;
@@ -104,8 +105,6 @@
   private ConfiguredTargetAccessor accessor;
   protected WalkableGraph graph;
 
-  private static final Function<ConfiguredTarget, SkyKey> CT_TO_SKYKEY =
-      target -> ConfiguredTargetValue.key(getCorrectLabel(target), target.getConfiguration());
   private static final Function<SkyKey, ConfiguredTargetKey> SKYKEY_TO_CTKEY =
       skyKey -> (ConfiguredTargetKey) skyKey.argument();
   private static final ImmutableList<TargetPatternKey> ALL_PATTERNS;
@@ -408,22 +407,22 @@
     // host config. This is somewhat counterintuitive and subject to change in the future but seems
     // like the best option right now.
     if (settings.contains(Setting.NO_HOST_DEPS)) {
-      BuildConfiguration currentConfig = target.getConfiguration();
+      BuildConfiguration currentConfig = getConfiguration(target);
       if (currentConfig != null && currentConfig.isHostConfiguration()) {
         deps =
             deps.stream()
                 .filter(
                     dep ->
-                        dep.getConfiguration() != null
-                            && dep.getConfiguration().isHostConfiguration())
+                        getConfiguration(dep) != null
+                            && getConfiguration(dep).isHostConfiguration())
                 .collect(Collectors.toList());
       } else {
         deps =
             deps.stream()
                 .filter(
                     dep ->
-                        dep.getConfiguration() == null
-                            || !dep.getConfiguration().isHostConfiguration())
+                        getConfiguration(dep) != null
+                            && !getConfiguration(dep).isHostConfiguration())
                 .collect(Collectors.toList());
       }
     }
@@ -434,19 +433,34 @@
               .filter(
                   dep ->
                       !implicitDeps.contains(
-                          ConfiguredTargetKey.of(
-                              getCorrectLabel(dep), dep.getConfiguration())))
+                          ConfiguredTargetKey.of(getCorrectLabel(dep), getConfiguration(dep))))
               .collect(Collectors.toList());
     }
     return deps;
   }
 
+  @Nullable
+  private BuildConfiguration getConfiguration(ConfiguredTarget target) {
+    try {
+      return target.getConfigurationKey() == null
+          ? null
+          : ((BuildConfigurationValue) graph.getValue(target.getConfigurationKey()))
+              .getConfiguration();
+    } catch (InterruptedException e) {
+      throw new IllegalStateException("Unexpected interruption during configured target query");
+    }
+  }
+
+  private ConfiguredTargetKey getSkyKey(ConfiguredTarget target) {
+    return ConfiguredTargetKey.of(target, getConfiguration(target));
+  }
+
   @Override
   public ThreadSafeMutableSet<ConfiguredTarget> getFwdDeps(Iterable<ConfiguredTarget> targets)
       throws InterruptedException {
     Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets));
     for (ConfiguredTarget target : targets) {
-      targetsByKey.put(CT_TO_SKYKEY.apply(target), target);
+      targetsByKey.put(getSkyKey(target), target);
     }
     Map<SkyKey, Collection<ConfiguredTarget>> directDeps =
         targetifyValues(graph.getDirectDeps(targetsByKey.keySet()));
@@ -489,7 +503,7 @@
       throws InterruptedException {
     Map<SkyKey, ConfiguredTarget> targetsByKey = new HashMap<>(Iterables.size(targets));
     for (ConfiguredTarget target : targets) {
-      targetsByKey.put(CT_TO_SKYKEY.apply(target), target);
+      targetsByKey.put(getSkyKey(target), target);
     }
     Map<SkyKey, Collection<ConfiguredTarget>> reverseDepsByKey =
         targetifyValues(graph.getReverseDeps(targetsByKey.keySet()));
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java b/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
index da47d3c..cb87802 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/TestSummary.java
@@ -432,8 +432,8 @@
         .compare(getSortKey(this.status), getSortKey(that.status))
         .compare(this.getLabel(), that.getLabel())
         .compare(
-            this.getTarget().getConfiguration().checksum(),
-            that.getTarget().getConfiguration().checksum())
+            this.getTarget().getConfigurationChecksum(),
+            that.getTarget().getConfigurationChecksum())
         .result();
   }
 
@@ -459,7 +459,8 @@
   @Override
   public BuildEventId getEventId() {
     return BuildEventId.testSummary(
-        AliasProvider.getDependencyLabel(target), target.getConfiguration().getEventId());
+        AliasProvider.getDependencyLabel(target),
+        BuildEventId.configurationId(target.getConfigurationChecksum()));
   }
 
   @Override
@@ -471,7 +472,8 @@
   public Collection<BuildEventId> postedAfter() {
     return ImmutableList.of(
         BuildEventId.targetCompleted(
-            AliasProvider.getDependencyLabel(target), target.getConfiguration().getEventId()));
+            AliasProvider.getDependencyLabel(target),
+            BuildEventId.configurationId(target.getConfigurationChecksum())));
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
index 303502b..fea730f 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/RunCommand.java
@@ -397,7 +397,9 @@
       return BlazeCommandResult.exitCode(ExitCode.COMMAND_LINE_ERROR);
     }
 
-    BuildConfiguration configuration = targetToRun.getConfiguration();
+    BuildConfiguration configuration =
+        env.getSkyframeExecutor()
+            .getConfiguration(env.getReporter(), targetToRun.getConfigurationKey());
     if (configuration == null) {
       // The target may be an input file, which doesn't have a configuration. In that case, we
       // choose any target configuration.
@@ -529,9 +531,11 @@
     Artifact manifest = Preconditions.checkNotNull(runfilesSupport.getRunfilesManifest());
     PathFragment runfilesDir = runfilesSupport.getRunfilesDirectoryExecPath();
     Path workingDir = env.getExecRoot().getRelative(runfilesDir);
+    BuildConfiguration configuration =
+        env.getSkyframeExecutor().getConfiguration(env.getReporter(), target.getConfigurationKey());
     // On Windows, runfiles tree is disabled.
     // Workspace name directory doesn't exist, so don't add it.
-    if (target.getConfiguration().runfilesEnabled()) {
+    if (configuration.runfilesEnabled()) {
       workingDir = workingDir.getRelative(runfilesSupport.getRunfiles().getSuffix());
     }
 
@@ -547,8 +551,8 @@
         manifest.getPath(),
         runfilesSupport.getRunfilesDirectory(),
         false);
-    helper.createSymlinksUsingCommand(env.getExecRoot(), target.getConfiguration(),
-        env.getBlazeWorkspace().getBinTools());
+    helper.createSymlinksUsingCommand(
+        env.getExecRoot(), configuration, env.getBlazeWorkspace().getBinTools());
     return workingDir;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
index bbce1d6..6e8e603 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/AspectFunction.java
@@ -291,16 +291,37 @@
     ConfiguredTarget associatedTarget = baseConfiguredTargetValue.getConfiguredTarget();
 
     ConfiguredTargetAndData associatedConfiguredTargetAndData;
-    Package targetPkg =
-        ((PackageValue)
-                env.getValue(PackageValue.key(associatedTarget.getLabel().getPackageIdentifier())))
-            .getPackage();
+    Package targetPkg;
+    BuildConfiguration configuration = null;
+    PackageValue.Key packageKey =
+        PackageValue.key(associatedTarget.getLabel().getPackageIdentifier());
+    if (associatedTarget.getConfigurationKey() == null) {
+      PackageValue val = ((PackageValue) env.getValue(packageKey));
+      if (val == null) {
+        // Unexpected in Bazel logic, but Skyframe makes no guarantees that this package is
+        // actually present.
+        return null;
+      }
+      targetPkg = val.getPackage();
+    } else {
+      Map<SkyKey, SkyValue> result =
+          env.getValues(ImmutableSet.of(packageKey, associatedTarget.getConfigurationKey()));
+      if (env.valuesMissing()) {
+        // Unexpected in Bazel logic, but Skyframe makes no guarantees that this package and
+        // configuration are actually present.
+        return null;
+      }
+      targetPkg = ((PackageValue) result.get(packageKey)).getPackage();
+      configuration =
+          ((BuildConfigurationValue) result.get(associatedTarget.getConfigurationKey()))
+              .getConfiguration();
+    }
     try {
       associatedConfiguredTargetAndData =
           new ConfiguredTargetAndData(
               associatedTarget,
               targetPkg.getTarget(associatedTarget.getLabel().getName()),
-              associatedTarget.getConfiguration());
+              configuration);
     } catch (NoSuchTargetException e) {
       throw new IllegalStateException("Name already verified", e);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
index 8eed98c..7bc792a 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BuildConfigurationValue.java
@@ -13,6 +13,7 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSortedSet;
 import com.google.common.collect.Interner;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
@@ -96,7 +97,8 @@
       this.optionsDiff = optionsDiff;
     }
 
-    ImmutableSortedSet<Class<? extends BuildConfiguration.Fragment>> getFragments() {
+    @VisibleForTesting
+    public ImmutableSortedSet<Class<? extends BuildConfiguration.Fragment>> getFragments() {
       return fragments.fragmentClasses();
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
index e5692d3..657eeee 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetKey.java
@@ -43,13 +43,19 @@
     this.configurationKey = configurationKey;
   }
 
+  /** Use {@link #of(ConfiguredTarget, BuildConfiguration)} instead of this. */
+  @Deprecated
   public static ConfiguredTargetKey of(ConfiguredTarget configuredTarget) {
+    return of(configuredTarget, configuredTarget.getConfiguration());
+  }
+
+  public static ConfiguredTargetKey of(
+      ConfiguredTarget configuredTarget, BuildConfiguration buildConfiguration) {
     AliasProvider aliasProvider = configuredTarget.getProvider(AliasProvider.class);
     Label label =
         aliasProvider != null ? aliasProvider.getAliasChain().get(0) : configuredTarget.getLabel();
-    return of(label, configuredTarget.getConfiguration());
+    return of(label, buildConfiguration);
   }
-
   /**
    * Caches so that the number of ConfiguredTargetKey instances is {@code O(configured targets)} and
    * not {@code O(edges between configured targets)}.
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java
index 0314deb..56b1299 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeAnalysisResult.java
@@ -58,7 +58,7 @@
     return hasAnalysisError;
   }
 
-  public Collection<ConfiguredTarget> getConfiguredTargets() {
+  public ImmutableList<ConfiguredTarget> getConfiguredTargets() {
     return configuredTargets;
   }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index 7457a76..3e9c1f1 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -1289,6 +1289,11 @@
     return buildDriver.evaluate(patternSkyKeys, keepGoing, numThreads, eventHandler);
   }
 
+  @VisibleForTesting
+  public BuildOptions getDefaultBuildOptions() {
+    return defaultBuildOptions;
+  }
+
   /**
    * Returns the {@link ConfiguredTargetAndData}s corresponding to the given keys.
    *
@@ -1305,9 +1310,38 @@
     return getConfiguredTargetMapForTesting(eventHandler, originalConfig, keys).values().asList();
   }
 
-  @VisibleForTesting
-  public BuildOptions getDefaultBuildOptions() {
-    return defaultBuildOptions;
+  /**
+   * Returns the {@link ConfiguredTargetAndData}s corresponding to the given keys.
+   *
+   * <p>For use for legacy support and tests calling through {@code BuildView} only.
+   *
+   * <p>If a requested configured target is in error, the corresponding value is omitted from the
+   * returned list.
+   */
+  @ThreadSafety.ThreadSafe
+  public ImmutableList<ConfiguredTargetAndData> getConfiguredTargetsForTesting(
+      ExtendedEventHandler eventHandler,
+      BuildConfigurationValue.Key originalConfig,
+      Iterable<Dependency> keys) {
+    return getConfiguredTargetMapForTesting(eventHandler, originalConfig, keys).values().asList();
+  }
+
+  /**
+   * Returns a map from {@link Dependency} inputs to the {@link ConfiguredTargetAndData}s
+   * corresponding to those dependencies.
+   *
+   * <p>For use for legacy support and tests calling through {@code BuildView} only.
+   *
+   * <p>If a requested configured target is in error, the corresponding value is omitted from the
+   * returned list.
+   */
+  @ThreadSafety.ThreadSafe
+  public ImmutableMultimap<Dependency, ConfiguredTargetAndData> getConfiguredTargetMapForTesting(
+      ExtendedEventHandler eventHandler,
+      BuildConfigurationValue.Key originalConfig,
+      Iterable<Dependency> keys) {
+    return getConfiguredTargetMapForTesting(
+        eventHandler, getConfiguration(eventHandler, originalConfig), keys);
   }
 
   /**
@@ -1436,16 +1470,30 @@
   }
 
   /**
-   * Returns the configuration corresponding to the given set of build options.
+   * Returns the configuration corresponding to the given set of build options. Should not be used
+   * in a world with trimmed configurations.
    *
    * @throws InvalidConfigurationException if the build options produces an invalid configuration
    */
-  public BuildConfiguration getConfiguration(ExtendedEventHandler eventHandler,
-      BuildOptions options, boolean keepGoing) throws InvalidConfigurationException {
+  @Deprecated
+  public BuildConfiguration getConfiguration(
+      ExtendedEventHandler eventHandler, BuildOptions options, boolean keepGoing)
+      throws InvalidConfigurationException {
     return Iterables.getOnlyElement(
         getConfigurations(eventHandler, ImmutableList.of(options), keepGoing));
   }
 
+  @VisibleForTesting
+  public BuildConfiguration getConfiguration(
+      ExtendedEventHandler eventHandler, BuildConfigurationValue.Key configurationKey) {
+    if (configurationKey == null) {
+      return null;
+    }
+    return ((BuildConfigurationValue)
+            evaluateSkyKeys(eventHandler, ImmutableList.of(configurationKey)).get(configurationKey))
+        .getConfiguration();
+  }
+
   /**
    * Returns the configurations corresponding to the given sets of build options. Output order is
    * the same as input order.
@@ -1691,9 +1739,8 @@
     return configuredTargetAndData == null ? null : configuredTargetAndData.getConfiguredTarget();
   }
 
-  @VisibleForTesting
   @Nullable
-  public ConfiguredTargetAndData getConfiguredTargetAndDataForTesting(
+  private ConfiguredTargetAndData getConfiguredTargetAndDataForTesting(
       ExtendedEventHandler eventHandler,
       Label label,
       BuildConfiguration configuration,