Break up ToolchainUtil and ToolchainContext into immutable classes
ToolchainContextBuilder and ToolchainContext.

PiperOrigin-RevId: 210377177
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
index 3f22aa6..1dc9c73 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainContext.java
@@ -16,169 +16,84 @@
 
 import static java.util.stream.Collectors.joining;
 
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableList;
+import com.google.auto.value.AutoValue;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
-import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
 import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
-import com.google.devtools.build.lib.collect.nestedset.NestedSet;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
 import com.google.devtools.build.lib.events.Location;
-import com.google.devtools.build.lib.packages.Attribute;
-import com.google.devtools.build.lib.skyframe.ConfiguredTargetAndData;
 import com.google.devtools.build.lib.skylarkbuildapi.ToolchainContextApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.EvalUtils;
-import com.google.devtools.build.lib.util.OrderedSetMultimap;
-import java.util.Optional;
 import java.util.Set;
-import java.util.stream.StreamSupport;
+import javax.annotation.Nullable;
 
-/** Contains toolchain-related information needed for a {@link RuleContext}. */
+/** Represents the data needed for a specific target's use of toolchains and platforms. */
+@AutoValue
 @Immutable
 @ThreadSafe
-public class ToolchainContext implements ToolchainContextApi {
+public abstract class ToolchainContext implements ToolchainContextApi {
 
-  public static ToolchainContext create(
-      String targetDescription,
-      PlatformInfo executionPlatform,
-      PlatformInfo targetPlatform,
-      Set<Label> requiredToolchains,
-      ImmutableBiMap<Label, Label> resolvedLabels) {
-    return new ToolchainContext(
-        targetDescription,
-        executionPlatform,
-        targetPlatform,
-        requiredToolchains,
-        new ResolvedToolchainLabels(resolvedLabels));
+  static Builder builder() {
+    return new AutoValue_ToolchainContext.Builder();
   }
 
-  /** The {@link PlatformInfo} describing where these toolchains can be executed. */
-  private final PlatformInfo executionPlatform;
+  /** Builder interface to help create new instances of {@link ToolchainContext}. */
+  @AutoValue.Builder
+  interface Builder {
+    /** Sets a description of the target being used, for error messaging. */
+    Builder setTargetDescription(String targetDescription);
 
-  /** The {@link PlatformInfo} describing the outputs of these toolchains. */
-  private final PlatformInfo targetPlatform;
+    /** Sets the selected execution platform that these toolchains use. */
+    Builder setExecutionPlatform(PlatformInfo executionPlatform);
 
-  /** Description of the target the toolchain context applies to, for use in error messages. */
-  private final String targetDescription;
+    /** Sets the target platform that these toolchains generate output for. */
+    Builder setTargetPlatform(PlatformInfo targetPlatform);
 
-  /** The toolchain types that are required by the target. */
-  private final ImmutableList<Label> requiredToolchains;
+    /** Sets the toolchain types that were requested. */
+    Builder setRequiredToolchainTypes(Set<Label> requiredToolchainTypes);
 
-  /** Map from toolchain type labels to actual resolved toolchain labels. */
-  private final ResolvedToolchainLabels resolvedToolchainLabels;
+    /** Sets the map from toolchain type to toolchain provider. */
+    Builder setToolchains(ImmutableMap<Label, ToolchainInfo> toolchains);
 
-  /** Stores the actual ToolchainInfo provider for each toolchain type. */
-  private ImmutableMap<Label, ToolchainInfo> toolchainProviders;
+    /** Sets the labels of the specific toolchains being used. */
+    Builder setResolvedToolchainLabels(ImmutableSet<Label> resolvedToolchainLabels);
 
-  private ToolchainContext(
-      String targetDescription,
-      PlatformInfo executionPlatform,
-      PlatformInfo targetPlatform,
-      Set<Label> requiredToolchains,
-      ResolvedToolchainLabels resolvedToolchainLabels) {
-    this.targetDescription = targetDescription;
-    this.executionPlatform = executionPlatform;
-    this.targetPlatform = targetPlatform;
-    this.requiredToolchains = ImmutableList.copyOf(requiredToolchains);
-    this.resolvedToolchainLabels = resolvedToolchainLabels;
-    this.toolchainProviders = ImmutableMap.of();
+    /** Returns a new {@link ToolchainContext}. */
+    ToolchainContext build();
   }
 
-  public PlatformInfo executionPlatform() {
-    return executionPlatform;
+  /** Returns a description of the target being used, for error messaging. */
+  abstract String targetDescription();
+
+  /** Returns the selected execution platform that these toolchains use. */
+  public abstract PlatformInfo executionPlatform();
+
+  /** Returns the target platform that these toolchains generate output for. */
+  public abstract PlatformInfo targetPlatform();
+
+  /** Returns the toolchain types that were requested. */
+  public abstract ImmutableSet<Label> requiredToolchainTypes();
+
+  abstract ImmutableMap<Label, ToolchainInfo> toolchains();
+
+  /** Returns the labels of the specific toolchains being used. */
+  public abstract ImmutableSet<Label> resolvedToolchainLabels();
+
+  /**
+   * Returns the toolchain for the given type, or {@code null} if the toolchain type was not
+   * required in this context.
+   */
+  @Nullable
+  public ToolchainInfo forToolchainType(Label toolchainType) {
+    return toolchains().get(toolchainType);
   }
 
-  public PlatformInfo targetPlatform() {
-    return targetPlatform;
-  }
-
-  public ImmutableList<Label> requiredToolchainTypes() {
-    return requiredToolchains;
-  }
-
-  public void resolveToolchains(
-      OrderedSetMultimap<Attribute, ConfiguredTargetAndData> prerequisiteMap) {
-    if (!this.requiredToolchains.isEmpty()) {
-      this.toolchainProviders = findToolchains(resolvedToolchainLabels, prerequisiteMap);
-    }
-  }
-
-  public ImmutableSet<Label> resolvedToolchainLabels() {
-    return resolvedToolchainLabels.getToolchainLabels();
-  }
-
-  /** Returns the {@link Label}s from the {@link NestedSet} that refer to toolchain dependencies. */
-  public Set<Label> filterToolchainLabels(Iterable<Label> labels) {
-    return StreamSupport.stream(labels.spliterator(), false)
-        .filter(label -> resolvedToolchainLabels.isToolchainDependency(label))
-        .collect(ImmutableSet.toImmutableSet());
-  }
-
-  /** Tracks the mapping from toolchain type label to the label of the actual resolved toolchain. */
-  private static class ResolvedToolchainLabels {
-
-    private final ImmutableBiMap<Label, Label> toolchainLabels;
-
-    private ResolvedToolchainLabels(ImmutableBiMap<Label, Label> toolchainLabels) {
-      this.toolchainLabels = toolchainLabels;
-    }
-
-    public Label getType(Label toolchainLabel) {
-      return toolchainLabels.inverse().get(toolchainLabel);
-    }
-
-    public Label getResolvedToolchainLabel(Label toolchainType) {
-      return toolchainLabels.get(toolchainType);
-    }
-
-    public ImmutableSet<Label> getToolchainLabels() {
-      return toolchainLabels.values();
-    }
-
-    public boolean isToolchainDependency(Label label) {
-      return toolchainLabels.containsValue(label);
-    }
-  }
-
-  private static ImmutableMap<Label, ToolchainInfo> findToolchains(
-      ResolvedToolchainLabels resolvedToolchainLabels,
-      OrderedSetMultimap<Attribute, ConfiguredTargetAndData> prerequisiteMap) {
-    // Find the prerequisites associated with PlatformSemantics.RESOLVED_TOOLCHAINS_ATTR.
-    Optional<Attribute> toolchainAttribute =
-        prerequisiteMap
-            .keys()
-            .stream()
-            .filter(attribute -> attribute != null)
-            .filter(
-                attribute -> attribute.getName().equals(PlatformSemantics.RESOLVED_TOOLCHAINS_ATTR))
-            .findFirst();
-    Preconditions.checkState(
-        toolchainAttribute.isPresent(),
-        "No toolchains attribute found while loading resolved toolchains");
-
-    ImmutableMap.Builder<Label, ToolchainInfo> toolchains = new ImmutableMap.Builder<>();
-    for (ConfiguredTargetAndData target : prerequisiteMap.get(toolchainAttribute.get())) {
-      Label discoveredLabel = target.getTarget().getLabel();
-      Label toolchainType = resolvedToolchainLabels.getType(discoveredLabel);
-      if (toolchainType != null) {
-        ToolchainInfo toolchainInfo = PlatformProviderUtils.toolchain(target.getConfiguredTarget());
-        toolchains.put(toolchainType, toolchainInfo);
-      }
-    }
-
-    return toolchains.build();
-  }
-
-  // Implement SkylarkValue and SkylarkIndexable.
-
   @Override
   public boolean isImmutable() {
     return true;
@@ -187,17 +102,15 @@
   @Override
   public void repr(SkylarkPrinter printer) {
     printer.append("<toolchain_context.resolved_labels: ");
-    printer.append(
-        toolchainProviders.keySet().stream().map(key -> key.toString()).collect(joining(", ")));
+    printer.append(toolchains().keySet().stream().map(Label::toString).collect(joining(", ")));
     printer.append(">");
   }
 
   private Label transformKey(Object key, Location loc) throws EvalException {
     if (key instanceof Label) {
-      Label toolchainType = (Label) key;
-      return toolchainType;
+      return (Label) key;
     } else if (key instanceof String) {
-      Label toolchainType = null;
+      Label toolchainType;
       String rawLabel = (String) key;
       try {
         toolchainType = Label.parseAbsolute(rawLabel, ImmutableMap.of());
@@ -219,29 +132,21 @@
   public ToolchainInfo getIndex(Object key, Location loc) throws EvalException {
     Label toolchainType = transformKey(key, loc);
 
-    if (!requiredToolchains.contains(toolchainType)) {
+    if (!requiredToolchainTypes().contains(toolchainType)) {
       throw new EvalException(
           loc,
           String.format(
               "In %s, toolchain type %s was requested but only types [%s] are configured",
-              targetDescription,
+              targetDescription(),
               toolchainType,
-              requiredToolchains
-                  .stream()
-                  .map(toolchain -> toolchain.toString())
-                  .collect(joining())));
+              requiredToolchainTypes().stream().map(Label::toString).collect(joining())));
     }
-    return toolchainProviders.get(toolchainType);
-  }
-
-  /** Returns the toolchain for the given type */
-  public ToolchainInfo forToolchainType(Label toolchainType) {
-    return toolchainProviders.get(toolchainType);
+    return forToolchainType(toolchainType);
   }
 
   @Override
   public boolean containsKey(Object key, Location loc) throws EvalException {
     Label toolchainType = transformKey(key, loc);
-    return toolchainProviders.containsKey(toolchainType);
+    return toolchains().containsKey(toolchainType);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ToolchainResolver.java b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainResolver.java
new file mode 100644
index 0000000..e03fa6f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ToolchainResolver.java
@@ -0,0 +1,591 @@
+// Copyright 2018 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.devtools.build.lib.analysis;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static java.util.stream.Collectors.joining;
+
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Joiner;
+import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Table;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
+import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
+import com.google.devtools.build.lib.analysis.platform.PlatformProviderUtils;
+import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
+import com.google.devtools.build.lib.cmdline.Label;
+import com.google.devtools.build.lib.events.Event;
+import com.google.devtools.build.lib.packages.Attribute;
+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.ConstraintValueLookupUtil;
+import com.google.devtools.build.lib.skyframe.ConstraintValueLookupUtil.InvalidConstraintValueException;
+import com.google.devtools.build.lib.skyframe.PlatformLookupUtil;
+import com.google.devtools.build.lib.skyframe.PlatformLookupUtil.InvalidPlatformException;
+import com.google.devtools.build.lib.skyframe.RegisteredExecutionPlatformsValue;
+import com.google.devtools.build.lib.skyframe.RegisteredToolchainsFunction.InvalidToolchainLabelException;
+import com.google.devtools.build.lib.skyframe.ToolchainException;
+import com.google.devtools.build.lib.skyframe.ToolchainResolutionFunction.NoToolchainFoundException;
+import com.google.devtools.build.lib.skyframe.ToolchainResolutionValue;
+import com.google.devtools.build.lib.util.OrderedSetMultimap;
+import com.google.devtools.build.skyframe.SkyFunction.Environment;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.ValueOrException2;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * Performs the toolchain resolution process to determine the correct toolchain target dependencies
+ * for a target being configured, based on the required toolchain types, target platform, and
+ * available execution platforms.
+ */
+public class ToolchainResolver {
+  // Required data.
+  private final Environment environment;
+  private final BuildConfigurationValue.Key configurationKey;
+
+  // Optional data.
+  private String targetDescription = "";
+  private ImmutableSet<Label> requiredToolchainTypes = ImmutableSet.of();
+  private ImmutableSet<Label> execConstraintLabels = ImmutableSet.of();
+
+  // Determined during execution.
+  private boolean debug = false;
+
+  /**
+   * Creates a new {@link ToolchainResolver} to help find the required toolchains for a configured
+   * target.
+   *
+   * @param env the environment to use to request dependent Skyframe nodes
+   * @param configurationKey The build configuration to use for dependent targets
+   */
+  public ToolchainResolver(Environment env, BuildConfigurationValue.Key configurationKey) {
+    this.environment = checkNotNull(env);
+    this.configurationKey = checkNotNull(configurationKey);
+  }
+
+  /**
+   * Sets a description of the target that toolchains will be resolved for. This is primarily useful
+   * for printing informative error messages, so that users can tell which targets had difficulty.
+   */
+  public ToolchainResolver setTargetDescription(String targetDescription) {
+    this.targetDescription = targetDescription;
+    return this;
+  }
+
+  /** Sets the required toolchain types that this resolver needs to find toolchains for. */
+  public ToolchainResolver setRequiredToolchainTypes(Set<Label> requiredToolchainTypes) {
+    this.requiredToolchainTypes = ImmutableSet.copyOf(requiredToolchainTypes);
+    return this;
+  }
+
+  /**
+   * Sets extra constraints on the execution platform. Targets can use this to ensure that the
+   * execution platform has some desired characteristics, such as having enough memory to run tests.
+   */
+  public ToolchainResolver setExecConstraintLabels(Set<Label> execConstraintLabels) {
+    this.execConstraintLabels = ImmutableSet.copyOf(execConstraintLabels);
+    return this;
+  }
+
+  /**
+   * Determines the specific toolchains that are required, given the requested toolchain types,
+   * target platform, and configuration.
+   *
+   * <p>In order to resolve toolchains, first the {@link ToolchainResolver} must be created, and
+   * then an {@link UnloadedToolchainContext} generated. The {@link UnloadedToolchainContext} will
+   * report the specific toolchain targets to depend on, and those can be found using the typical
+   * dependency machinery. Once dependencies, including toolchains, have been loaded, the {@link
+   * UnloadedToolchainContext#load} method can be called to generate the final {@link
+   * ToolchainContext} to be used by the target.
+   *
+   * <p>This makes several SkyFrame calls, particularly to {@link
+   * com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction} (to load platforms and
+   * toolchains), to {@link
+   * com.google.devtools.build.lib.skyframe.RegisteredExecutionPlatformsFunction}, and to {@link
+   * com.google.devtools.build.lib.skyframe.ToolchainResolutionFunction}. This method return {@code
+   * null} to signal a SkyFrame restart is needed to resolve dependencies.
+   */
+  @Nullable
+  public UnloadedToolchainContext resolve() throws InterruptedException, ToolchainException {
+
+    try {
+      UnloadedToolchainContext.Builder unloadedToolchainContext =
+          UnloadedToolchainContext.builder();
+      unloadedToolchainContext
+          .setTargetDescription(targetDescription)
+          .setRequiredToolchainTypes(requiredToolchainTypes);
+
+      // Determine the configuration being used.
+      BuildConfigurationValue value =
+          (BuildConfigurationValue) environment.getValue(configurationKey);
+      if (value == null) {
+        throw new ValueMissingException();
+      }
+      BuildConfiguration configuration = value.getConfiguration();
+      PlatformConfiguration platformConfiguration =
+          configuration.getFragment(PlatformConfiguration.class);
+      if (platformConfiguration == null) {
+        throw new ValueMissingException();
+      }
+
+      // Check if debug output should be generated.
+      this.debug = configuration.getOptions().get(PlatformOptions.class).toolchainResolutionDebug;
+
+      // Create keys for all platforms that will be used, and validate them early.
+      PlatformKeys platformKeys = loadPlatformKeys(configuration, platformConfiguration);
+      if (environment.valuesMissing()) {
+        return null;
+      }
+
+      // Determine the actual toolchain implementations to use.
+      determineToolchainImplementations(unloadedToolchainContext, platformKeys);
+
+      return unloadedToolchainContext.build();
+    } catch (ValueMissingException e) {
+      return null;
+    }
+  }
+
+  @AutoValue
+  abstract static class PlatformKeys {
+    abstract ConfiguredTargetKey hostPlatformKey();
+
+    abstract ConfiguredTargetKey targetPlatformKey();
+
+    abstract ImmutableList<ConfiguredTargetKey> executionPlatformKeys();
+
+    static PlatformKeys create(
+        ConfiguredTargetKey hostPlatformKey,
+        ConfiguredTargetKey targetPlatformKey,
+        List<ConfiguredTargetKey> executionPlatformKeys) {
+      return new AutoValue_ToolchainResolver_PlatformKeys(
+          hostPlatformKey, targetPlatformKey, ImmutableList.copyOf(executionPlatformKeys));
+    }
+  }
+
+  private PlatformKeys loadPlatformKeys(
+      BuildConfiguration configuration, PlatformConfiguration platformConfiguration)
+      throws InterruptedException, InvalidPlatformException, ValueMissingException,
+          InvalidConstraintValueException {
+    // Determine the target and host platform keys.
+    Label hostPlatformLabel = platformConfiguration.getHostPlatform();
+    Label targetPlatformLabel = platformConfiguration.getTargetPlatform();
+
+    ConfiguredTargetKey hostPlatformKey = ConfiguredTargetKey.of(hostPlatformLabel, configuration);
+    ConfiguredTargetKey targetPlatformKey =
+        ConfiguredTargetKey.of(targetPlatformLabel, configuration);
+
+    // Load the host and target platforms early, to check for errors.
+    PlatformLookupUtil.getPlatformInfo(
+        ImmutableList.of(hostPlatformKey, targetPlatformKey), environment);
+    if (environment.valuesMissing()) {
+      throw new ValueMissingException();
+    }
+
+    ImmutableList<ConfiguredTargetKey> executionPlatformKeys =
+        loadExecutionPlatformKeys(configuration, hostPlatformKey);
+
+    return PlatformKeys.create(hostPlatformKey, targetPlatformKey, executionPlatformKeys);
+  }
+
+  private ImmutableList<ConfiguredTargetKey> loadExecutionPlatformKeys(
+      BuildConfiguration configuration, ConfiguredTargetKey defaultPlatformKey)
+      throws InvalidPlatformException, InterruptedException, InvalidConstraintValueException,
+          ValueMissingException {
+    RegisteredExecutionPlatformsValue registeredExecutionPlatforms =
+        (RegisteredExecutionPlatformsValue)
+            environment.getValueOrThrow(
+                RegisteredExecutionPlatformsValue.key(configurationKey),
+                InvalidPlatformException.class);
+    if (registeredExecutionPlatforms == null) {
+      throw new ValueMissingException();
+    }
+
+    ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys =
+        new ImmutableList.Builder<ConfiguredTargetKey>()
+            .addAll(registeredExecutionPlatforms.registeredExecutionPlatformKeys())
+            .add(defaultPlatformKey)
+            .build();
+
+    // Filter out execution platforms that don't satisfy the extra constraints.
+    ImmutableList<ConfiguredTargetKey> execConstraintKeys =
+        execConstraintLabels.stream()
+            .map(label -> ConfiguredTargetKey.of(label, configuration))
+            .collect(toImmutableList());
+
+    return filterAvailablePlatforms(availableExecutionPlatformKeys, execConstraintKeys);
+  }
+
+  /** Returns only the platform keys that match the given constraints. */
+  private ImmutableList<ConfiguredTargetKey> filterAvailablePlatforms(
+      ImmutableList<ConfiguredTargetKey> platformKeys,
+      ImmutableList<ConfiguredTargetKey> constraintKeys)
+      throws InterruptedException, InvalidPlatformException, InvalidConstraintValueException,
+          ValueMissingException {
+
+    // Short circuit if not needed.
+    if (constraintKeys.isEmpty()) {
+      return platformKeys;
+    }
+
+    // At this point the host and target platforms have been loaded, but not necessarily the chosen
+    // execution platform (it might be the same as the host platform, and might not).
+    //
+    // It's not worth trying to optimize away this call, since in the optimizable case (the exec
+    // platform is the host platform), Skyframe will return the correct results immediately without
+    // need of a restart.
+    Map<ConfiguredTargetKey, PlatformInfo> platformInfoMap =
+        PlatformLookupUtil.getPlatformInfo(platformKeys, environment);
+    if (platformInfoMap == null) {
+      throw new ValueMissingException();
+    }
+    List<ConstraintValueInfo> constraints =
+        ConstraintValueLookupUtil.getConstraintValueInfo(constraintKeys, environment);
+    if (constraints == null) {
+      throw new ValueMissingException();
+    }
+
+    return platformKeys.stream()
+        .filter(key -> filterPlatform(platformInfoMap.get(key), constraints))
+        .collect(toImmutableList());
+  }
+
+  /** Returns {@code true} if the given platform has all of the constraints. */
+  private boolean filterPlatform(PlatformInfo platformInfo, List<ConstraintValueInfo> constraints) {
+    for (ConstraintValueInfo filterConstraint : constraints) {
+      ConstraintValueInfo platformInfoConstraint =
+          platformInfo.getConstraint(filterConstraint.constraint());
+      if (platformInfoConstraint == null || !platformInfoConstraint.equals(filterConstraint)) {
+        // The value for this setting is not present in the platform, or doesn't match the expected
+        // value.
+        if (debug) {
+          environment
+              .getListener()
+              .handle(
+                  Event.info(
+                      String.format(
+                          "ToolchainUtil: Removed execution platform %s from"
+                              + " available execution platforms, it is missing constraint %s",
+                          platformInfo.label(), filterConstraint.label())));
+        }
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  private void determineToolchainImplementations(
+      UnloadedToolchainContext.Builder unloadedToolchainContext, PlatformKeys platformKeys)
+      throws InterruptedException, ToolchainException, ValueMissingException {
+
+    // Find the toolchains for the required toolchain types.
+    List<ToolchainResolutionValue.Key> registeredToolchainKeys = new ArrayList<>();
+    for (Label toolchainType : requiredToolchainTypes) {
+      registeredToolchainKeys.add(
+          ToolchainResolutionValue.key(
+              configurationKey,
+              toolchainType,
+              platformKeys.targetPlatformKey(),
+              platformKeys.executionPlatformKeys()));
+    }
+
+    Map<SkyKey, ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>>
+        results =
+            environment.getValuesOrThrow(
+                registeredToolchainKeys,
+                NoToolchainFoundException.class,
+                InvalidToolchainLabelException.class);
+    boolean valuesMissing = false;
+
+    // Determine the potential set of toolchains.
+    Table<ConfiguredTargetKey, Label, Label> resolvedToolchains = HashBasedTable.create();
+    List<Label> missingToolchains = new ArrayList<>();
+    for (Map.Entry<
+            SkyKey, ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>>
+        entry : results.entrySet()) {
+      try {
+        Label requiredToolchainType =
+            ((ToolchainResolutionValue.Key) entry.getKey().argument()).toolchainType();
+        ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>
+            valueOrException = entry.getValue();
+        ToolchainResolutionValue toolchainResolutionValue =
+            (ToolchainResolutionValue) valueOrException.get();
+        if (toolchainResolutionValue == null) {
+          valuesMissing = true;
+          continue;
+        }
+
+        resolvedToolchains.putAll(
+            findPlatformsAndLabels(requiredToolchainType, toolchainResolutionValue));
+      } catch (NoToolchainFoundException e) {
+        // Save the missing type and continue looping to check for more.
+        missingToolchains.add(e.missingToolchainType());
+      }
+    }
+
+    if (!missingToolchains.isEmpty()) {
+      throw new UnresolvedToolchainsException(missingToolchains);
+    }
+
+    if (valuesMissing) {
+      throw new ValueMissingException();
+    }
+
+    // Find and return the first execution platform which has all required toolchains.
+    Optional<ConfiguredTargetKey> selectedExecutionPlatformKey;
+    if (requiredToolchainTypes.isEmpty()
+        && platformKeys.executionPlatformKeys().contains(platformKeys.hostPlatformKey())) {
+      // Fall back to the legacy behavior: use the host platform if it's available, otherwise the
+      // first execution platform.
+      selectedExecutionPlatformKey = Optional.of(platformKeys.hostPlatformKey());
+    } else {
+      // If there are no toolchains, this will return the first execution platform.
+      selectedExecutionPlatformKey =
+          findExecutionPlatformForToolchains(
+              platformKeys.executionPlatformKeys(), resolvedToolchains);
+    }
+
+    if (!selectedExecutionPlatformKey.isPresent()) {
+      throw new NoMatchingPlatformException(
+          requiredToolchainTypes,
+          platformKeys.executionPlatformKeys(),
+          platformKeys.targetPlatformKey());
+    }
+
+    Map<ConfiguredTargetKey, PlatformInfo> platforms =
+        PlatformLookupUtil.getPlatformInfo(
+            ImmutableList.of(selectedExecutionPlatformKey.get(), platformKeys.targetPlatformKey()),
+            environment);
+    if (platforms == null) {
+      throw new ValueMissingException();
+    }
+
+    unloadedToolchainContext.setExecutionPlatform(
+        platforms.get(selectedExecutionPlatformKey.get()));
+    unloadedToolchainContext.setTargetPlatform(platforms.get(platformKeys.targetPlatformKey()));
+
+    Map<Label, Label> toolchains = resolvedToolchains.row(selectedExecutionPlatformKey.get());
+    unloadedToolchainContext.setToolchainTypeToResolved(ImmutableBiMap.copyOf(toolchains));
+  }
+
+  /**
+   * Adds all of toolchain labels from{@code toolchainResolutionValue} to {@code
+   * resolvedToolchains}.
+   */
+  private static Table<ConfiguredTargetKey, Label, Label> findPlatformsAndLabels(
+      Label requiredToolchainType, ToolchainResolutionValue toolchainResolutionValue) {
+
+    Table<ConfiguredTargetKey, Label, Label> resolvedToolchains = HashBasedTable.create();
+    for (Map.Entry<ConfiguredTargetKey, Label> entry :
+        toolchainResolutionValue.availableToolchainLabels().entrySet()) {
+      resolvedToolchains.put(entry.getKey(), requiredToolchainType, entry.getValue());
+    }
+    return resolvedToolchains;
+  }
+
+  /**
+   * Finds the first platform from {@code availableExecutionPlatformKeys} that is present in {@code
+   * resolvedToolchains} and has all required toolchain types.
+   */
+  private Optional<ConfiguredTargetKey> findExecutionPlatformForToolchains(
+      ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
+      Table<ConfiguredTargetKey, Label, Label> resolvedToolchains) {
+    for (ConfiguredTargetKey executionPlatformKey : availableExecutionPlatformKeys) {
+      Map<Label, Label> toolchains = resolvedToolchains.row(executionPlatformKey);
+      if (!toolchains.keySet().containsAll(requiredToolchainTypes)) {
+        // Not all toolchains are present, keep going
+        continue;
+      }
+
+      if (debug) {
+        String selectedToolchains =
+            toolchains.entrySet().stream()
+                .map(e -> String.format("type %s -> toolchain %s", e.getKey(), e.getValue()))
+                .collect(joining(", "));
+        environment
+            .getListener()
+            .handle(
+                Event.info(
+                    String.format(
+                        "ToolchainUtil: Selected execution platform %s, %s",
+                        executionPlatformKey.getLabel(), selectedToolchains)));
+      }
+      return Optional.of(executionPlatformKey);
+    }
+
+    return Optional.empty();
+  }
+
+  /**
+   * Represents the state of toolchain resolution once the specific required toolchains have been
+   * determined, but before the toolchain dependencies have been resolved.
+   */
+  @AutoValue
+  public abstract static class UnloadedToolchainContext {
+
+    static Builder builder() {
+      return new AutoValue_ToolchainResolver_UnloadedToolchainContext.Builder();
+    }
+
+    /** Builder class to help create the {@link UnloadedToolchainContext}. */
+    @AutoValue.Builder
+    public interface Builder {
+      /** Sets a description of the target being used, for error messaging. */
+      Builder setTargetDescription(String targetDescription);
+
+      /** Sets the selected execution platform that these toolchains use. */
+      Builder setExecutionPlatform(PlatformInfo executionPlatform);
+
+      /** Sets the target platform that these toolchains generate output for. */
+      Builder setTargetPlatform(PlatformInfo targetPlatform);
+
+      /** Sets the toolchain types that were requested. */
+      Builder setRequiredToolchainTypes(Set<Label> requiredToolchainTypes);
+
+      Builder setToolchainTypeToResolved(ImmutableBiMap<Label, Label> toolchainTypeToResolved);
+
+      UnloadedToolchainContext build();
+    }
+
+    /** Returns a description of the target being used, for error messaging. */
+    abstract String targetDescription();
+
+    /** Returns the selected execution platform that these toolchains use. */
+    abstract PlatformInfo executionPlatform();
+
+    /** Returns the target platform that these toolchains generate output for. */
+    abstract PlatformInfo targetPlatform();
+
+    /** Returns the toolchain types that were requested. */
+    abstract ImmutableSet<Label> requiredToolchainTypes();
+
+    /** The map of toolchain type to resolved toolchain to be used. */
+    abstract ImmutableBiMap<Label, Label> toolchainTypeToResolved();
+
+    /** Returns the labels of the specific toolchains being used. */
+    public ImmutableSet<Label> resolvedToolchainLabels() {
+      return toolchainTypeToResolved().values();
+    }
+
+    /**
+     * Finishes preparing the {@link ToolchainContext} by finding the specific toolchain providers
+     * to be used for each toolchain type.
+     */
+    public ToolchainContext load(
+        OrderedSetMultimap<Attribute, ConfiguredTargetAndData> prerequisiteMap) {
+
+      ToolchainContext.Builder toolchainContext =
+          ToolchainContext.builder()
+              .setTargetDescription(targetDescription())
+              .setExecutionPlatform(executionPlatform())
+              .setTargetPlatform(targetPlatform())
+              .setRequiredToolchainTypes(requiredToolchainTypes())
+              .setResolvedToolchainLabels(resolvedToolchainLabels());
+
+      // Find the prerequisites associated with PlatformSemantics.RESOLVED_TOOLCHAINS_ATTR.
+      Optional<Attribute> toolchainAttribute =
+          prerequisiteMap.keys().stream()
+              .filter(Objects::nonNull)
+              .filter(
+                  attribute ->
+                      attribute.getName().equals(PlatformSemantics.RESOLVED_TOOLCHAINS_ATTR))
+              .findFirst();
+      ImmutableMap.Builder<Label, ToolchainInfo> toolchains = new ImmutableMap.Builder<>();
+      if (toolchainAttribute.isPresent()) {
+        for (ConfiguredTargetAndData target : prerequisiteMap.get(toolchainAttribute.get())) {
+          Label discoveredLabel = target.getTarget().getLabel();
+          Label toolchainType = toolchainTypeToResolved().inverse().get(discoveredLabel);
+
+          // If the toolchainType hadn't been resolved to an actual toolchain, resolution would have
+          // failed with an error much earlier. This null check is just for safety.
+          if (toolchainType != null) {
+            ToolchainInfo toolchainInfo =
+                PlatformProviderUtils.toolchain(target.getConfiguredTarget());
+            toolchains.put(toolchainType, toolchainInfo);
+          }
+
+          // Find any template variables present for this toolchain.
+          // TODO(jcater): save this somewhere.
+        }
+      }
+
+      return toolchainContext.setToolchains(toolchains.build()).build();
+    }
+  }
+
+  private static final class ValueMissingException extends Exception {
+    private ValueMissingException() {
+      super();
+    }
+  }
+
+  /** Exception used when no execution platform can be found. */
+  static final class NoMatchingPlatformException extends ToolchainException {
+    NoMatchingPlatformException(
+        Set<Label> requiredToolchains,
+        ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
+        ConfiguredTargetKey targetPlatformKey) {
+      super(formatError(requiredToolchains, availableExecutionPlatformKeys, targetPlatformKey));
+    }
+
+    private static String formatError(
+        Set<Label> requiredToolchains,
+        ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
+        ConfiguredTargetKey targetPlatformKey) {
+      if (requiredToolchains.isEmpty()) {
+        return String.format(
+            "Unable to find an execution platform for target platform %s"
+                + " from available execution platforms [%s]",
+            targetPlatformKey.getLabel(),
+            availableExecutionPlatformKeys.stream()
+                .map(key -> key.getLabel().toString())
+                .collect(Collectors.joining(", ")));
+      }
+      return String.format(
+          "Unable to find an execution platform for toolchains [%s] and target platform %s"
+              + " from available execution platforms [%s]",
+          Joiner.on(", ").join(requiredToolchains),
+          targetPlatformKey.getLabel(),
+          availableExecutionPlatformKeys.stream()
+              .map(key -> key.getLabel().toString())
+              .collect(Collectors.joining(", ")));
+    }
+  }
+
+  /** Exception used when a toolchain type is required but no matching toolchain is found. */
+  static final class UnresolvedToolchainsException extends ToolchainException {
+    UnresolvedToolchainsException(List<Label> missingToolchainTypes) {
+      super(
+          String.format(
+              "no matching toolchains found for types %s",
+              Joiner.on(", ").join(missingToolchainTypes)));
+    }
+  }
+}
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 4088d3f..2ab09fc 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
@@ -30,6 +30,8 @@
 import com.google.devtools.build.lib.analysis.DependencyResolver.InconsistentAspectOrderException;
 import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
 import com.google.devtools.build.lib.analysis.ToolchainContext;
+import com.google.devtools.build.lib.analysis.ToolchainResolver;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.UnloadedToolchainContext;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
@@ -402,26 +404,29 @@
       }
 
       // Determine what toolchains are needed by this target.
-      ToolchainContext toolchainContext;
-      try {
-        ImmutableSet<Label> requiredToolchains = aspect.getDefinition().getRequiredToolchains();
-        toolchainContext =
-            ToolchainUtil.createToolchainContext(
-                env,
-                String.format(
-                    "aspect %s applied to %s",
-                    aspect.getDescriptor().getDescription(),
-                    associatedConfiguredTargetAndData.getTarget().toString()),
-                requiredToolchains,
-                /* execConstraintLabels= */ ImmutableSet.of(),
-                key.getAspectConfigurationKey());
-      } catch (ToolchainException e) {
-        // TODO(katre): better error handling
-        throw new AspectCreationException(
-            e.getMessage(), new LabelCause(key.getLabel(), e.getMessage()));
-      }
-      if (env.valuesMissing()) {
-        return null;
+      UnloadedToolchainContext unloadedToolchainContext = null;
+      if (configuration != null) {
+        // Configuration can be null in the case of aspects applied to input files. In this case,
+        // there are no chances of toolchains being used, so skip it.
+        try {
+          ImmutableSet<Label> requiredToolchains = aspect.getDefinition().getRequiredToolchains();
+          unloadedToolchainContext =
+              new ToolchainResolver(env, BuildConfigurationValue.key(configuration))
+                  .setTargetDescription(
+                      String.format(
+                          "aspect %s applied to %s",
+                          aspect.getDescriptor().getDescription(),
+                          associatedConfiguredTargetAndData.getTarget()))
+                  .setRequiredToolchainTypes(requiredToolchains)
+                  .resolve();
+        } catch (ToolchainException e) {
+          // TODO(katre): better error handling
+          throw new AspectCreationException(
+              e.getMessage(), new LabelCause(key.getLabel(), e.getMessage()));
+        }
+        if (env.valuesMissing()) {
+          return null;
+        }
       }
 
       OrderedSetMultimap<Attribute, ConfiguredTargetAndData> depValueMap;
@@ -433,9 +438,9 @@
                 originalTargetAndAspectConfiguration,
                 aspectPath,
                 configConditions,
-                toolchainContext == null
+                unloadedToolchainContext == null
                     ? ImmutableSet.of()
-                    : toolchainContext.resolvedToolchainLabels(),
+                    : unloadedToolchainContext.resolvedToolchainLabels(),
                 ruleClassProvider,
                 view.getHostConfiguration(originalTargetAndAspectConfiguration.getConfiguration()),
                 transitivePackagesForPackageRootResolution,
@@ -453,8 +458,9 @@
       }
 
       // Load the requested toolchains into the ToolchainContext, now that we have dependencies.
-      if (toolchainContext != null) {
-        toolchainContext.resolveToolchains(depValueMap);
+      ToolchainContext toolchainContext = null;
+      if (unloadedToolchainContext != null) {
+        toolchainContext = unloadedToolchainContext.load(depValueMap);
       }
 
       return createAspect(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
index bad9ec0..f88f7b6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Streams;
 import com.google.devtools.build.lib.actions.Actions;
 import com.google.devtools.build.lib.actions.Actions.GeneratingActions;
 import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
@@ -32,6 +33,8 @@
 import com.google.devtools.build.lib.analysis.PlatformSemantics;
 import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
 import com.google.devtools.build.lib.analysis.ToolchainContext;
+import com.google.devtools.build.lib.analysis.ToolchainResolver;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.UnloadedToolchainContext;
 import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
@@ -229,7 +232,7 @@
 
     SkyframeDependencyResolver resolver = view.createDependencyResolver(env);
 
-    ToolchainContext toolchainContext = null;
+    UnloadedToolchainContext unloadedToolchainContext = null;
 
     // TODO(janakr): this acquire() call may tie up this thread indefinitely, reducing the
     // parallelism of Skyframe. This is a strict improvement over the prior state of the code, in
@@ -272,13 +275,12 @@
 
           // Collect local (target, rule) constraints for filtering out execution platforms.
           ImmutableSet<Label> execConstraintLabels = getExecutionPlatformConstraints(rule);
-          toolchainContext =
-              ToolchainUtil.createToolchainContext(
-                  env,
-                  rule.toString(),
-                  requiredToolchains,
-                  execConstraintLabels,
-                  configuredTargetKey.getConfigurationKey());
+          unloadedToolchainContext =
+              new ToolchainResolver(env, configuredTargetKey.getConfigurationKey())
+                  .setTargetDescription(rule.toString())
+                  .setRequiredToolchainTypes(requiredToolchains)
+                  .setExecConstraintLabels(execConstraintLabels)
+                  .resolve();
           if (env.valuesMissing()) {
             return null;
           }
@@ -293,9 +295,9 @@
               ctgValue,
               ImmutableList.<Aspect>of(),
               configConditions,
-              toolchainContext == null
+              unloadedToolchainContext == null
                   ? ImmutableSet.of()
-                  : toolchainContext.resolvedToolchainLabels(),
+                  : unloadedToolchainContext.resolvedToolchainLabels(),
               ruleClassProvider,
               view.getHostConfiguration(configuration),
               transitivePackagesForPackageRootResolution,
@@ -312,8 +314,9 @@
       Preconditions.checkNotNull(depValueMap);
 
       // Load the requested toolchains into the ToolchainContext, now that we have dependencies.
-      if (toolchainContext != null) {
-        toolchainContext.resolveToolchains(depValueMap);
+      ToolchainContext toolchainContext = null;
+      if (unloadedToolchainContext != null) {
+        toolchainContext = unloadedToolchainContext.load(depValueMap);
       }
 
       ConfiguredTargetValue ans =
@@ -332,10 +335,14 @@
         ConfiguredValueCreationException cvce = (ConfiguredValueCreationException) e.getCause();
 
         // Check if this is caused by an unresolved toolchain, and report it as such.
-        if (toolchainContext != null) {
+        if (unloadedToolchainContext != null) {
+          UnloadedToolchainContext finalUnloadedToolchainContext = unloadedToolchainContext;
           Set<Label> toolchainDependencyErrors =
-              toolchainContext.filterToolchainLabels(
-                  Iterables.transform(cvce.getRootCauses(), Cause::getLabel));
+              Streams.stream(cvce.getRootCauses())
+                  .map(Cause::getLabel)
+                  .filter(l -> finalUnloadedToolchainContext.resolvedToolchainLabels().contains(l))
+                  .collect(ImmutableSet.toImmutableSet());
+
           if (!toolchainDependencyErrors.isEmpty()) {
             env.getListener()
                 .handle(
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 22f3d66..8d9309f 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
@@ -64,7 +64,6 @@
 import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
 import com.google.devtools.build.lib.analysis.ConfiguredTarget;
 import com.google.devtools.build.lib.analysis.Dependency;
-import com.google.devtools.build.lib.analysis.ToolchainContext;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactContext;
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
 import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory;
@@ -1014,17 +1013,9 @@
   }
 
   @VisibleForTesting
-  public ToolchainContext getToolchainContextForTesting(
-      Set<Label> requiredToolchains, BuildConfiguration config, ExtendedEventHandler eventHandler)
-      throws ToolchainException, InterruptedException {
-    SkyFunctionEnvironmentForTesting env =
-        new SkyFunctionEnvironmentForTesting(buildDriver, eventHandler, this);
-    return ToolchainUtil.createToolchainContext(
-        env,
-        "",
-        requiredToolchains,
-        /* execConstraintLabels= */ ImmutableSet.of(),
-        config == null ? null : BuildConfigurationValue.key(config));
+  public SkyFunctionEnvironmentForTesting getSkyFunctionEnvironmentForTesting(
+      ExtendedEventHandler eventHandler) {
+    return new SkyFunctionEnvironmentForTesting(buildDriver, eventHandler, this);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java
index abaa41f..341be5a 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java
@@ -48,7 +48,7 @@
   @AutoCodec
   @AutoCodec.VisibleForSerialization
   @AutoValue
-  abstract static class Key implements SkyKey {
+  public abstract static class Key implements SkyKey {
 
     @Override
     public SkyFunctionName functionName() {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java
deleted file mode 100644
index 8399fb7..0000000
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainUtil.java
+++ /dev/null
@@ -1,469 +0,0 @@
-// Copyright 2017 The Bazel Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//    http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.devtools.build.lib.skyframe;
-
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static java.util.stream.Collectors.joining;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.base.Joiner;
-import com.google.common.base.Optional;
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Table;
-import com.google.devtools.build.lib.analysis.PlatformConfiguration;
-import com.google.devtools.build.lib.analysis.PlatformOptions;
-import com.google.devtools.build.lib.analysis.ToolchainContext;
-import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
-import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
-import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
-import com.google.devtools.build.lib.cmdline.Label;
-import com.google.devtools.build.lib.events.Event;
-import com.google.devtools.build.lib.skyframe.ConstraintValueLookupUtil.InvalidConstraintValueException;
-import com.google.devtools.build.lib.skyframe.PlatformLookupUtil.InvalidPlatformException;
-import com.google.devtools.build.lib.skyframe.RegisteredToolchainsFunction.InvalidToolchainLabelException;
-import com.google.devtools.build.lib.skyframe.ToolchainResolutionFunction.NoToolchainFoundException;
-import com.google.devtools.build.skyframe.SkyFunction.Environment;
-import com.google.devtools.build.skyframe.SkyKey;
-import com.google.devtools.build.skyframe.ValueOrException2;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-import javax.annotation.Nullable;
-
-/**
- * Common code to create a {@link ToolchainContext} given a set of required toolchain type labels.
- */
-// TODO(katre): Refactor this and ToolchainContext into something nicer to work with and with
-// fewer static methods everywhere.
-public class ToolchainUtil {
-
-  /**
-   * Returns a new {@link ToolchainContext}, containing:
-   *
-   * <ul>
-   *   <li>If {@code requiredToolchains} was non-empty, the resolved toolchains and execution
-   *       platform (as labels), based on the results of the {@link ToolchainResolutionFunction}
-   *   <li>If {@code requiredToolchains} was empty:
-   *       <ul>
-   *         <li>The resolved toolchains will be empty.
-   *         <li>The execution platform will be the host platform, if the host platform was in the
-   *             set of available execution platforms.
-   *         <li>Otherwise, the execution platform will be the first available execution platform.
-   *       </ul>
-   * </ul>
-   *
-   * @param env the Skyframe environment to use to acquire dependencies
-   * @param targetDescription a description of the target use, for error and debug message context
-   * @param requiredToolchains the required toolchain types that must be resolved
-   * @param execConstraintLabels extra constraints on the execution platform to select
-   * @param configurationKey the build configuration to use for resolving other targets
-   */
-  @Nullable
-  static ToolchainContext createToolchainContext(
-      Environment env,
-      String targetDescription,
-      Set<Label> requiredToolchains,
-      Set<Label> execConstraintLabels,
-      @Nullable BuildConfigurationValue.Key configurationKey)
-      throws InterruptedException, ToolchainException {
-
-    // In some cases this is called with a missing configuration, so we skip toolchain context.
-    if (configurationKey == null) {
-      return null;
-    }
-
-    // This call could be combined with the call below, but this SkyFunction is evaluated so rarely
-    // it's not worth optimizing.
-    BuildConfigurationValue value = (BuildConfigurationValue) env.getValue(configurationKey);
-    if (env.valuesMissing()) {
-      return null;
-    }
-    BuildConfiguration configuration = value.getConfiguration();
-
-    // Load the target and host platform keys.
-    PlatformConfiguration platformConfiguration =
-        configuration.getFragment(PlatformConfiguration.class);
-    if (platformConfiguration == null) {
-      return null;
-    }
-    Label hostPlatformLabel = platformConfiguration.getHostPlatform();
-    Label targetPlatformLabel = platformConfiguration.getTargetPlatform();
-
-    ConfiguredTargetKey hostPlatformKey = ConfiguredTargetKey.of(hostPlatformLabel, configuration);
-    ConfiguredTargetKey targetPlatformKey =
-        ConfiguredTargetKey.of(targetPlatformLabel, configuration);
-    ImmutableList<ConfiguredTargetKey> execConstraintKeys =
-        execConstraintLabels
-            .stream()
-            .map(label -> ConfiguredTargetKey.of(label, configuration))
-            .collect(toImmutableList());
-
-    // Load the host and target platforms early, to check for errors.
-    PlatformLookupUtil.getPlatformInfo(ImmutableList.of(hostPlatformKey, targetPlatformKey), env);
-    if (env.valuesMissing()) {
-      return null;
-    }
-
-    // Load all available execution platform keys. This will find any errors in the execution
-    // platform definitions.
-    RegisteredExecutionPlatformsValue registeredExecutionPlatforms =
-        loadRegisteredExecutionPlatforms(env, configurationKey);
-    if (registeredExecutionPlatforms == null) {
-      return null;
-    }
-
-    ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys =
-        new ImmutableList.Builder<ConfiguredTargetKey>()
-            .addAll(registeredExecutionPlatforms.registeredExecutionPlatformKeys())
-            .add(hostPlatformKey)
-            .build();
-
-    // Filter out execution platforms that don't satisfy the extra constraints.
-    boolean debug = configuration.getOptions().get(PlatformOptions.class).toolchainResolutionDebug;
-    availableExecutionPlatformKeys =
-        filterPlatforms(availableExecutionPlatformKeys, execConstraintKeys, env, debug);
-    if (availableExecutionPlatformKeys == null) {
-      return null;
-    }
-
-    ResolvedToolchains resolvedToolchains =
-        resolveToolchainLabels(
-            env,
-            requiredToolchains,
-            configurationKey,
-            hostPlatformKey,
-            availableExecutionPlatformKeys,
-            targetPlatformKey,
-            debug);
-    if (resolvedToolchains == null) {
-      return null;
-    }
-
-    return createContext(
-        env,
-        targetDescription,
-        resolvedToolchains.executionPlatformKey(),
-        resolvedToolchains.targetPlatformKey(),
-        requiredToolchains,
-        resolvedToolchains.toolchains());
-  }
-
-  private static RegisteredExecutionPlatformsValue loadRegisteredExecutionPlatforms(
-      Environment env, BuildConfigurationValue.Key configurationKey)
-      throws InterruptedException, InvalidPlatformException {
-      RegisteredExecutionPlatformsValue registeredExecutionPlatforms =
-          (RegisteredExecutionPlatformsValue)
-              env.getValueOrThrow(
-                  RegisteredExecutionPlatformsValue.key(configurationKey),
-                  InvalidPlatformException.class);
-      if (registeredExecutionPlatforms == null) {
-        return null;
-      }
-      return registeredExecutionPlatforms;
-  }
-
-  /** Data class to hold the result of resolving toolchain labels. */
-  @AutoValue
-  protected abstract static class ResolvedToolchains {
-
-    abstract ConfiguredTargetKey executionPlatformKey();
-
-    abstract ConfiguredTargetKey targetPlatformKey();
-
-    abstract ImmutableBiMap<Label, Label> toolchains();
-
-    protected static ResolvedToolchains create(
-        ConfiguredTargetKey executionPlatformKey,
-        ConfiguredTargetKey targetPlatformKey,
-        Map<Label, Label> toolchains) {
-      return new AutoValue_ToolchainUtil_ResolvedToolchains(
-          executionPlatformKey, targetPlatformKey, ImmutableBiMap.copyOf(toolchains));
-    }
-  }
-
-  @Nullable
-  private static ResolvedToolchains resolveToolchainLabels(
-      Environment env,
-      Set<Label> requiredToolchains,
-      BuildConfigurationValue.Key configurationKey,
-      ConfiguredTargetKey hostPlatformKey,
-      ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
-      ConfiguredTargetKey targetPlatformKey,
-      boolean debug)
-      throws InterruptedException, ToolchainException {
-
-    // Find the toolchains for the required toolchain types.
-    List<ToolchainResolutionValue.Key> registeredToolchainKeys = new ArrayList<>();
-    for (Label toolchainType : requiredToolchains) {
-      registeredToolchainKeys.add(
-          ToolchainResolutionValue.key(
-              configurationKey, toolchainType, targetPlatformKey, availableExecutionPlatformKeys));
-    }
-
-    Map<SkyKey, ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>>
-        results =
-            env.getValuesOrThrow(
-                registeredToolchainKeys,
-                NoToolchainFoundException.class,
-                InvalidToolchainLabelException.class);
-    boolean valuesMissing = false;
-
-    // Determine the potential set of toolchains.
-    Table<ConfiguredTargetKey, Label, Label> resolvedToolchains = HashBasedTable.create();
-    List<Label> missingToolchains = new ArrayList<>();
-    for (Map.Entry<
-            SkyKey, ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>>
-        entry : results.entrySet()) {
-      try {
-        Label requiredToolchainType =
-            ((ToolchainResolutionValue.Key) entry.getKey().argument()).toolchainType();
-        ValueOrException2<NoToolchainFoundException, InvalidToolchainLabelException>
-            valueOrException = entry.getValue();
-        if (valueOrException.get() == null) {
-          valuesMissing = true;
-          continue;
-        }
-
-        ToolchainResolutionValue toolchainResolutionValue =
-            (ToolchainResolutionValue) valueOrException.get();
-        addPlatformsAndLabels(resolvedToolchains, requiredToolchainType, toolchainResolutionValue);
-      } catch (NoToolchainFoundException e) {
-        // Save the missing type and continue looping to check for more.
-        missingToolchains.add(e.missingToolchainType());
-      }
-    }
-
-    if (!missingToolchains.isEmpty()) {
-      throw new UnresolvedToolchainsException(missingToolchains);
-    }
-
-    if (valuesMissing) {
-      return null;
-    }
-
-    // Find and return the first execution platform which has all required toolchains.
-    Optional<ConfiguredTargetKey> selectedExecutionPlatformKey;
-    if (requiredToolchains.isEmpty() && availableExecutionPlatformKeys.contains(hostPlatformKey)) {
-      // Fall back to the legacy behavior: use the host platform if it's available, otherwise the
-      // first execution platform.
-      selectedExecutionPlatformKey = Optional.of(hostPlatformKey);
-    } else {
-      // If there are no toolchains, this will return the first execution platform.
-      selectedExecutionPlatformKey =
-          findExecutionPlatformForToolchains(
-              env, requiredToolchains, availableExecutionPlatformKeys, resolvedToolchains, debug);
-    }
-
-    if (!selectedExecutionPlatformKey.isPresent()) {
-      throw new NoMatchingPlatformException(
-          requiredToolchains, availableExecutionPlatformKeys, targetPlatformKey);
-    }
-
-    return ResolvedToolchains.create(
-        selectedExecutionPlatformKey.get(),
-        targetPlatformKey,
-        resolvedToolchains.row(selectedExecutionPlatformKey.get()));
-  }
-
-  private static Optional<ConfiguredTargetKey> findExecutionPlatformForToolchains(
-      Environment env,
-      Set<Label> requiredToolchains,
-      ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
-      Table<ConfiguredTargetKey, Label, Label> resolvedToolchains,
-      boolean debug) {
-    for (ConfiguredTargetKey executionPlatformKey : availableExecutionPlatformKeys) {
-      // PlatformInfo executionPlatform = platforms.get(executionPlatformKey);
-      Map<Label, Label> toolchains = resolvedToolchains.row(executionPlatformKey);
-      if (!toolchains.keySet().containsAll(requiredToolchains)) {
-        // Not all toolchains are present, keep going
-        continue;
-      }
-
-      if (debug) {
-        env.getListener()
-            .handle(
-                Event.info(
-                    String.format(
-                        "ToolchainUtil: Selected execution platform %s, %s",
-                        executionPlatformKey.getLabel(),
-                        toolchains
-                            .entrySet()
-                            .stream()
-                            .map(
-                                e ->
-                                    String.format(
-                                        "type %s -> toolchain %s", e.getKey(), e.getValue()))
-                            .collect(joining(", ")))));
-      }
-      return Optional.of(executionPlatformKey);
-    }
-
-    return Optional.absent();
-  }
-
-  private static void addPlatformsAndLabels(
-      Table<ConfiguredTargetKey, Label, Label> resolvedToolchains,
-      Label requiredToolchainType,
-      ToolchainResolutionValue toolchainResolutionValue) {
-
-    for (Map.Entry<ConfiguredTargetKey, Label> entry :
-        toolchainResolutionValue.availableToolchainLabels().entrySet()) {
-      resolvedToolchains.put(entry.getKey(), requiredToolchainType, entry.getValue());
-    }
-  }
-
-  @Nullable
-  private static ToolchainContext createContext(
-      Environment env,
-      String targetDescription,
-      ConfiguredTargetKey executionPlatformKey,
-      ConfiguredTargetKey targetPlatformKey,
-      Set<Label> requiredToolchains,
-      ImmutableBiMap<Label, Label> toolchains)
-      throws InterruptedException, InvalidPlatformException {
-
-    Map<ConfiguredTargetKey, PlatformInfo> platforms =
-        PlatformLookupUtil.getPlatformInfo(
-            ImmutableList.of(executionPlatformKey, targetPlatformKey), env);
-
-    if (platforms == null) {
-      return null;
-    }
-
-    return ToolchainContext.create(
-        targetDescription,
-        platforms.get(executionPlatformKey),
-        platforms.get(targetPlatformKey),
-        requiredToolchains,
-        toolchains);
-  }
-
-  @Nullable
-  private static ImmutableList<ConfiguredTargetKey> filterPlatforms(
-      ImmutableList<ConfiguredTargetKey> platformKeys,
-      ImmutableList<ConfiguredTargetKey> constraintKeys,
-      Environment env,
-      boolean debug)
-      throws InterruptedException, InvalidConstraintValueException, InvalidPlatformException {
-
-    // Short circuit if not needed.
-    if (constraintKeys.isEmpty()) {
-      return platformKeys;
-    }
-
-    Map<ConfiguredTargetKey, PlatformInfo> platformInfoMap =
-        PlatformLookupUtil.getPlatformInfo(platformKeys, env);
-    if (platformInfoMap == null) {
-      return null;
-    }
-    List<ConstraintValueInfo> constraints =
-        ConstraintValueLookupUtil.getConstraintValueInfo(constraintKeys, env);
-    if (constraints == null) {
-      return null;
-    }
-
-    return platformKeys
-        .stream()
-        .filter(key -> filterPlatform(platformInfoMap.get(key), constraints, env, debug))
-        .collect(toImmutableList());
-  }
-
-  private static boolean filterPlatform(
-      PlatformInfo platformInfo,
-      List<ConstraintValueInfo> constraints,
-      Environment env,
-      boolean debug) {
-    for (ConstraintValueInfo filterConstraint : constraints) {
-      ConstraintValueInfo platformInfoConstraint =
-          platformInfo.getConstraint(filterConstraint.constraint());
-      if (platformInfoConstraint == null || !platformInfoConstraint.equals(filterConstraint)) {
-        // The value for this setting is not present in the platform, or doesn't match the expected
-        // value.
-        if (debug) {
-          env.getListener()
-              .handle(
-                  Event.info(
-                      String.format(
-                          "ToolchainUtil: Removed execution platform %s from"
-                              + " available execution platforms, it is missing constraint %s",
-                          platformInfo.label(), filterConstraint.label())));
-        }
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-  /** Exception used when no execution platform can be found. */
-  static final class NoMatchingPlatformException extends ToolchainException {
-    NoMatchingPlatformException() {
-      super("No available execution platform satisfies all requested toolchain types");
-    }
-
-    public NoMatchingPlatformException(
-        Set<Label> requiredToolchains,
-        ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
-        ConfiguredTargetKey targetPlatformKey) {
-      super(formatError(requiredToolchains, availableExecutionPlatformKeys, targetPlatformKey));
-    }
-
-    private static String formatError(
-        Set<Label> requiredToolchains,
-        ImmutableList<ConfiguredTargetKey> availableExecutionPlatformKeys,
-        ConfiguredTargetKey targetPlatformKey) {
-      if (requiredToolchains.isEmpty()) {
-        return String.format(
-            "Unable to find an execution platform for target platform %s"
-                + " from available execution platforms [%s]",
-            targetPlatformKey.getLabel(),
-            availableExecutionPlatformKeys
-                .stream()
-                .map(key -> key.getLabel().toString())
-                .collect(Collectors.joining(", ")));
-      }
-      return String.format(
-          "Unable to find an execution platform for toolchains [%s] and target platform %s"
-              + " from available execution platforms [%s]",
-          Joiner.on(", ").join(requiredToolchains),
-          targetPlatformKey.getLabel(),
-          availableExecutionPlatformKeys
-              .stream()
-              .map(key -> key.getLabel().toString())
-              .collect(Collectors.joining(", ")));
-    }
-  }
-
-  /** Exception used when a toolchain type is required but no matching toolchain is found. */
-  public static final class UnresolvedToolchainsException extends ToolchainException {
-    private final ImmutableList<Label> missingToolchainTypes;
-
-    public UnresolvedToolchainsException(List<Label> missingToolchainTypes) {
-      super(
-          String.format(
-              "no matching toolchains found for types %s",
-              Joiner.on(", ").join(missingToolchainTypes)));
-      this.missingToolchainTypes = ImmutableList.copyOf(missingToolchainTypes);
-    }
-
-    public ImmutableList<Label> missingToolchainTypes() {
-      return missingToolchainTypes;
-    }
-  }
-}
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 4f04b02..25ce176 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -818,6 +818,7 @@
         "//src/main/java/com/google/devtools/build/lib/vfs/inmemoryfs",
         "//src/main/java/com/google/devtools/build/skyframe",
         "//src/main/java/com/google/devtools/build/skyframe:skyframe-objects",
+        "//src/test/java/com/google/devtools/build/lib/rules/platform:testutil",
         "//src/test/java/com/google/devtools/build/lib/skyframe:testutil",
         "//third_party:auto_value",
         "//third_party:jsr305",
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainUtilTest.java b/src/test/java/com/google/devtools/build/lib/analysis/ToolchainResolverTest.java
similarity index 61%
rename from src/test/java/com/google/devtools/build/lib/skyframe/ToolchainUtilTest.java
rename to src/test/java/com/google/devtools/build/lib/analysis/ToolchainResolverTest.java
index 8d77206..37efeb2 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainUtilTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/ToolchainResolverTest.java
@@ -1,4 +1,4 @@
-// Copyright 2017 The Bazel Authors. All rights reserved.
+// Copyright 2018 The Bazel Authors. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package com.google.devtools.build.lib.skyframe;
+package com.google.devtools.build.lib.analysis;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
@@ -21,15 +21,16 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
-import com.google.devtools.build.lib.analysis.BlazeDirectories;
-import com.google.devtools.build.lib.analysis.ToolchainContext;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.NoMatchingPlatformException;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.UnloadedToolchainContext;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.UnresolvedToolchainsException;
 import com.google.devtools.build.lib.analysis.util.AnalysisMock;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.rules.platform.ToolchainTestCase;
+import com.google.devtools.build.lib.skyframe.BuildConfigurationValue;
 import com.google.devtools.build.lib.skyframe.ConstraintValueLookupUtil.InvalidConstraintValueException;
 import com.google.devtools.build.lib.skyframe.PlatformLookupUtil.InvalidPlatformException;
-import com.google.devtools.build.lib.skyframe.ToolchainUtil.NoMatchingPlatformException;
-import com.google.devtools.build.lib.skyframe.ToolchainUtil.UnresolvedToolchainsException;
+import com.google.devtools.build.lib.skyframe.ToolchainException;
 import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
 import com.google.devtools.build.skyframe.EvaluationResult;
 import com.google.devtools.build.skyframe.SkyFunction;
@@ -43,17 +44,15 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-/** Tests for {@link ToolchainUtil}. */
+/** Tests for {@link ToolchainResolver}. */
 @RunWith(JUnit4.class)
-public class ToolchainUtilTest extends ToolchainTestCase {
-
+public class ToolchainResolverTest extends ToolchainTestCase {
   /**
-   * An {@link AnalysisMock} that injects {@link CreateToolchainContextFunction} into the Skyframe
+   * An {@link AnalysisMock} that injects {@link ResolveToolchainsFunction} into the Skyframe
    * executor.
    */
-  private static final class AnalysisMockWithCreateToolchainContextFunction
-      extends AnalysisMock.Delegate {
-    AnalysisMockWithCreateToolchainContextFunction() {
+  private static final class LocalAnalysisMock extends AnalysisMock.Delegate {
+    LocalAnalysisMock() {
       super(AnalysisMock.get());
     }
 
@@ -62,18 +61,18 @@
         BlazeDirectories directories) {
       return ImmutableMap.<SkyFunctionName, SkyFunction>builder()
           .putAll(super.getSkyFunctions(directories))
-          .put(CREATE_TOOLCHAIN_CONTEXT_FUNCTION, new CreateToolchainContextFunction())
+          .put(RESOLVE_TOOLCHAINS_FUNCTION, new ResolveToolchainsFunction())
           .build();
     }
   }
 
   @Override
   protected AnalysisMock getAnalysisMock() {
-    return new AnalysisMockWithCreateToolchainContextFunction();
+    return new LocalAnalysisMock();
   }
 
   @Test
-  public void createToolchainContext() throws Exception {
+  public void resolve() throws Exception {
     // This should select platform mac, toolchain extra_toolchain_mac, because platform
     // mac is listed first.
     addToolchain(
@@ -93,58 +92,58 @@
         "register_execution_platforms('//platforms:mac', '//platforms:linux')");
 
     useConfiguration("--platforms=//platforms:linux");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
-            "test", ImmutableSet.of(testToolchainType), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(testToolchainType), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasNoError();
-    ToolchainContext toolchainContext = result.get(key).toolchainContext();
-    assertThat(toolchainContext).isNotNull();
+    UnloadedToolchainContext unloadedToolchainContext = result.get(key).unloadedToolchainContext();
+    assertThat(unloadedToolchainContext).isNotNull();
 
-    assertThat(toolchainContext.requiredToolchainTypes()).containsExactly(testToolchainType);
-    assertThat(toolchainContext.resolvedToolchainLabels())
+    assertThat(unloadedToolchainContext.requiredToolchainTypes())
+        .containsExactly(testToolchainType);
+    assertThat(unloadedToolchainContext.resolvedToolchainLabels())
         .containsExactly(Label.parseAbsoluteUnchecked("//extra:extra_toolchain_mac_impl"));
 
-    assertThat(toolchainContext.executionPlatform()).isNotNull();
-    assertThat(toolchainContext.executionPlatform().label())
+    assertThat(unloadedToolchainContext.executionPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.executionPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:mac"));
 
-    assertThat(toolchainContext.targetPlatform()).isNotNull();
-    assertThat(toolchainContext.targetPlatform().label())
+    assertThat(unloadedToolchainContext.targetPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.targetPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux"));
   }
 
   @Test
-  public void createToolchainContext_noToolchainType() throws Exception {
+  public void resolve_noToolchainType() throws Exception {
     scratch.file("host/BUILD", "platform(name = 'host')");
     rewriteWorkspace("register_execution_platforms('//platforms:mac', '//platforms:linux')");
 
     useConfiguration("--host_platform=//host:host", "--platforms=//platforms:linux");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create("test", ImmutableSet.of(), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasNoError();
-    ToolchainContext toolchainContext = result.get(key).toolchainContext();
-    assertThat(toolchainContext).isNotNull();
+    UnloadedToolchainContext unloadedToolchainContext = result.get(key).unloadedToolchainContext();
+    assertThat(unloadedToolchainContext).isNotNull();
 
-    assertThat(toolchainContext.requiredToolchainTypes()).isEmpty();
+    assertThat(unloadedToolchainContext.requiredToolchainTypes()).isEmpty();
 
     // With no toolchains requested, should fall back to the host platform.
-    assertThat(toolchainContext.executionPlatform()).isNotNull();
-    assertThat(toolchainContext.executionPlatform().label())
+    assertThat(unloadedToolchainContext.executionPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.executionPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//host:host"));
 
-    assertThat(toolchainContext.targetPlatform()).isNotNull();
-    assertThat(toolchainContext.targetPlatform().label())
+    assertThat(unloadedToolchainContext.targetPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.targetPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux"));
   }
 
   @Test
-  public void createToolchainContext_noToolchainType_hostNotAvailable() throws Exception {
+  public void resolve_noToolchainType_hostNotAvailable() throws Exception {
     scratch.file("host/BUILD", "platform(name = 'host')");
     scratch.file(
         "sample/BUILD",
@@ -162,44 +161,41 @@
         "    '//sample:sample_a', '//sample:sample_b')");
 
     useConfiguration("--host_platform=//host:host", "--platforms=//platforms:linux");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(),
             ImmutableSet.of(Label.parseAbsoluteUnchecked("//sample:demo_b")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasNoError();
-    ToolchainContext toolchainContext = result.get(key).toolchainContext();
-    assertThat(toolchainContext).isNotNull();
+    UnloadedToolchainContext unloadedToolchainContext = result.get(key).unloadedToolchainContext();
+    assertThat(unloadedToolchainContext).isNotNull();
 
-    assertThat(toolchainContext.requiredToolchainTypes()).isEmpty();
+    assertThat(unloadedToolchainContext.requiredToolchainTypes()).isEmpty();
 
-    // With no toolchains requested, should fall back to the host platform.
-    assertThat(toolchainContext.executionPlatform()).isNotNull();
-    assertThat(toolchainContext.executionPlatform().label())
+    assertThat(unloadedToolchainContext.executionPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.executionPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//sample:sample_b"));
 
-    assertThat(toolchainContext.targetPlatform()).isNotNull();
-    assertThat(toolchainContext.targetPlatform().label())
+    assertThat(unloadedToolchainContext.targetPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.targetPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux"));
   }
 
   @Test
-  public void createToolchainContext_unavailableToolchainType_single() throws Exception {
-    useConfiguration(
-        "--host_platform=//platforms:linux",
-        "--platforms=//platforms:mac");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+  public void resolve_unavailableToolchainType_single() throws Exception {
+    useConfiguration("--host_platform=//platforms:linux", "--platforms=//platforms:mac");
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(
                 testToolchainType, Label.parseAbsoluteUnchecked("//fake/toolchain:type_1")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result)
         .hasErrorEntryForKeyThat(key)
@@ -213,12 +209,10 @@
   }
 
   @Test
-  public void createToolchainContext_unavailableToolchainType_multiple() throws Exception {
-    useConfiguration(
-        "--host_platform=//platforms:linux",
-        "--platforms=//platforms:mac");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+  public void resolve_unavailableToolchainType_multiple() throws Exception {
+    useConfiguration("--host_platform=//platforms:linux", "--platforms=//platforms:mac");
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(
                 testToolchainType,
@@ -226,7 +220,7 @@
                 Label.parseAbsoluteUnchecked("//fake/toolchain:type_2")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result)
         .hasErrorEntryForKeyThat(key)
@@ -236,14 +230,13 @@
   }
 
   @Test
-  public void createToolchainContext_invalidTargetPlatform_badTarget() throws Exception {
+  public void resolve_invalidTargetPlatform_badTarget() throws Exception {
     scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')");
     useConfiguration("--platforms=//invalid:not_a_platform");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
-            "test", ImmutableSet.of(testToolchainType), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(testToolchainType), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
@@ -260,14 +253,13 @@
   }
 
   @Test
-  public void createToolchainContext_invalidTargetPlatform_badPackage() throws Exception {
+  public void resolve_invalidTargetPlatform_badPackage() throws Exception {
     scratch.resolve("invalid").delete();
     useConfiguration("--platforms=//invalid:not_a_platform");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
-            "test", ImmutableSet.of(testToolchainType), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(testToolchainType), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
@@ -282,14 +274,13 @@
   }
 
   @Test
-  public void createToolchainContext_invalidHostPlatform() throws Exception {
+  public void resolve_invalidHostPlatform() throws Exception {
     scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')");
     useConfiguration("--host_platform=//invalid:not_a_platform");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
-            "test", ImmutableSet.of(testToolchainType), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(testToolchainType), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
@@ -304,14 +295,13 @@
   }
 
   @Test
-  public void createToolchainContext_invalidExecutionPlatform() throws Exception {
+  public void resolve_invalidExecutionPlatform() throws Exception {
     scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')");
     useConfiguration("--extra_execution_platforms=//invalid:not_a_platform");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
-            "test", ImmutableSet.of(testToolchainType), targetConfigKey);
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create("test", ImmutableSet.of(testToolchainType), targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
@@ -326,7 +316,7 @@
   }
 
   @Test
-  public void createToolchainContext_execConstraints() throws Exception {
+  public void resolve_execConstraints() throws Exception {
     // This should select platform linux, toolchain extra_toolchain_linux, due to extra constraints,
     // even though platform mac is registered first.
     addToolchain(
@@ -346,42 +336,43 @@
         "register_execution_platforms('//platforms:mac', '//platforms:linux')");
 
     useConfiguration("--platforms=//platforms:linux");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(testToolchainType),
             ImmutableSet.of(Label.parseAbsoluteUnchecked("//constraints:linux")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasNoError();
-    ToolchainContext toolchainContext = result.get(key).toolchainContext();
-    assertThat(toolchainContext).isNotNull();
+    UnloadedToolchainContext unloadedToolchainContext = result.get(key).unloadedToolchainContext();
+    assertThat(unloadedToolchainContext).isNotNull();
 
-    assertThat(toolchainContext.requiredToolchainTypes()).containsExactly(testToolchainType);
-    assertThat(toolchainContext.resolvedToolchainLabels())
+    assertThat(unloadedToolchainContext.requiredToolchainTypes())
+        .containsExactly(testToolchainType);
+    assertThat(unloadedToolchainContext.resolvedToolchainLabels())
         .containsExactly(Label.parseAbsoluteUnchecked("//extra:extra_toolchain_linux_impl"));
 
-    assertThat(toolchainContext.executionPlatform()).isNotNull();
-    assertThat(toolchainContext.executionPlatform().label())
+    assertThat(unloadedToolchainContext.executionPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.executionPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux"));
 
-    assertThat(toolchainContext.targetPlatform()).isNotNull();
-    assertThat(toolchainContext.targetPlatform().label())
+    assertThat(unloadedToolchainContext.targetPlatform()).isNotNull();
+    assertThat(unloadedToolchainContext.targetPlatform().label())
         .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux"));
   }
 
   @Test
-  public void createToolchainContext_execConstraints_invalid() throws Exception {
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+  public void resolve_execConstraints_invalid() throws Exception {
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(testToolchainType),
             ImmutableSet.of(Label.parseAbsoluteUnchecked("//platforms:linux")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
 
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
@@ -396,7 +387,7 @@
   }
 
   @Test
-  public void createToolchainContext_noMatchingPlatform() throws Exception {
+  public void resolve_noMatchingPlatform() throws Exception {
     // Write toolchain A, and a toolchain implementing it.
     scratch.appendFile(
         "a/BUILD",
@@ -426,15 +417,15 @@
         "register_execution_platforms('//platforms:mac', '//platforms:linux')");
 
     useConfiguration("--platforms=//platforms:linux");
-    CreateToolchainContextKey key =
-        CreateToolchainContextKey.create(
+    ResolveToolchainsKey key =
+        ResolveToolchainsKey.create(
             "test",
             ImmutableSet.of(
                 Label.parseAbsoluteUnchecked("//a:toolchain_type_A"),
                 Label.parseAbsoluteUnchecked("//b:toolchain_type_B")),
             targetConfigKey);
 
-    EvaluationResult<CreateToolchainContextValue> result = createToolchainContext(key);
+    EvaluationResult<ResolveToolchainsValue> result = createToolchainContextBuilder(key);
     assertThatEvaluationResult(result).hasError();
     assertThatEvaluationResult(result)
         .hasErrorEntryForKeyThat(key)
@@ -442,26 +433,25 @@
         .isInstanceOf(NoMatchingPlatformException.class);
   }
 
-  // Calls ToolchainUtil.createToolchainContext.
-  private static final SkyFunctionName CREATE_TOOLCHAIN_CONTEXT_FUNCTION =
-      SkyFunctionName.createHermetic("CREATE_TOOLCHAIN_CONTEXT_FUNCTION");
+  private static final SkyFunctionName RESOLVE_TOOLCHAINS_FUNCTION =
+      SkyFunctionName.createHermetic("RESOLVE_TOOLCHAINS_FUNCTION");
 
   @AutoValue
-  abstract static class CreateToolchainContextKey implements SkyKey {
+  abstract static class ResolveToolchainsKey implements SkyKey {
     @Override
     public SkyFunctionName functionName() {
-      return CREATE_TOOLCHAIN_CONTEXT_FUNCTION;
+      return RESOLVE_TOOLCHAINS_FUNCTION;
     }
 
     abstract String targetDescription();
 
-    abstract ImmutableSet<Label> requiredToolchains();
+    abstract ImmutableSet<Label> requiredToolchainTypes();
 
     abstract ImmutableSet<Label> execConstraintLabels();
 
     abstract BuildConfigurationValue.Key configurationKey();
 
-    public static CreateToolchainContextKey create(
+    public static ResolveToolchainsKey create(
         String targetDescription,
         Set<Label> requiredToolchains,
         BuildConfigurationValue.Key configurationKey) {
@@ -472,12 +462,12 @@
           configurationKey);
     }
 
-    public static CreateToolchainContextKey create(
+    public static ResolveToolchainsKey create(
         String targetDescription,
         Set<Label> requiredToolchains,
         Set<Label> execConstraintLabels,
         BuildConfigurationValue.Key configurationKey) {
-      return new AutoValue_ToolchainUtilTest_CreateToolchainContextKey(
+      return new AutoValue_ToolchainResolverTest_ResolveToolchainsKey(
           targetDescription,
           ImmutableSet.copyOf(requiredToolchains),
           ImmutableSet.copyOf(execConstraintLabels),
@@ -485,8 +475,8 @@
     }
   }
 
-  EvaluationResult<CreateToolchainContextValue> createToolchainContext(
-      CreateToolchainContextKey key) throws InterruptedException {
+  private EvaluationResult<ResolveToolchainsValue> createToolchainContextBuilder(
+      ResolveToolchainsKey key) throws InterruptedException {
     try {
       // Must re-enable analysis for Skyframe functions that create configured targets.
       skyframeExecutor.getSkyframeBuildView().enableAnalysis(true);
@@ -497,46 +487,36 @@
     }
   }
 
-  // TODO(blaze-team): implement equals and hashcode for ToolchainContext and convert this to
-  // autovalue.
-  static class CreateToolchainContextValue implements SkyValue {
-    private final ToolchainContext toolchainContext;
+  @AutoValue
+  abstract static class ResolveToolchainsValue implements SkyValue {
+    abstract UnloadedToolchainContext unloadedToolchainContext();
 
-    private CreateToolchainContextValue(ToolchainContext toolchainContext) {
-      this.toolchainContext = toolchainContext;
-    }
-
-    static CreateToolchainContextValue create(ToolchainContext toolchainContext) {
-      return new CreateToolchainContextValue(toolchainContext);
-    }
-
-    ToolchainContext toolchainContext() {
-      return toolchainContext;
+    static ResolveToolchainsValue create(UnloadedToolchainContext unloadedToolchainContext) {
+      return new AutoValue_ToolchainResolverTest_ResolveToolchainsValue(unloadedToolchainContext);
     }
   }
 
-  private static final class CreateToolchainContextFunction implements SkyFunction {
+  private static final class ResolveToolchainsFunction implements SkyFunction {
 
     @Nullable
     @Override
     public SkyValue compute(SkyKey skyKey, Environment env)
         throws SkyFunctionException, InterruptedException {
-      CreateToolchainContextKey key = (CreateToolchainContextKey) skyKey;
-      ToolchainContext toolchainContext = null;
+      ResolveToolchainsKey key = (ResolveToolchainsKey) skyKey;
+      ToolchainResolver toolchainResolver =
+          new ToolchainResolver(env, key.configurationKey())
+              .setTargetDescription(key.targetDescription())
+              .setRequiredToolchainTypes(key.requiredToolchainTypes())
+              .setExecConstraintLabels(key.execConstraintLabels());
+
       try {
-        toolchainContext =
-            ToolchainUtil.createToolchainContext(
-                env,
-                key.targetDescription(),
-                key.requiredToolchains(),
-                key.execConstraintLabels(),
-                key.configurationKey());
-        if (toolchainContext == null) {
+        UnloadedToolchainContext unloadedToolchainContext = toolchainResolver.resolve();
+        if (unloadedToolchainContext == null) {
           return null;
         }
-        return CreateToolchainContextValue.create(toolchainContext);
+        return ResolveToolchainsValue.create(unloadedToolchainContext);
       } catch (ToolchainException e) {
-        throw new CreateToolchainContextFunctionException(e);
+        throw new ResolveToolchainsFunctionException(e);
       }
     }
 
@@ -547,8 +527,8 @@
     }
   }
 
-  private static class CreateToolchainContextFunctionException extends SkyFunctionException {
-    public CreateToolchainContextFunctionException(ToolchainException e) {
+  private static class ResolveToolchainsFunctionException extends SkyFunctionException {
+    ResolveToolchainsFunctionException(ToolchainException e) {
       super(e, Transience.PERSISTENT);
     }
   }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java
index 1f92bea..fbb2944 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BuildViewForTesting.java
@@ -42,6 +42,8 @@
 import com.google.devtools.build.lib.analysis.RuleContext;
 import com.google.devtools.build.lib.analysis.TargetAndConfiguration;
 import com.google.devtools.build.lib.analysis.ToolchainContext;
+import com.google.devtools.build.lib.analysis.ToolchainResolver;
+import com.google.devtools.build.lib.analysis.ToolchainResolver.UnloadedToolchainContext;
 import com.google.devtools.build.lib.analysis.TopLevelArtifactContext;
 import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
 import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
@@ -78,6 +80,7 @@
 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.SkyFunctionEnvironmentForTesting;
 import com.google.devtools.build.lib.skyframe.SkyframeBuildView;
 import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
 import com.google.devtools.build.lib.skyframe.TargetPatternPhaseValue;
@@ -504,18 +507,22 @@
           Event.error("Failed to get target when trying to get rule context for testing"));
       throw new IllegalStateException(e);
     }
-    Set<Label> requiredToolchains =
+    ImmutableSet<Label> requiredToolchains =
         target.getAssociatedRule().getRuleClassObject().getRequiredToolchains();
-    ToolchainContext toolchainContext =
-        skyframeExecutor.getToolchainContextForTesting(
-            requiredToolchains, targetConfig, eventHandler);
+    SkyFunctionEnvironmentForTesting skyfunctionEnvironment =
+        skyframeExecutor.getSkyFunctionEnvironmentForTesting(eventHandler);
+    UnloadedToolchainContext unloadedToolchainContext =
+        new ToolchainResolver(skyfunctionEnvironment, BuildConfigurationValue.key(targetConfig))
+            .setRequiredToolchainTypes(requiredToolchains)
+            .resolve();
+
     OrderedSetMultimap<Attribute, ConfiguredTargetAndData> prerequisiteMap =
         getPrerequisiteMapForTesting(
             eventHandler,
             configuredTarget,
             configurations,
-            toolchainContext.resolvedToolchainLabels());
-    toolchainContext.resolveToolchains(prerequisiteMap);
+            unloadedToolchainContext.resolvedToolchainLabels());
+    ToolchainContext toolchainContext = unloadedToolchainContext.load(prerequisiteMap);
 
     return new RuleContext.Builder(
             env,
@@ -534,7 +541,7 @@
                 eventHandler,
                 configuredTarget,
                 configurations,
-                toolchainContext.resolvedToolchainLabels()))
+                unloadedToolchainContext.resolvedToolchainLabels()))
         .setConfigConditions(ImmutableMap.<Label, ConfigMatchingProvider>of())
         .setUniversalFragments(ruleClassProvider.getUniversalFragments())
         .setToolchainContext(toolchainContext)