Do not depend on CppConfiguration to provide CROSSTOOL file to cc_toolchain

Use CcSkyframeSupportFunction instead.

#6072

This is a rollforward of unknown commit after fixing loading CROSSTOOL from different package roots.

RELNOTES: None.
PiperOrigin-RevId: 214394291
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
index 226f135..f613d8e 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRulesModule.java
@@ -20,8 +20,8 @@
 import com.google.devtools.build.lib.analysis.config.BuildOptions;
 import com.google.devtools.build.lib.bazel.rules.cpp.BazelCppRuleClasses;
 import com.google.devtools.build.lib.bazel.rules.sh.BazelShRuleClasses;
+import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportFunction;
 import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportValue;
-import com.google.devtools.build.lib.rules.cpp.CcSupportFunction;
 import com.google.devtools.build.lib.runtime.BlazeModule;
 import com.google.devtools.build.lib.runtime.BlazeRuntime;
 import com.google.devtools.build.lib.runtime.Command;
@@ -99,7 +99,8 @@
   @Override
   public void workspaceInit(
       BlazeRuntime runtime, BlazeDirectories directories, WorkspaceBuilder builder) {
-    builder.addSkyFunction(CcSkyframeSupportValue.SKYFUNCTION, new CcSupportFunction(directories));
+    builder.addSkyFunction(
+        CcSkyframeSupportValue.SKYFUNCTION, new CcSkyframeSupportFunction(directories));
   }
 
   @Override
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportFunction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportFunction.java
new file mode 100644
index 0000000..6334d1e
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportFunction.java
@@ -0,0 +1,123 @@
+// Copyright 2014 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.rules.cpp;
+
+import com.google.common.base.Preconditions;
+import com.google.devtools.build.lib.actions.FileValue;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
+import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.skyframe.PackageLookupValue;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.Path;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
+import com.google.devtools.build.skyframe.SkyFunction;
+import com.google.devtools.build.skyframe.SkyFunctionException;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.annotation.Nullable;
+
+/**
+ * A {@link SkyFunction} that does things for FDO that a regular configured target is not allowed
+ * to.
+ *
+ * <p>This only exists because the value of {@code --fdo_optimize} can be a workspace-relative path
+ * and thus we need to depend on {@link BlazeDirectories} somehow, which neither the configuration
+ * nor the analysis phase can "officially" do.
+ *
+ * <p>The fix is probably to make it possible for {@link
+ * com.google.devtools.build.lib.analysis.actions.SymlinkAction} to create workspace-relative
+ * symlinks because action execution can hopefully depend on {@link BlazeDirectories}.
+ *
+ * <p>There is also the awful and incorrect {@link Path#exists()} call in {@link
+ * com.google.devtools.build.lib.view.proto.CcProtoProfileProvider#getProfile(
+ * com.google.devtools.build.lib.analysis.RuleContext)} which needs a {@link Path}.
+ */
+public class CcSkyframeSupportFunction implements SkyFunction {
+
+  private final BlazeDirectories directories;
+
+  public CcSkyframeSupportFunction(BlazeDirectories directories) {
+    this.directories = Preconditions.checkNotNull(directories);
+  }
+
+  @Nullable
+  @Override
+  public SkyValue compute(SkyKey skyKey, Environment env)
+      throws InterruptedException, CcSkyframeSupportException {
+    CcSkyframeSupportValue.Key key = (CcSkyframeSupportValue.Key) skyKey.argument();
+    Path fdoZipPath = null;
+    if (key.getFdoZipPath() != null) {
+      fdoZipPath = directories.getWorkspace().getRelative(key.getFdoZipPath());
+    }
+
+    CrosstoolRelease crosstoolRelease = null;
+    if (key.getCcToolchainSuiteLabel() != null) {
+      try {
+        // 1. Lookup the package to handle multiple package roots (PackageLookupValue)
+        PackageIdentifier packageIdentifier = key.getCcToolchainSuiteLabel().getPackageIdentifier();
+        PackageLookupValue crosstoolPackageValue =
+            (PackageLookupValue) env.getValue(PackageLookupValue.key(packageIdentifier));
+        if (env.valuesMissing()) {
+          return null;
+        }
+
+        // 2. Get crosstool file (FileValue)
+        PathFragment crosstool =
+            packageIdentifier
+                .getPackageFragment()
+                .getRelative(CrosstoolConfigurationLoader.CROSSTOOL_CONFIGURATION_FILENAME);
+        FileValue crosstoolFileValue =
+            (FileValue)
+                env.getValue(
+                    FileValue.key(
+                        RootedPath.toRootedPath(crosstoolPackageValue.getRoot(), crosstool)));
+        if (env.valuesMissing()) {
+          return null;
+        }
+
+        // 3. Parse the crosstool file the into CrosstoolRelease
+        Path crosstoolFile = crosstoolFileValue.realRootedPath().asPath();
+        try (InputStream inputStream = crosstoolFile.getInputStream()) {
+          String crosstoolContent = new String(FileSystemUtils.readContentAsLatin1(inputStream));
+          crosstoolRelease =
+              CrosstoolConfigurationLoader.toReleaseConfiguration(
+                  "CROSSTOOL file " + crosstool, () -> crosstoolContent, /* digestOrNull= */ null);
+        }
+      } catch (IOException | InvalidConfigurationException e) {
+        throw new CcSkyframeSupportException(e, key);
+      }
+    }
+
+    return new CcSkyframeSupportValue(fdoZipPath, crosstoolRelease);
+  }
+
+  @Nullable
+  @Override
+  public String extractTag(SkyKey skyKey) {
+    return null;
+  }
+
+  /** Exception encapsulating IOExceptions thrown in {@link CcSkyframeSupportFunction} */
+  public static class CcSkyframeSupportException extends SkyFunctionException {
+
+    public CcSkyframeSupportException(Exception cause, SkyKey childKey) {
+      super(cause, childKey);
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportValue.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportValue.java
index 19aef12..d097230 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportValue.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSkyframeSupportValue.java
@@ -14,23 +14,26 @@
 package com.google.devtools.build.lib.rules.cpp;
 
 import com.google.common.collect.Interner;
+import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.concurrent.BlazeInterners;
 import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
 import java.util.Objects;
+import javax.annotation.Nullable;
 
 /**
  * A container for the path to the FDO profile.
  *
- * <p>{@link CcSkyframeSupportValue} is created from {@link CcSupportFunction} (a {@link
+ * <p>{@link CcSkyframeSupportValue} is created from {@link CcSkyframeSupportFunction} (a {@link
  * SkyFunction}), which is requested from Skyframe by the {@code cc_toolchain}/{@code
- * cc_toolchain_suite} rule. It's done this way because the path depends on both a command line
+ * cc_toolchain_suite} rules. It's done this way because the path depends on both a command line
  * argument and the location of the workspace and the latter is not available either during
  * configuration creation or during the analysis phase.
  */
@@ -45,20 +48,28 @@
   public static class Key implements SkyKey {
     private static final Interner<Key> interner = BlazeInterners.newWeakInterner();
 
-    private final PathFragment filePath;
+    @Nullable private final PathFragment fdoZipPath;
+    @Nullable private final Label ccToolchainSuiteLabel;
 
-    private Key(PathFragment filePath) {
-      this.filePath = filePath;
+    private Key(PathFragment fdoZipPath, Label ccToolchainSuiteLabel) {
+      this.fdoZipPath = fdoZipPath;
+      this.ccToolchainSuiteLabel = ccToolchainSuiteLabel;
     }
 
     @AutoCodec.Instantiator
     @AutoCodec.VisibleForSerialization
-    static Key of(PathFragment filePath) {
-      return interner.intern(new Key(filePath));
+    static Key of(PathFragment fdoZipPath, Label ccToolchainSuiteLabel) {
+      return interner.intern(new Key(fdoZipPath, ccToolchainSuiteLabel));
     }
 
-    public PathFragment getFilePath() {
-      return filePath;
+    @Nullable
+    public Label getCcToolchainSuiteLabel() {
+      return ccToolchainSuiteLabel;
+    }
+
+    @Nullable
+    public PathFragment getFdoZipPath() {
+      return fdoZipPath;
     }
 
     @Override
@@ -66,18 +77,18 @@
       if (this == o) {
         return true;
       }
-
       if (!(o instanceof Key)) {
         return false;
       }
-
-      Key that = (Key) o;
-      return Objects.equals(this.filePath, that.filePath);
+      Key key = (Key) o;
+      return Objects.equals(fdoZipPath, key.fdoZipPath)
+          && Objects.equals(ccToolchainSuiteLabel, key.ccToolchainSuiteLabel);
     }
 
     @Override
     public int hashCode() {
-      return Objects.hash(filePath);
+
+      return Objects.hash(fdoZipPath, ccToolchainSuiteLabel);
     }
 
     @Override
@@ -88,19 +99,26 @@
 
   /** Path of the profile file passed to {@code --fdo_optimize} */
   // TODO(lberki): This should be a PathFragment.
-  // Except that CcProtoProfileProvider#getProfile() calls #exists() on it, which is ridiculously
-  // incorrect.
-  private final Path filePath;
+  // Except that CcProtoProfileProvider#getProfile() calls #exists() on it,
+  // This is all ridiculously incorrect and should be removed asap.
+  private final Path fdoZipPath;
 
-  CcSkyframeSupportValue(Path filePath) {
-    this.filePath = filePath;
+  private final CrosstoolRelease crosstoolRelease;
+
+  CcSkyframeSupportValue(Path fdoZipPath, CrosstoolRelease crosstoolRelease) {
+    this.fdoZipPath = fdoZipPath;
+    this.crosstoolRelease = crosstoolRelease;
   }
 
-  public Path getFilePath() {
-    return filePath;
+  public Path getFdoZipPath() {
+    return fdoZipPath;
   }
 
-  public static SkyKey key(PathFragment fdoProfileArgument) {
-    return Key.of(fdoProfileArgument);
+  public CrosstoolRelease getCrosstoolRelease() {
+    return crosstoolRelease;
+  }
+
+  public static SkyKey key(PathFragment fdoZipPath, Label ccToolchainSuiteLabel) {
+    return Key.of(fdoZipPath, ccToolchainSuiteLabel);
   }
 }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSupportFunction.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSupportFunction.java
deleted file mode 100644
index 113361f..0000000
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcSupportFunction.java
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2014 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.rules.cpp;
-
-import com.google.common.base.Preconditions;
-import com.google.devtools.build.lib.analysis.BlazeDirectories;
-import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.skyframe.SkyFunction;
-import com.google.devtools.build.skyframe.SkyKey;
-import com.google.devtools.build.skyframe.SkyValue;
-import javax.annotation.Nullable;
-
-/**
- * A {@link SkyFunction} that does things for FDO that a regular configured target is not allowed
- * to.
- *
- * <p>This only exists because the value of {@code --fdo_optimize} can be a workspace-relative path
- * and thus we need to depend on {@link BlazeDirectories} somehow, which neither the configuration
- * nor the analysis phase can "officially" do.
- *
- * <p>The fix is probably to make it possible for {@link
- * com.google.devtools.build.lib.analysis.actions.SymlinkAction} to create workspace-relative
- * symlinks because action execution can hopefully depend on {@link BlazeDirectories}.
- *
- * <p>There is also the awful and incorrect {@link Path#exists()} call in {@link
- * com.google.devtools.build.lib.view.proto.CcProtoProfileProvider#getProfile(
- * com.google.devtools.build.lib.analysis.RuleContext)} which needs a {@link Path}.
- */
-public class CcSupportFunction implements SkyFunction {
-  private final BlazeDirectories directories;
-
-  public CcSupportFunction(BlazeDirectories directories) {
-    this.directories = Preconditions.checkNotNull(directories);
-  }
-
-  @Nullable
-  @Override
-  public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException {
-    CcSkyframeSupportValue.Key key = (CcSkyframeSupportValue.Key) skyKey.argument();
-    Path resolvedPath =
-        key.getFilePath() == null
-            ? null
-            : directories.getWorkspace().getRelative(key.getFilePath());
-    return new CcSkyframeSupportValue(resolvedPath);
-  }
-
-  @Nullable
-  @Override
-  public String extractTag(SkyKey skyKey) {
-    return null;
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
index 3fc13b0..903d014 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchain.java
@@ -14,6 +14,7 @@
 package com.google.devtools.build.lib.rules.cpp;
 
 import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
+import static com.google.devtools.build.lib.syntax.Type.STRING;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -52,15 +53,18 @@
 import com.google.devtools.build.lib.collect.nestedset.Order;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.packages.License;
+import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportFunction.CcSkyframeSupportException;
 import com.google.devtools.build.lib.rules.cpp.CppConfiguration.Tool;
 import com.google.devtools.build.lib.rules.cpp.FdoProvider.FdoMode;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.util.FileType;
 import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.util.StringUtil;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.protobuf.TextFormat;
@@ -326,7 +330,6 @@
     BuildConfiguration configuration = Preconditions.checkNotNull(ruleContext.getConfiguration());
     CppConfiguration cppConfiguration =
         Preconditions.checkNotNull(configuration.getFragment(CppConfiguration.class));
-    CppToolchainInfo toolchainInfo = getCppToolchainInfo(ruleContext, cppConfiguration);
 
     PathFragment fdoZip = null;
     FdoInputFile prefetchHints = null;
@@ -407,14 +410,32 @@
       return null;
     }
 
-    SkyKey fdoKey = CcSkyframeSupportValue.key(fdoZip);
+    // Is there a toolchain proto available on the target directly?
+    CToolchain toolchain = parseToolchainFromAttributes(ruleContext);
+    Label ccToolchainSuiteLabelIfNeeded = null;
+    if (toolchain == null && cppConfiguration.getCrosstoolFromCcToolchainProtoAttribute() == null) {
+      ccToolchainSuiteLabelIfNeeded = cppConfiguration.getCrosstoolTop();
+    }
+
+    SkyKey ccSupportKey = CcSkyframeSupportValue.key(fdoZip, ccToolchainSuiteLabelIfNeeded);
 
     SkyFunction.Environment skyframeEnv = ruleContext.getAnalysisEnvironment().getSkyframeEnv();
-    CcSkyframeSupportValue fdoSupport = (CcSkyframeSupportValue) skyframeEnv.getValue(fdoKey);
+    CcSkyframeSupportValue ccSkyframeSupportValue;
+    try {
+      ccSkyframeSupportValue =
+          (CcSkyframeSupportValue)
+              skyframeEnv.getValueOrThrow(ccSupportKey, CcSkyframeSupportException.class);
+    } catch (CcSkyframeSupportException e) {
+      ruleContext.throwWithRuleError(e.getMessage());
+      throw new IllegalStateException("Should not be reached");
+    }
     if (skyframeEnv.valuesMissing()) {
      return null;
     }
 
+    CppToolchainInfo toolchainInfo =
+        getCppToolchainInfo(ruleContext, cppConfiguration, ccSkyframeSupportValue, toolchain);
+
     final Label label = ruleContext.getLabel();
     final NestedSet<Artifact> crosstool = ruleContext.getPrerequisite("all_files", Mode.HOST)
         .getProvider(FileProvider.class).getFilesToBuild();
@@ -550,12 +571,12 @@
     if (fdoMode == FdoMode.LLVM_FDO) {
       profileArtifact =
           convertLLVMRawProfileToIndexed(
-              fdoSupport.getFilePath().asFragment(), toolchainInfo, ruleContext);
+              ccSkyframeSupportValue.getFdoZipPath().asFragment(), toolchainInfo, ruleContext);
       if (ruleContext.hasErrors()) {
         return null;
       }
     } else if (fdoMode == FdoMode.AUTO_FDO || fdoMode == FdoMode.XBINARY_FDO) {
-      Path fdoProfile = fdoSupport.getFilePath();
+      Path fdoProfile = ccSkyframeSupportValue.getFdoZipPath();
       profileArtifact = ruleContext.getUniqueDirectoryArtifact(
               "fdo",
               fdoProfile.getBaseName(),
@@ -608,7 +629,7 @@
             sysroot,
             fdoMode,
             new FdoProvider(
-                fdoSupport.getFilePath(),
+                ccSkyframeSupportValue.getFdoZipPath(),
                 fdoMode,
                 cppConfiguration.getFdoInstrument(),
                 profileArtifact,
@@ -653,7 +674,11 @@
 
   /** Finds an appropriate {@link CppToolchainInfo} for this target. */
   private CppToolchainInfo getCppToolchainInfo(
-      RuleContext ruleContext, CppConfiguration cppConfiguration) throws RuleErrorException {
+      RuleContext ruleContext,
+      CppConfiguration cppConfiguration,
+      CcSkyframeSupportValue ccSkyframeSupportValue,
+      CToolchain toolchainFromCcToolchainAttribute)
+      throws RuleErrorException {
 
     if (cppConfiguration.enableCcToolchainConfigInfoFromSkylark()) {
       // Attempt to obtain CppToolchainInfo from the 'toolchain_config' attribute of cc_toolchain.
@@ -676,7 +701,10 @@
     }
 
     // Attempt to find a toolchain based on the target attributes, not the configuration.
-    CToolchain toolchain = getToolchainFromAttributes(ruleContext, cppConfiguration);
+    CToolchain toolchain = toolchainFromCcToolchainAttribute;
+    if (toolchain == null) {
+      toolchain = getToolchainFromAttributes(ruleContext, cppConfiguration, ccSkyframeSupportValue);
+    }
 
     // If we found a toolchain, use it.
     try {
@@ -699,15 +727,14 @@
   @Nullable
   private CToolchain parseToolchainFromAttributes(RuleContext ruleContext)
       throws RuleErrorException {
-    if (ruleContext.attributes().get("proto", Type.STRING).isEmpty()) {
+    String protoAttribute = StringUtil.emptyToNull(ruleContext.attributes().get("proto", STRING));
+    if (protoAttribute == null) {
       return null;
     }
 
-    String data = ruleContext.attributes().get("proto", Type.STRING);
-
     CToolchain.Builder builder = CToolchain.newBuilder();
     try {
-      TextFormat.merge(data, builder);
+      TextFormat.merge(protoAttribute, builder);
       return builder.build();
     } catch (ParseException e) {
       throw ruleContext.throwWithAttributeError("proto", "Could not parse CToolchain data");
@@ -753,25 +780,31 @@
 
   @Nullable
   private CToolchain getToolchainFromAttributes(
-      RuleContext ruleContext, CppConfiguration cppConfiguration) throws RuleErrorException {
-
-    // Is there a toolchain proto available on the target directly?
-    CToolchain toolchain = parseToolchainFromAttributes(ruleContext);
-    if (toolchain != null) {
-      return toolchain;
-    }
+      RuleContext ruleContext,
+      CppConfiguration cppConfiguration,
+      CcSkyframeSupportValue ccSkyframeSupportValue)
+      throws RuleErrorException {
 
     String toolchainIdentifier = ruleContext.attributes().get("toolchain_identifier", Type.STRING);
     String cpu = ruleContext.attributes().get("cpu", Type.STRING);
     String compiler = ruleContext.attributes().get("compiler", Type.STRING);
     try {
+      CrosstoolRelease crosstoolRelease;
+      if (cppConfiguration.getCrosstoolFromCcToolchainProtoAttribute() != null) {
+        // We have cc_toolchain_suite.proto attribute set, let's use it
+        crosstoolRelease = cppConfiguration.getCrosstoolFromCcToolchainProtoAttribute();
+      } else {
+        // We use the proto from the CROSSTOOL file
+        crosstoolRelease = ccSkyframeSupportValue.getCrosstoolRelease();
+      }
+
       return CToolchainSelectionUtils.selectCToolchain(
           toolchainIdentifier,
           cpu,
           compiler,
           cppConfiguration.getTransformedCpuFromOptions(),
           cppConfiguration.getCompilerFromOptions(),
-          cppConfiguration.getCrosstoolFile().getProto());
+          crosstoolRelease);
     } catch (InvalidConfigurationException e) {
       ruleContext.throwWithRuleError(
           String.format("Error while selecting cc_toolchain: %s", e.getMessage()));
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
index 0c9e75f..d148191 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfiguration.java
@@ -34,13 +34,13 @@
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.rules.cpp.CppConfigurationLoader.CppConfigurationParameters;
-import com.google.devtools.build.lib.rules.cpp.CrosstoolConfigurationLoader.CrosstoolFile;
 import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
 import com.google.devtools.build.lib.skylarkbuildapi.cpp.CppConfigurationApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
 import java.util.Map;
 import javax.annotation.Nullable;
 
@@ -175,9 +175,16 @@
   public static final String FDO_STAMP_MACRO = "BUILD_FDO_TYPE";
 
   private final Label crosstoolTop;
+  /**
+   * cc_toolchain_suite allows to override CROSSTOOL by using proto attribute. This attribute value
+   * is stored here so cc_toolchain can access it in the analysis. Don't use this for anything, it
+   * will be removed when b/113849758 is fixed. If you do, I'll send bubo to take your keyboard
+   * away.
+   */
+  @Deprecated private final CrosstoolRelease crosstoolFromCcToolchainProtoAttribute;
+
   private final String transformedCpuFromOptions;
   private final String compilerFromOptions;
-  private final CrosstoolFile crosstoolFile;
   // TODO(lberki): desiredCpu *should* be always the same as targetCpu, except that we don't check
   // that the CPU we get from the toolchain matches BuildConfiguration.Options.cpu . So we store
   // it here so that the output directory doesn't depend on the CToolchain. When we will eventually
@@ -264,9 +271,9 @@
 
     return new CppConfiguration(
         params.crosstoolTop,
+        params.crosstoolFromCcToolchainProtoAttribute,
         params.transformedCpu,
         params.compiler,
-        params.crosstoolFile,
         Preconditions.checkNotNull(params.commonOptions.cpu),
         crosstoolTopPathFragment,
         params.fdoPath,
@@ -301,9 +308,9 @@
 
   private CppConfiguration(
       Label crosstoolTop,
+      CrosstoolRelease crosstoolFromCcToolchainProtoAttribute,
       String transformedCpuFromOptions,
       String compilerFromOptions,
-      CrosstoolFile crosstoolFile,
       String desiredCpu,
       PathFragment crosstoolTopPathFragment,
       PathFragment fdoPath,
@@ -330,9 +337,9 @@
       boolean shouldProvideMakeVariables,
       CppToolchainInfo cppToolchainInfo) {
     this.crosstoolTop = crosstoolTop;
+    this.crosstoolFromCcToolchainProtoAttribute = crosstoolFromCcToolchainProtoAttribute;
     this.transformedCpuFromOptions = transformedCpuFromOptions;
     this.compilerFromOptions = compilerFromOptions;
-    this.crosstoolFile = crosstoolFile;
     this.desiredCpu = desiredCpu;
     this.crosstoolTopPathFragment = crosstoolTopPathFragment;
     this.fdoPath = fdoPath;
@@ -390,11 +397,6 @@
     return cppToolchainInfo.getToolchainIdentifier();
   }
 
-  /** Returns the contents of the CROSSTOOL for this configuration. */
-  public CrosstoolFile getCrosstoolFile() {
-    return crosstoolFile;
-  }
-
   /** Returns the label of the CROSSTOOL for this configuration. */
   public Label getCrosstoolTop() {
     return crosstoolTop;
@@ -1180,6 +1182,17 @@
     return cppOptions.disableMakeVariables || !cppOptions.enableMakeVariables;
   }
 
+  /**
+   * cc_toolchain_suite allows to override CROSSTOOL by using proto attribute. This attribute value
+   * is stored here so cc_toolchain can access it in the analysis. Don't use this for anything, it
+   * will be removed when b/113849758 is fixed. If you do, I'll send bubo to take your keyboard
+   * away.
+   */
+  @Deprecated
+  public CrosstoolRelease getCrosstoolFromCcToolchainProtoAttribute() {
+    return crosstoolFromCcToolchainProtoAttribute;
+  }
+
   public boolean enableLinkoptsInUserLinkFlags() {
     return cppOptions.enableLinkoptsInUserLinkFlags;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationLoader.java
index d45a5a8..6e26904 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CppConfigurationLoader.java
@@ -14,6 +14,8 @@
 
 package com.google.devtools.build.lib.rules.cpp;
 
+import static com.google.devtools.build.lib.rules.cpp.CrosstoolConfigurationLoader.toReleaseConfiguration;
+
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.analysis.RedirectChaser;
@@ -32,11 +34,12 @@
 import com.google.devtools.build.lib.packages.NonconfigurableAttributeMapper;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Target;
-import com.google.devtools.build.lib.rules.cpp.CrosstoolConfigurationLoader.CrosstoolFile;
 import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.StringUtil;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.PathFragment;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain;
+import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
 import com.google.devtools.common.options.OptionsParsingException;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -79,8 +82,6 @@
    * Value class for all the data needed to create a {@link CppConfiguration}.
    */
   public static class CppConfigurationParameters {
-    protected final CrosstoolConfigurationLoader.CrosstoolFile crosstoolFile;
-    protected final String cacheKeySuffix;
     protected final BuildConfiguration.Options commonOptions;
     protected final CppOptions cppOptions;
     protected final Label crosstoolTop;
@@ -92,12 +93,12 @@
     protected final CcToolchainConfigInfo ccToolchainConfigInfo;
     protected final String transformedCpu;
     protected final String compiler;
+    protected final CrosstoolRelease crosstoolFromCcToolchainProtoAttribute;
 
     CppConfigurationParameters(
         String transformedCpu,
         String compiler,
-        CrosstoolFile crosstoolFile,
-        String cacheKeySuffix,
+        CrosstoolRelease crosstoolFromCcToolchainProtoAttribute,
         BuildOptions buildOptions,
         PathFragment fdoPath,
         Label fdoOptimizeLabel,
@@ -108,8 +109,7 @@
         CcToolchainConfigInfo ccToolchainConfigInfo) {
       this.transformedCpu = transformedCpu;
       this.compiler = compiler;
-      this.crosstoolFile = crosstoolFile;
-      this.cacheKeySuffix = cacheKeySuffix;
+      this.crosstoolFromCcToolchainProtoAttribute = crosstoolFromCcToolchainProtoAttribute;
       this.commonOptions = buildOptions.get(BuildConfiguration.Options.class);
       this.cppOptions = buildOptions.get(CppOptions.class);
       this.fdoPath = fdoPath;
@@ -149,9 +149,9 @@
               crosstoolTopLabel));
     }
 
-    CrosstoolConfigurationLoader.CrosstoolFile file =
+    CrosstoolRelease crosstoolRelease =
         CrosstoolConfigurationLoader.readCrosstool(env, crosstoolTopLabel);
-    if (file == null) {
+    if (crosstoolRelease == null) {
       return null;
     }
 
@@ -161,7 +161,12 @@
         transformedCpu + (cppOptions.cppCompiler == null ? "" : ("|" + cppOptions.cppCompiler));
     Label ccToolchainLabel =
         selectCcToolchainLabel(
-            cppOptions, crosstoolTopLabel, (Rule) crosstoolTop, file, transformedCpu, key);
+            cppOptions,
+            crosstoolTopLabel,
+            (Rule) crosstoolTop,
+            crosstoolRelease,
+            transformedCpu,
+            key);
 
     Target ccToolchain = loadCcToolchainTarget(env, ccToolchainLabel);
     if (ccToolchain == null) {
@@ -187,7 +192,7 @@
             compilerAttribute,
             transformedCpu,
             cppOptions.cppCompiler,
-            file.getProto());
+            crosstoolRelease);
 
     cToolchain =
         CppToolchainInfo.addLegacyFeatures(
@@ -204,6 +209,19 @@
       }
     }
 
+    String ccToolchainSuiteProtoAttributeValue =
+        StringUtil.emptyToNull(
+            NonconfigurableAttributeMapper.of((Rule) crosstoolTop).get("proto", Type.STRING));
+
+    CrosstoolRelease crosstoolFromCcToolchainProtoAttribute = null;
+    if (ccToolchainSuiteProtoAttributeValue != null) {
+      crosstoolFromCcToolchainProtoAttribute =
+          toReleaseConfiguration(
+              "cc_toolchain_suite rule " + crosstoolTopLabel,
+              () -> ccToolchainSuiteProtoAttributeValue,
+              /* digestOrNull= */ null);
+    }
+
     PathFragment fdoPath = null;
     Label fdoProfileLabel = null;
     if (cppOptions.getFdoOptimize() != null) {
@@ -227,8 +245,7 @@
     return new CppConfigurationParameters(
         transformedCpu,
         cppOptions.cppCompiler,
-        file,
-        file.getMd5(),
+        crosstoolFromCcToolchainProtoAttribute,
         options,
         fdoPath,
         fdoProfileLabel,
@@ -263,7 +280,7 @@
       CppOptions cppOptions,
       Label crosstoolTopLabel,
       Rule crosstoolTop,
-      CrosstoolFile file,
+      CrosstoolRelease crosstoolRelease,
       String transformedCpu,
       String key)
       throws InvalidConfigurationException {
@@ -293,7 +310,7 @@
               /* compilerAttribute= */ null,
               transformedCpu,
               compiler,
-              file.getProto());
+              crosstoolRelease);
       ccToolchainLabel = toolchains.get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler());
       if (cppOptions.disableCcToolchainFromCrosstool) {
         throw new InvalidConfigurationException(
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoader.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoader.java
index 693b1a5..cafbdaf 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoader.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoader.java
@@ -29,12 +29,10 @@
 import com.google.devtools.build.lib.packages.Package;
 import com.google.devtools.build.lib.packages.Rule;
 import com.google.devtools.build.lib.packages.Target;
-import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.syntax.Type;
 import com.google.devtools.build.lib.util.Fingerprint;
 import com.google.devtools.build.lib.vfs.FileSystemUtils;
 import com.google.devtools.build.lib.vfs.Path;
-import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig;
 import com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CrosstoolRelease;
 import com.google.protobuf.TextFormat;
 import com.google.protobuf.TextFormat.ParseException;
@@ -53,7 +51,7 @@
  * done in a saner way.
  */
 public class CrosstoolConfigurationLoader {
-  private static final String CROSSTOOL_CONFIGURATION_FILENAME = "CROSSTOOL";
+  static final String CROSSTOOL_CONFIGURATION_FILENAME = "CROSSTOOL";
 
   /**
    * Cache for storing result of toReleaseConfiguration function based on path and md5 sum of
@@ -61,110 +59,91 @@
    */
   private static final Cache<String, CrosstoolRelease> crosstoolReleaseCache =
       CacheBuilder.newBuilder().concurrencyLevel(4).maximumSize(100).build();
-  /** A class that holds the results of reading a CROSSTOOL file. */
-  @AutoCodec
-  public static class CrosstoolFile {
-    private final String location;
-    private final CrosstoolConfig.CrosstoolRelease proto;
-    private final String md5;
 
-    @AutoCodec.Instantiator
-    CrosstoolFile(String location, CrosstoolConfig.CrosstoolRelease proto, String md5) {
-      this.location = location;
-      this.proto = proto;
-      this.md5 = md5;
-    }
+  private CrosstoolConfigurationLoader() {}
 
-    /**
-     * Returns a user-friendly location of the CROSSTOOL proto for error messages.
-     */
-    public String getLocation() {
-      return location;
-    }
-
-    /**
-     * Returns the parsed contents of the CROSSTOOL file.
-     */
-    public CrosstoolConfig.CrosstoolRelease getProto() {
-      return proto;
-    }
-
-    /**
-     * Returns an MD5 hash of the CROSSTOOL file contents.
-     */
-    public String getMd5() {
-      return md5;
-    }
-  }
-
-  private CrosstoolConfigurationLoader() {
+  @FunctionalInterface
+  interface CrosstoolDataFunction {
+    String apply() throws IOException;
   }
 
   /**
-   * Reads the given <code>data</code> String, which must be in ascii format,
-   * into a protocol buffer. It uses the <code>name</code> parameter for error
-   * messages.
+   * Reads the given <code>data</code> String, which must be in ascii format, into a protocol
+   * buffer. It uses the <code>name</code> parameter for error messages.
    *
+   * @param name for the error messages
+   * @param dataFunction returns data in the text proto format
+   * @param digestOrNull to be used or null; will be computed from data when null
    * @throws IOException if the parsing failed
    */
   @VisibleForTesting
-  static CrosstoolConfig.CrosstoolRelease toReleaseConfiguration(String name, String data)
-      throws IOException {
-    CrosstoolConfig.CrosstoolRelease.Builder builder =
-        CrosstoolConfig.CrosstoolRelease.newBuilder();
+  public static CrosstoolRelease toReleaseConfiguration(
+      String name, CrosstoolDataFunction dataFunction, String digestOrNull)
+      throws InvalidConfigurationException {
+    if (digestOrNull != null) {
+      // We were given the digest, let's first consult the cache before reading the data, saving
+      // not only proto parsing time, but also IO in case of cache hit.
+      return getCachedReleaseConfiguration(name, dataFunction, digestOrNull);
+    } else {
+      // We were not given the digest, we have to read the data to compute the digest. We will
+      // still save the proto parsing time in case of the cache hit.
+      try {
+        String data = dataFunction.apply();
+        String digest = computeCrosstoolDigest(data);
+        return getCachedReleaseConfiguration(name, () -> data, digest);
+      } catch (IOException e) {
+        throw new InvalidConfigurationException(e);
+      }
+    }
+  }
+
+  private static CrosstoolRelease getCachedReleaseConfiguration(
+      String name, CrosstoolDataFunction dataFunction, String digest)
+      throws InvalidConfigurationException {
     try {
-      TextFormat.merge(data, builder);
+      return crosstoolReleaseCache.get(
+          digest, () -> getUncachedReleaseConfiguration(name, dataFunction));
+    } catch (ExecutionException e) {
+      throw new InvalidConfigurationException(e.getCause().getMessage());
+    }
+  }
+
+  @VisibleForTesting
+  static CrosstoolRelease getUncachedReleaseConfiguration(
+      String name, CrosstoolDataFunction dataFunction)
+      throws IOException, InvalidConfigurationException {
+    CrosstoolRelease.Builder builder = CrosstoolRelease.newBuilder();
+    try {
+      TextFormat.merge(dataFunction.apply(), builder);
       return builder.build();
     } catch (ParseException e) {
-      throw new IOException("Could not read the crosstool configuration file '" + name + "', "
-          + "because of a parser error (" + e.getMessage() + ")");
+      throw new InvalidConfigurationException(
+          "Could not read the crosstool configuration file '"
+              + name
+              + "', "
+              + "because of a parser error ("
+              + e.getMessage()
+              + ")");
     } catch (UninitializedMessageException e) {
-      throw new IOException("Could not read the crosstool configuration file '" + name + "', "
-          + "because of an incomplete protocol buffer (" + e.getMessage() + ")");
+      throw new InvalidConfigurationException(
+          "Could not read the crosstool configuration file '"
+              + name
+              + "', "
+              + "because of an incomplete protocol buffer ("
+              + e.getMessage()
+              + ")");
     }
   }
 
-  /**
-   * This class is the in-memory representation of a text-formatted Crosstool proto file.
-   *
-   * <p>This layer of abstraction is here so that we can load these protos either from BUILD files
-   * or from CROSSTOOL files.
-   *
-   * <p>An implementation of this class should override {@link #getContents()} and call
-   * the constructor with the MD5 checksum of what that method will return and a human-readable name
-   * used in error messages.
-   */
-  private abstract static class CrosstoolProto {
-    private final byte[] md5;
-    private final String name;
-
-    private CrosstoolProto(byte[] md5, String name) {
-      this.md5 = md5;
-      this.name = name;
-    }
-
-    /**
-     * The binary MD5 checksum of the proto.
-     */
-    public byte[] getMd5() {
-      return md5;
-    }
-
-    /**
-     * A user-friendly string describing the location of the proto.
-     */
-    public String getName() {
-      return name;
-    }
-
-    /**
-     * The proto itself.
-     */
-    public abstract String getContents() throws IOException;
+  private static String computeCrosstoolDigest(String data) {
+    return BaseEncoding.base16()
+        .lowerCase()
+        .encode(new Fingerprint().addBytes(data.getBytes(UTF_8)).digestAndReset());
   }
 
-  private static CrosstoolProto getCrosstoolProtofromBuildFile(
-      ConfigurationEnvironment env, Label crosstoolTop) throws InterruptedException {
+  private static CrosstoolRelease getCrosstoolProtoFromBuildFile(
+      ConfigurationEnvironment env, Label crosstoolTop)
+      throws InterruptedException, InvalidConfigurationException {
     Target target;
     try {
       target = env.getTarget(crosstoolTop);
@@ -183,22 +162,23 @@
     }
 
     final String contents = NonconfigurableAttributeMapper.of(rule).get("proto", Type.STRING);
-    byte[] md5 = new Fingerprint().addBytes(contents.getBytes(UTF_8)).digestAndReset();
-    return new CrosstoolProto(md5, "cc_toolchain_suite rule " + crosstoolTop.toString()) {
-
-      @Override
-      public String getContents() throws IOException {
-        return contents;
-      }
-    };
+    return toReleaseConfiguration(
+        "cc_toolchain_suite rule " + crosstoolTop, () -> contents, /* digestOrNull= */ null);
   }
 
-  private static CrosstoolProto getCrosstoolProtoFromCrosstoolFile(
+  private static CrosstoolRelease findCrosstoolConfiguration(
       ConfigurationEnvironment env, Label crosstoolTop)
       throws IOException, InvalidConfigurationException, InterruptedException {
-    final Path path;
+
+    CrosstoolRelease crosstoolProtoFromBuildFile =
+        getCrosstoolProtoFromBuildFile(env, crosstoolTop);
+    if (crosstoolProtoFromBuildFile != null) {
+      return crosstoolProtoFromBuildFile;
+    }
+    Path path;
     try {
-      Package containingPackage = env.getTarget(crosstoolTop).getPackage();
+      Package containingPackage;
+      containingPackage = env.getTarget(crosstoolTop).getPackage();
       if (containingPackage == null) {
         return null;
       }
@@ -209,54 +189,33 @@
     }
 
     if (path == null || !path.exists()) {
-      return null;
+      // Normally you'd expect to return null when path is null (so Skyframe computes the value),
+      // and throw when path doesn't exist. But because {@link ConfigurationFragmentFunction}
+      // doesn't propagate the exceptions when there are valuesMissing(), we need to throw
+      // this exception always just to be sure it gets through.
+      throw new InvalidConfigurationException(
+          "The crosstool_top you specified was resolved to '"
+              + crosstoolTop
+              + "', which does not contain a CROSSTOOL file.");
     }
 
-    return new CrosstoolProto(path.getDigest(), "CROSSTOOL file " + path.getPathString()) {
-      @Override
-      public String getContents() throws IOException {
-        try (InputStream inputStream = path.getInputStream()) {
-          return new String(FileSystemUtils.readContentAsLatin1(inputStream));
-        }
-      }
-    };
-  }
-
-  private static CrosstoolFile findCrosstoolConfiguration(
-      ConfigurationEnvironment env, Label crosstoolTop)
-      throws IOException, InvalidConfigurationException, InterruptedException {
-
-    CrosstoolProto crosstoolProto = getCrosstoolProtofromBuildFile(env, crosstoolTop);
-    if (crosstoolProto == null) {
-      crosstoolProto = getCrosstoolProtoFromCrosstoolFile(env, crosstoolTop);
-    }
-
-    if (crosstoolProto == null) {
-      throw new InvalidConfigurationException("The crosstool_top you specified was resolved to '" +
-          crosstoolTop + "', which does not contain a CROSSTOOL file.");
-    } else {
-      // Do this before we read the data, so if it changes, we get a different MD5 the next time.
-      // Alternatively, we could calculate the MD5 of the contents, which we also read, but this
-      // is faster if the file comes from a file system with md5 support.
-      final CrosstoolProto finalProto = crosstoolProto;
-      String md5 = BaseEncoding.base16().lowerCase().encode(finalProto.getMd5());
-      CrosstoolConfig.CrosstoolRelease release;
-      try {
-        release =
-            crosstoolReleaseCache.get(
-                md5, () -> toReleaseConfiguration(finalProto.getName(), finalProto.getContents()));
-      } catch (ExecutionException e) {
-        throw new InvalidConfigurationException(e);
-      }
-
-      return new CrosstoolFile(finalProto.getName(), release, md5);
-    }
+    // Do this before we read the data, so if it changes, we get a different MD5 the next time.
+    // Alternatively, we could calculate the MD5 of the contents, which we also read, but this
+    // is faster if the file comes from a file system with md5 support.
+    String digest = BaseEncoding.base16().lowerCase().encode(path.getDigest());
+    return toReleaseConfiguration(
+        "CROSSTOOL file " + path,
+        () -> {
+          try (InputStream inputStream = path.getInputStream()) {
+            return new String(FileSystemUtils.readContentAsLatin1(inputStream));
+          }
+        },
+        digest);
   }
 
   /** Reads a crosstool file. */
   @Nullable
-  public static CrosstoolConfigurationLoader.CrosstoolFile readCrosstool(
-      ConfigurationEnvironment env, Label crosstoolTop)
+  public static CrosstoolRelease readCrosstool(ConfigurationEnvironment env, Label crosstoolTop)
       throws InvalidConfigurationException, InterruptedException {
     crosstoolTop = RedirectChaser.followRedirects(env, crosstoolTop, "crosstool_top");
     if (crosstoolTop == null) {
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
index 03368f63..588e526 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
@@ -26,8 +26,8 @@
 import com.google.devtools.build.lib.packages.util.MockCcSupport;
 import com.google.devtools.build.lib.packages.util.MockPythonSupport;
 import com.google.devtools.build.lib.packages.util.MockToolsConfig;
+import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportFunction;
 import com.google.devtools.build.lib.rules.cpp.CcSkyframeSupportValue;
-import com.google.devtools.build.lib.rules.cpp.CcSupportFunction;
 import com.google.devtools.build.lib.rules.repository.LocalRepositoryFunction;
 import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
 import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
@@ -136,7 +136,7 @@
         SkyFunctions.REPOSITORY,
         new RepositoryLoaderFunction(),
         CcSkyframeSupportValue.SKYFUNCTION,
-        new CcSupportFunction(directories));
+        new CcSkyframeSupportFunction(directories));
   }
 
   public static class Delegate extends AnalysisMock {
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java
index 843fc93c..74bb8f5 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainTest.java
@@ -105,6 +105,7 @@
                 .setSupportsInterfaceSharedObjects(false)
                 .buildPartial());
     useConfiguration();
+    invalidatePackages();
 
     ConfiguredTarget target = getConfiguredTarget("//a:b");
     CcToolchainProvider toolchainProvider =
@@ -115,6 +116,7 @@
         .isFalse();
 
     useConfiguration("--interface_shared_objects");
+    invalidatePackages();
     target = getConfiguredTarget("//a:b");
     toolchainProvider = (CcToolchainProvider) target.get(ToolchainInfo.PROVIDER);
     assertThat(
@@ -130,6 +132,7 @@
                 .setSupportsInterfaceSharedObjects(true)
                 .buildPartial());
     useConfiguration();
+    invalidatePackages();
 
     target = getConfiguredTarget("//a:b");
     toolchainProvider = (CcToolchainProvider) target.get(ToolchainInfo.PROVIDER);
@@ -139,6 +142,7 @@
         .isTrue();
 
     useConfiguration("--nointerface_shared_objects");
+    invalidatePackages();
     target = getConfiguredTarget("//a:b");
     toolchainProvider = (CcToolchainProvider) target.get(ToolchainInfo.PROVIDER);
     assertThat(
@@ -484,6 +488,7 @@
                   .buildPartial());
 
       useConfiguration();
+      invalidatePackages();
 
       ConfiguredTarget target = getConfiguredTarget("//a:b");
       CcToolchainProvider toolchainProvider =
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java
index de8db5b..7b6b87d 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CrosstoolConfigurationLoaderTest.java
@@ -783,9 +783,10 @@
   @Test
   public void testIncompleteFile() throws Exception {
     try {
-      CrosstoolConfigurationLoader.toReleaseConfiguration("/CROSSTOOL", "major_version: \"12\"");
+      CrosstoolConfigurationLoader.getUncachedReleaseConfiguration(
+          "/CROSSTOOL", () -> "major_version: \"12\"");
       fail();
-    } catch (IOException e) {
+    } catch (InvalidConfigurationException e) {
       assertStringStartsWith(
           "Could not read the crosstool configuration file "
               + "'/CROSSTOOL', because of an incomplete protocol buffer",
@@ -870,9 +871,10 @@
   @Test
   public void testInvalidFile() throws Exception {
     try {
-      CrosstoolConfigurationLoader.toReleaseConfiguration("/CROSSTOOL", "some xxx : yak \"");
+      CrosstoolConfigurationLoader.getUncachedReleaseConfiguration(
+          "/CROSSTOOL", () -> "some xxx : yak \"");
       fail();
-    } catch (IOException e) {
+    } catch (InvalidConfigurationException e) {
       assertStringStartsWith(
           "Could not read the crosstool configuration file "
               + "'/CROSSTOOL', because of a parser error",