Refactor CppConfigurationLoader

Apart from slight moving of blocks of code nearer to where they are used, this cl does one major thing.

Before:
When we didn't find a label in cc_toolchain_suite.toolchains satisfying '--cpu|--compiler', we iterated over the CROSSTOOL and selected a CToolchain 'foo'. Then we used that 'foo.targetCpu|foo.compiler' to get the label from cc_toolchain_suite.toolchains to get the cc_toolchain 'bar'. So we used (foo, bar).

After:
After we find 'bar', and bar.toolchain_identifier is set, we select the CToolchain 'baz' from the CROSSTOOL matching that toolchain_identifier. Baz doesn't necessarily have to be the same as foo. So we use (foo, baz).

#6072.

RELNOTES: None.
PiperOrigin-RevId: 211981401
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 3c6e91b..7a44b64 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
@@ -32,10 +32,11 @@
 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.vfs.FileSystemUtils;
 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.CToolchain;
 import com.google.devtools.common.options.OptionsParsingException;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -123,19 +124,26 @@
       ConfigurationEnvironment env, BuildOptions options)
       throws InvalidConfigurationException, InterruptedException {
 
-    Label crosstoolTopLabel = RedirectChaser.followRedirects(env,
-        options.get(CppOptions.class).crosstoolTop, "crosstool_top");
+    CppOptions cppOptions = options.get(CppOptions.class);
+    Label crosstoolTopLabel =
+        RedirectChaser.followRedirects(env, cppOptions.crosstoolTop, "crosstool_top");
     if (crosstoolTopLabel == null) {
       return null;
     }
 
-    CppOptions cppOptions = options.get(CppOptions.class);
-    Label stlLabel = null;
-    if (cppOptions.stl != null) {
-      stlLabel = RedirectChaser.followRedirects(env, cppOptions.stl, "stl");
-      if (stlLabel == null) {
-        return null;
-      }
+    Target crosstoolTop;
+    try {
+      crosstoolTop = env.getTarget(crosstoolTopLabel);
+    } catch (NoSuchThingException e) {
+      throw new IllegalStateException(e); // Should have been found out during redirect chasing
+    }
+
+    if (!(crosstoolTop instanceof Rule)
+        || !((Rule) crosstoolTop).getRuleClass().equals("cc_toolchain_suite")) {
+      throw new InvalidConfigurationException(
+          String.format(
+              "The specified --crosstool_top '%s' is not a valid cc_toolchain_suite rule",
+              crosstoolTopLabel));
     }
 
     CrosstoolConfigurationLoader.CrosstoolFile file =
@@ -144,6 +152,52 @@
       return null;
     }
 
+    Options buildOptions = options.get(Options.class);
+    String transformedCpu = cpuTransformer.getTransformer().apply(buildOptions.cpu);
+    String key =
+        transformedCpu + (cppOptions.cppCompiler == null ? "" : ("|" + cppOptions.cppCompiler));
+    Label ccToolchainLabel =
+        selectCcToolchainLabel(
+            options, cppOptions, crosstoolTopLabel, (Rule) crosstoolTop, file, transformedCpu, key);
+
+    Target ccToolchain = loadCcToolchainTarget(env, ccToolchainLabel);
+    if (ccToolchain == null) {
+      return null;
+    }
+
+    // If cc_toolchain_suite contains an entry for the given --cpu and --compiler options, we
+    // select the toolchain by its identifier if "toolchain_identifier" attribute is present.
+    // Otherwise, we fall back to going through the CROSSTOOL file to select the toolchain using
+    // the legacy selection mechanism.
+    String identifier =
+        NonconfigurableAttributeMapper.of((Rule) ccToolchain)
+            .get("toolchain_identifier", Type.STRING);
+    CToolchain cToolchain;
+    if (!identifier.isEmpty()) {
+      cToolchain =
+          CrosstoolConfigurationLoader.getToolchainByIdentifier(
+              file.getProto(), identifier, transformedCpu, cppOptions.cppCompiler);
+    } else {
+      cToolchain =
+          CrosstoolConfigurationLoader.selectToolchain(
+              file.getProto(), options, cpuTransformer.getTransformer());
+    }
+
+    cToolchain =
+        CppToolchainInfo.addLegacyFeatures(
+            cToolchain, crosstoolTopLabel.getPackageIdentifier().getPathUnderExecRoot());
+    CcToolchainConfigInfo ccToolchainConfigInfo = CcToolchainConfigInfo.fromToolchain(cToolchain);
+
+    Label sysrootLabel = getSysrootLabel(cToolchain, cppOptions.libcTopLabel);
+
+    Label stlLabel = null;
+    if (cppOptions.stl != null) {
+      stlLabel = RedirectChaser.followRedirects(env, cppOptions.stl, "stl");
+      if (stlLabel == null) {
+        return null;
+      }
+    }
+
     PathFragment fdoPath = null;
     Label fdoProfileLabel = null;
     if (cppOptions.getFdoOptimize() != null) {
@@ -164,62 +218,22 @@
       }
     }
 
-    Label ccToolchainLabel;
-    Target crosstoolTop;
-    CrosstoolConfig.CToolchain toolchain = null;
-    try {
-      crosstoolTop = env.getTarget(crosstoolTopLabel);
-    } catch (NoSuchThingException e) {
-      throw new IllegalStateException(e);  // Should have been found out during redirect chasing
-    }
+    return new CppConfigurationParameters(
+        file,
+        file.getMd5(),
+        options,
+        fdoPath,
+        fdoProfileLabel,
+        crosstoolTopLabel,
+        ccToolchainLabel,
+        stlLabel,
+        sysrootLabel,
+        cpuTransformer,
+        ccToolchainConfigInfo);
+  }
 
-    String desiredCpu = cpuTransformer.getTransformer().apply(options.get(Options.class).cpu);
-    if (crosstoolTop instanceof Rule
-        && ((Rule) crosstoolTop).getRuleClass().equals("cc_toolchain_suite")) {
-      Rule ccToolchainSuite = (Rule) crosstoolTop;
-      String key =
-          desiredCpu + (cppOptions.cppCompiler == null ? "" : ("|" + cppOptions.cppCompiler));
-      Map<String, Label> toolchains =
-          NonconfigurableAttributeMapper.of(ccToolchainSuite)
-              .get("toolchains", BuildType.LABEL_DICT_UNARY);
-      ccToolchainLabel = toolchains.get(key);
-      String errorMessage =
-          String.format(
-              "cc_toolchain_suite '%s' does not contain a toolchain for CPU '%s'",
-              crosstoolTopLabel, desiredCpu);
-      if (cppOptions.cppCompiler != null) {
-        errorMessage = errorMessage + " and compiler " + cppOptions.cppCompiler;
-      }
-      if (ccToolchainLabel == null) {
-        // If the cc_toolchain_suite does not contain entry for --cpu|--compiler (or only --cpu if
-        // --compiler is not present) we select the toolchain by looping through all the toolchains
-        // in the CROSSTOOL file and selecting the one that matches --cpu (and --compiler, if
-        // present). Then we use the toolchain.target_cpu|toolchain.compiler key to get the
-        // cc_toolchain label.
-        toolchain =
-            CrosstoolConfigurationLoader.selectToolchain(
-                file.getProto(), options, cpuTransformer.getTransformer());
-        ccToolchainLabel = toolchains.get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler());
-        if (cppOptions.disableCcToolchainFromCrosstool) {
-          throw new InvalidConfigurationException(
-              errorMessage
-                  + String.format(
-                      ", you may want to add an entry for '%s|%s' into toolchains and "
-                          + "toolchain_identifier '%s' into the corresponding cc_toolchain rule.",
-                      toolchain.getTargetCpu(),
-                      toolchain.getCompiler(),
-                      toolchain.getToolchainIdentifier()));
-        }
-      }
-      if (ccToolchainLabel == null) {
-        throw new InvalidConfigurationException(errorMessage);
-      }
-    } else {
-      throw new InvalidConfigurationException(String.format(
-          "The specified --crosstool_top '%s' is not a valid cc_toolchain_suite rule",
-          crosstoolTopLabel));
-    }
-
+  private Target loadCcToolchainTarget(ConfigurationEnvironment env, Label ccToolchainLabel)
+      throws InterruptedException, InvalidConfigurationException {
     Target ccToolchain;
     try {
       ccToolchain = env.getTarget(ccToolchainLabel);
@@ -235,46 +249,59 @@
       throw new InvalidConfigurationException(String.format(
           "The label '%s' is not a cc_toolchain rule", ccToolchainLabel));
     }
+    return ccToolchain;
+  }
 
-    if (toolchain == null) {
-      // If cc_toolchain_suite contains an entry for the given --cpu and --compiler options, we
-      // select the toolchain by its identifier if "toolchain_identifier" attribute is present.
-      // Otherwise, we fall back to going through the CROSSTOOL file to select the toolchain using
-      // the legacy selection mechanism.
-      String identifier =
-          NonconfigurableAttributeMapper.of((Rule) ccToolchain)
-              .get("toolchain_identifier", Type.STRING);
-      toolchain =
-          identifier.isEmpty()
-              ? CrosstoolConfigurationLoader.selectToolchain(
-                  file.getProto(), options, cpuTransformer.getTransformer())
-              : CrosstoolConfigurationLoader.getToolchainByIdentifier(
-                  file.getProto(), identifier, desiredCpu, cppOptions.cppCompiler);
+  private Label selectCcToolchainLabel(
+      BuildOptions options,
+      CppOptions cppOptions,
+      Label crosstoolTopLabel,
+      Rule crosstoolTop,
+      CrosstoolFile file,
+      String transformedCpu,
+      String key)
+      throws InvalidConfigurationException {
+    String errorMessage =
+        String.format(
+            "cc_toolchain_suite '%s' does not contain a toolchain for CPU '%s'",
+            crosstoolTopLabel, transformedCpu);
+    if (cppOptions.cppCompiler != null) {
+      errorMessage = errorMessage + " and compiler " + cppOptions.cppCompiler;
     }
-    toolchain =
-        CppToolchainInfo.addLegacyFeatures(
-            toolchain, crosstoolTopLabel.getPackageIdentifier().getPathUnderExecRoot());
 
-    CcToolchainConfigInfo ccToolchainConfigInfo = CcToolchainConfigInfo.fromToolchain(toolchain);
-
-    Label sysrootLabel = getSysrootLabel(toolchain, cppOptions.libcTopLabel);
-
-    return new CppConfigurationParameters(
-        file,
-        file.getMd5(),
-        options,
-        fdoPath,
-        fdoProfileLabel,
-        crosstoolTopLabel,
-        ccToolchainLabel,
-        stlLabel,
-        sysrootLabel,
-        cpuTransformer,
-        ccToolchainConfigInfo);
+    Map<String, Label> toolchains =
+        NonconfigurableAttributeMapper.of(crosstoolTop)
+            .get("toolchains", BuildType.LABEL_DICT_UNARY);
+    Label ccToolchainLabel = toolchains.get(key);
+    if (ccToolchainLabel == null) {
+      // If the cc_toolchain_suite does not contain entry for --cpu|--compiler (or only --cpu if
+      // --compiler is not present) we select the toolchain by looping through all the toolchains
+      // in the CROSSTOOL file and selecting the one that matches --cpu (and --compiler, if
+      // present). Then we use the toolchain.target_cpu|toolchain.compiler key to get the
+      // cc_toolchain label.
+      CToolchain toolchain =
+          CrosstoolConfigurationLoader.selectToolchain(
+              file.getProto(), options, cpuTransformer.getTransformer());
+      ccToolchainLabel = toolchains.get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler());
+      if (cppOptions.disableCcToolchainFromCrosstool) {
+        throw new InvalidConfigurationException(
+            errorMessage
+                + String.format(
+                    ", you may want to add an entry for '%s|%s' into toolchains and "
+                        + "toolchain_identifier '%s' into the corresponding cc_toolchain rule.",
+                    toolchain.getTargetCpu(),
+                    toolchain.getCompiler(),
+                    toolchain.getToolchainIdentifier()));
+      }
+    }
+    if (ccToolchainLabel == null) {
+      throw new InvalidConfigurationException(errorMessage);
+    }
+    return ccToolchainLabel;
   }
 
   @Nullable
-  public static Label getSysrootLabel(CrosstoolConfig.CToolchain toolchain, Label libcTopLabel)
+  public static Label getSysrootLabel(CToolchain toolchain, Label libcTopLabel)
       throws InvalidConfigurationException {
     PathFragment defaultSysroot =
         CppConfiguration.computeDefaultSysroot(toolchain.getBuiltinSysroot());
diff --git a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainSuiteTest.java b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainSuiteTest.java
index 5615adc..16537a1 100644
--- a/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainSuiteTest.java
+++ b/src/test/java/com/google/devtools/build/lib/rules/cpp/CcToolchainSuiteTest.java
@@ -477,8 +477,9 @@
         "       'k8': ':k8-override',",
         "       'k8|compiler': ':k8',",
         "       'k8|compiler-from-attribute': ':k8-override',",
-        "       'ppc|compiler': ':invalid',",
+        "       'ppc|compiler': ':ppc',",
         "       'ppc|compiler-from-attribute': ':ppc-override',",
+        "       'ppc_invalid|compiler': ':invalid',",
         "       'k8|compiler1': ':duplicate',",
         "       'k8|right-compiler': ':wrong-compiler',",
         "       'x64_windows' : ':windows',",
@@ -665,9 +666,11 @@
     assertThat(config.getToolchainIdentifier()).isEqualTo("ppc-from-attribute");
 
     try {
-      useConfiguration("--crosstool_top=//cc:suite", "--compiler=compiler", "--cpu=ppc");
+      useConfiguration("--crosstool_top=//cc:suite", "--compiler=compiler", "--cpu=ppc_invalid");
       getConfiguration(getConfiguredTarget("//a:b")).getFragment(CppConfiguration.class);
-      fail("expected failure because ppc|compiler entry points to an invalid toolchain identifier");
+      fail(
+          "expected failure because ppc_invalid|compiler entry points to an invalid toolchain "
+              + "identifier");
     } catch (InvalidConfigurationException e) {
       assertThat(e)
           .hasMessageThat()