diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java index a22c7a3f327d15..f5ca99384c657a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainRule.java @@ -176,7 +176,6 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) // TODO(b/78578234): Make this the default and remove the late-bound versions. .add(attr("libc_top", LABEL).allowedFileTypes()) .add(attr(LIBC_TOP_ATTR, LABEL).value(LIBC_TOP_VALUE)) - .add(attr(FDO_OPTIMIZE_ATTR, LABEL).singleArtifact().value(FDO_OPTIMIZE_VALUE)) .add( attr(FDO_PROFILE_ATTR, LABEL) @@ -194,6 +193,10 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) .value(CppRuleClasses.LIPO_CONTEXT_COLLECTOR) .skipPrereqValidatorCheck()) .add(attr("proto", Type.STRING)) + .add( + attr("toolchain_identifier", Type.STRING) + .nonconfigurable("Used in configuration creation") + .value("")) .build(); } 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 317d52db40b630..73d6c538c10cc4 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 @@ -55,6 +55,35 @@ * This class represents the C/C++ parts of the {@link BuildConfiguration}, including the host * architecture, target architecture, compiler version, and a standard library version. It has * information about the tools locations and the flags required for compiling. + * + *

Before {@link CppConfiguration} is created, two things need to be done: + * + *

    + *
  1. choosing a {@link CcToolchainRule} label from {@code toolchains} map attribute of {@link + * CcToolchainSuiteRule}. + *
  2. selection of a {@link + * com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.CToolchain} from the + * CROSSTOOL file. + *
+ * + *

The process goes as follows: + * + *

Check for existence of {@link CcToolchainSuiteRule}.toolchains[|], if + * --compiler is specified, otherwise check for {@link CcToolchainSuiteRule}.toolchains[]. + * + *

*/ @AutoCodec @Immutable 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 e20e3a0d4c8088..48ec524c0b0973 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,6 +32,7 @@ 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.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; @@ -142,9 +143,6 @@ protected CppConfigurationParameters createParameters( if (file == null) { return null; } - CrosstoolConfig.CToolchain toolchain = - CrosstoolConfigurationLoader.selectToolchain( - file.getProto(), options, cpuTransformer.getTransformer()); PathFragment fdoPath = null; Label fdoProfileLabel = null; @@ -168,18 +166,17 @@ protected CppConfigurationParameters createParameters( 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 } + 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 desiredCpu = cpuTransformer.getTransformer().apply(options.get(Options.class).cpu); String key = desiredCpu + (cppOptions.cppCompiler == null ? "" : ("|" + cppOptions.cppCompiler)); Map toolchains = @@ -187,13 +184,21 @@ protected CppConfigurationParameters createParameters( .get("toolchains", BuildType.LABEL_DICT_UNARY); 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. + toolchain = + CrosstoolConfigurationLoader.selectToolchain( + file.getProto(), options, cpuTransformer.getTransformer()); ccToolchainLabel = toolchains.get(toolchain.getTargetCpu() + "|" + toolchain.getCompiler()); } if (ccToolchainLabel == null) { String errorMessage = String.format( "cc_toolchain_suite '%s' does not contain a toolchain for CPU '%s'", - crosstoolTopLabel, toolchain.getTargetCpu()); + crosstoolTopLabel, desiredCpu); if (cppOptions.cppCompiler != null) { errorMessage = errorMessage + " and compiler " + cppOptions.cppCompiler; } @@ -221,6 +226,22 @@ protected CppConfigurationParameters createParameters( "The label '%s' is not a cc_toolchain rule", ccToolchainLabel)); } + 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); + } + Label sysrootLabel = getSysrootLabel(toolchain, cppOptions.libcTopLabel); return new CppConfigurationParameters( 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 caed2d37b6ecb7..a475aeb1a2f2e3 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 @@ -18,6 +18,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; +import com.google.common.base.Strings; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.io.BaseEncoding; @@ -375,7 +376,7 @@ public static CrosstoolConfig.CToolchain selectToolchain( + cpuBuilder + "]"); } - checkToolChain(selectedIdentifier, desiredCpu); + checkToolchain(selectedIdentifier, desiredCpu); for (CrosstoolConfig.CToolchain toolchain : release.getToolchainList()) { if (toolchain.getToolchainIdentifier().equals(selectedIdentifier)) { @@ -406,15 +407,14 @@ private static void describeToolchainList(StringBuilder message, } /** - * Makes sure that {@code selectedIdentifier} is a valid identifier for a toolchain, - * i.e. it starts with a letter or an underscore and continues with only dots, dashes, - * spaces, letters, digits or underscores (i.e. matches the following regular expression: - * "[a-zA-Z_][\.\- \w]*"). + * Makes sure that {@code selectedIdentifier} is a valid identifier for a toolchain, i.e. it + * starts with a letter or an underscore and continues with only dots, dashes, spaces, letters, + * digits or underscores (i.e. matches the following regular expression: "[a-zA-Z_][\.\- \w]*"). * - * @throws InvalidConfigurationException if selectedIdentifier does not match the - * aforementioned regular expression. + * @throws InvalidConfigurationException if selectedIdentifier does not match the aforementioned + * regular expression. */ - private static void checkToolChain(String selectedIdentifier, String cpu) + private static void checkToolchain(String selectedIdentifier, String cpu) throws InvalidConfigurationException { // If you update this regex, please do so in the javadoc comment too, and also in the // crosstool_config.proto file. @@ -438,4 +438,47 @@ public static CrosstoolConfig.CrosstoolRelease getCrosstoolReleaseProto( selectToolchain(file.getProto(), options, cpuTransformer); return file.getProto(); } + + /** + * Selects a crosstool toolchain based on the toolchain identifier. + * + * @throws InvalidConfigurationException if no matching toolchain can be found, if multiple + * toolchains with the same identifier are found, or if the target_cpu or compiler of the + * selected toolchain are not the same as --cpu and --compiler options. + */ + public static CrosstoolConfig.CToolchain getToolchainByIdentifier( + CrosstoolConfig.CrosstoolRelease proto, + String toolchainIdentifier, + String cpu, + @Nullable String compiler) + throws InvalidConfigurationException { + checkToolchain(toolchainIdentifier, cpu); + CrosstoolConfig.CToolchain selectedToolchain = null; + for (CrosstoolConfig.CToolchain toolchain : proto.getToolchainList()) { + if (toolchain.getToolchainIdentifier().equals(toolchainIdentifier)) { + if (selectedToolchain != null) { + throw new InvalidConfigurationException( + String.format("Multiple toolchains with '%s' identifier", toolchainIdentifier)); + } + selectedToolchain = toolchain; + } + } + if (selectedToolchain == null) { + throw new InvalidConfigurationException( + String.format("Toolchain identifier '%s' was not found", toolchainIdentifier)); + } + if ((compiler != null && !selectedToolchain.getCompiler().equals(compiler)) + || !selectedToolchain.getTargetCpu().equals(cpu)) { + throw new InvalidConfigurationException( + String.format( + "The selected toolchain's cpu and compiler must match the command line options:\n" + + " --cpu: %s, toolchain.target_cpu: %s\n" + + " --compiler: %s, toolchain.compiler: %s", + cpu, + selectedToolchain.getTargetCpu(), + Strings.nullToEmpty(compiler), + selectedToolchain.getCompiler())); + } + return selectedToolchain; + } } 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 f6925f2d855d32..869601122dd020 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 @@ -380,4 +380,316 @@ public void testCcToolchainLabelFromAttributes() throws Exception { .contains("The label '//cc:invalid-label' is not a cc_toolchain rule"); } } + + @Test + public void testCcToolchainFromToolchainIdentifier() throws Exception { + scratch.file( + "cc/BUILD", + "filegroup(name='empty')", + "filegroup(name='everything')", + "TOOLCHAIN_NAMES = [", + " 'k8',", + " 'ppc',", + " 'darwin',", + " 'windows']", + "[cc_toolchain(", + " name = NAME,", + " cpu = 'banana',", + " all_files = ':empty',", + " ar_files = ':empty',", + " as_files = ':empty',", + " compiler_files = ':empty',", + " dwp_files = ':empty',", + " linker_files = ':empty',", + " strip_files = ':empty',", + " objcopy_files = ':empty',", + " dynamic_runtime_libs = [':empty'],", + " static_runtime_libs = [':empty'],", + ") for NAME in TOOLCHAIN_NAMES]", + "[cc_toolchain(", + " name = NAME + '-override',", + " cpu = 'banana',", + " all_files = ':empty',", + " ar_files = ':empty',", + " as_files = ':empty',", + " compiler_files = ':empty',", + " dwp_files = ':empty',", + " linker_files = ':empty',", + " strip_files = ':empty',", + " objcopy_files = ':empty',", + " dynamic_runtime_libs = [':empty'],", + " static_runtime_libs = [':empty'],", + " toolchain_identifier = NAME + '-from-attribute'", + ") for NAME in TOOLCHAIN_NAMES]", + "cc_toolchain(", + " name = 'invalid',", + " cpu = 'banana',", + " all_files = ':empty',", + " ar_files = ':empty',", + " as_files = ':empty',", + " compiler_files = ':empty',", + " dwp_files = ':empty',", + " linker_files = ':empty',", + " strip_files = ':empty',", + " objcopy_files = ':empty',", + " dynamic_runtime_libs = [':empty'],", + " static_runtime_libs = [':empty'],", + " toolchain_identifier = 'invalid-toolchain',", + ")", + "cc_toolchain(", + " name = 'duplicate',", + " cpu = 'banana',", + " all_files = ':empty',", + " ar_files = ':empty',", + " as_files = ':empty',", + " compiler_files = ':empty',", + " dwp_files = ':empty',", + " linker_files = ':empty',", + " strip_files = ':empty',", + " objcopy_files = ':empty',", + " dynamic_runtime_libs = [':empty'],", + " static_runtime_libs = [':empty'],", + " toolchain_identifier = 'duplicate-toolchain',", + ")", + "cc_toolchain(", + " name = 'wrong-compiler',", + " cpu = 'banana',", + " all_files = ':empty',", + " ar_files = ':empty',", + " as_files = ':empty',", + " compiler_files = ':empty',", + " dwp_files = ':empty',", + " linker_files = ':empty',", + " strip_files = ':empty',", + " objcopy_files = ':empty',", + " dynamic_runtime_libs = [':empty'],", + " static_runtime_libs = [':empty'],", + " toolchain_identifier = 'wrong-compiler-toolchain',", + ")", + "cc_toolchain_suite(", + " name = 'suite',", + " toolchains = {", + " 'k8': ':k8-override',", + " 'k8|compiler': ':k8',", + " 'k8|compiler-from-attribute': ':k8-override',", + " 'ppc|compiler': ':invalid',", + " 'ppc|compiler-from-attribute': ':ppc-override',", + " 'k8|compiler1': ':duplicate',", + " 'k8|right-compiler': ':wrong-compiler',", + " 'x64_windows' : ':windows',", + " 'x64_windows|compiler' : ':windows',", + " 'x64_windows|compiler-from-attribute' : ':windows',", + " 'x64_windows|compiler1' : ':windows',", + " 'x64_windows|right-compiler' : ':windows',", + " 'darwin' : ':darwin',", + " 'darwin|compiler' : ':darwin',", + " 'darwin|compiler-from-attribute' : ':darwin',", + " 'darwin|compiler1' : ':darwin',", + " 'darwin|right-compiler' : ':darwin',", + " },", + " proto = \"\"\"", + "major_version: 'v1'", + "minor_version: '0'", + "default_target_cpu: 'k8'", + "default_toolchain {", + " cpu: 'k8'", + " toolchain_identifier: 'k8-toolchain-identifier'", + "}", + "default_toolchain {", + " cpu: 'ppc'", + " toolchain_identifier: 'ppc-toolchain-identifier'", + "}", + "default_toolchain {", + " cpu: 'darwin'", + " toolchain_identifier: 'darwin-toolchain-identifier'", + "}", + "default_toolchain {", + " cpu: 'x64_windows'", + " toolchain_identifier: 'windows-toolchain-identifier'", + "}", + "toolchain {", + " compiler: 'compiler'", + " target_cpu: 'k8'", + " toolchain_identifier: 'k8-toolchain-identifier'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler-from-attribute'", + " target_cpu: 'k8'", + " toolchain_identifier: 'k8-from-attribute'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler'", + " target_cpu: 'ppc'", + " toolchain_identifier: 'ppc-toolchain-identifier'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler-from-attribute'", + " target_cpu: 'ppc'", + " toolchain_identifier: 'ppc-from-attribute'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler'", + " target_cpu: 'darwin'", + " toolchain_identifier: 'darwin-toolchain-identifier'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: ''", + " abi_libc_version: ''", + " target_libc: ''", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler-from-attribute'", + " target_cpu: 'darwin'", + " toolchain_identifier: 'darwin-from-attribute'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler'", + " target_cpu: 'x64_windows'", + " toolchain_identifier: 'windows-toolchain-identifier'", + " host_system_name: 'windows'", + " target_system_name: 'windows'", + " abi_version: ''", + " abi_libc_version: ''", + " target_libc: ''", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler-from-attribute'", + " target_cpu: 'x64_windows'", + " toolchain_identifier: 'windows-from-attribute'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler1'", + " target_cpu: 'k8'", + " toolchain_identifier: 'duplicate-toolchain'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'compiler2'", + " target_cpu: 'k8'", + " toolchain_identifier: 'duplicate-toolchain'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "toolchain {", + " compiler: 'wrong-compiler'", + " target_cpu: 'k8'", + " toolchain_identifier: 'wrong-compiler-toolchain'", + " host_system_name: 'linux'", + " target_system_name: 'linux'", + " abi_version: 'cpu-abi'", + " abi_libc_version: ''", + " target_libc: 'local'", + " builtin_sysroot: 'sysroot'", + " default_grte_top: '//cc:grtetop'", + "}", + "\"\"\"", + ")"); + + scratch.file("a/BUILD", "cc_binary(name='b', srcs=['b.cc'])"); + + useConfiguration("--crosstool_top=//cc:suite", "--cpu=k8"); + ConfiguredTarget c = getConfiguredTarget("//a:b"); + CppConfiguration config = getConfiguration(c).getFragment(CppConfiguration.class); + assertThat(config.getToolchainIdentifier()).isEqualTo("k8-from-attribute"); + + useConfiguration("--crosstool_top=//cc:suite", "--cpu=ppc"); + c = getConfiguredTarget("//a:b"); + config = getConfiguration(c).getFragment(CppConfiguration.class); + assertThat(config.getToolchainIdentifier()).isEqualTo("ppc-toolchain-identifier"); + + useConfiguration( + "--crosstool_top=//cc:suite", "--compiler=compiler-from-attribute", "--cpu=ppc"); + config = getConfiguration(getConfiguredTarget("//a:b")).getFragment(CppConfiguration.class); + assertThat(config.getToolchainIdentifier()).isEqualTo("ppc-from-attribute"); + + try { + useConfiguration("--crosstool_top=//cc:suite", "--compiler=compiler", "--cpu=ppc"); + getConfiguration(getConfiguredTarget("//a:b")).getFragment(CppConfiguration.class); + fail("expected failure because ppc|compiler entry points to an invalid toolchain identifier"); + } catch (InvalidConfigurationException e) { + assertThat(e) + .hasMessageThat() + .contains("Toolchain identifier 'invalid-toolchain' was not found"); + } + + try { + useConfiguration("--crosstool_top=//cc:suite", "--compiler=compiler1", "--cpu=k8"); + getConfiguration(getConfiguredTarget("//a:b")).getFragment(CppConfiguration.class); + fail("expected failure because of duplicate toolchain identifier"); + } catch (InvalidConfigurationException e) { + assertThat(e) + .hasMessageThat() + .contains("Multiple toolchains with 'duplicate-toolchain' identifier"); + } + + try { + useConfiguration("--crosstool_top=//cc:suite", "--compiler=right-compiler", "--cpu=k8"); + getConfiguration(getConfiguredTarget("//a:b")).getFragment(CppConfiguration.class); + fail("expected failure because toolchain.compiler does not equal --compiler"); + } catch (InvalidConfigurationException e) { + assertThat(e) + .hasMessageThat() + .contains( + "The selected toolchain's cpu and compiler must match the command line options:\n" + + " --cpu: k8, toolchain.target_cpu: k8\n" + + " --compiler: right-compiler, toolchain.compiler: wrong-compiler"); + } + } }