Support optional toolchains with `find_cpp_toolchain`
Rules that want to use optional toolchains but still support builds that don't use C++ toolchain resolution should point their `_cc_toolchain` attribute to the new
`@bazel_tools//tools/cpp:optional_current_cc_toolchain` target.
Fixes #16966
Closes #16968.
PiperOrigin-RevId: 505707967
Change-Id: I251749348f982ae063b51f7d9cc0078a1b61a948
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAliasRule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAliasRule.java
index 8ed40b6..5fb5f9b 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAliasRule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcToolchainAliasRule.java
@@ -33,6 +33,7 @@
import com.google.devtools.build.lib.analysis.TemplateVariableInfo;
import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.Type;
import javax.annotation.Nullable;
/** Implementation of the {@code cc_toolchain_alias} rule. */
@@ -50,6 +51,7 @@
.add(
attr(CcToolchain.CC_TOOLCHAIN_TYPE_ATTRIBUTE_NAME, NODEP_LABEL)
.value(CppRuleClasses.ccToolchainTypeAttribute(env)))
+ .add(attr("mandatory", Type.BOOLEAN).value(true))
.requiresConfigurationFragments(PlatformConfiguration.class)
.addToolchainTypes(CppRuleClasses.ccToolchainTypeRequirement(env))
.build();
diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
index 31eb6f4..7c2e208 100644
--- a/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
+++ b/src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
@@ -201,18 +201,22 @@
_CPP_TOOLCHAIN_TYPE = "@" + objc_semantics.get_repo() + "//tools/cpp:toolchain_type"
-def _find_cpp_toolchain(ctx):
+def _find_cpp_toolchain(ctx, *, mandatory = True):
"""
Finds the c++ toolchain.
If the c++ toolchain is in use, returns it. Otherwise, returns a c++
- toolchain derived from legacy toolchain selection.
+ toolchain derived from legacy toolchain selection, constructed from
+ the CppConfiguration.
Args:
ctx: The rule context for which to find a toolchain.
+ mandatory: If this is set to False, this function will return None rather
+ than fail if no toolchain is found.
Returns:
- A CcToolchainProvider.
+ A CcToolchainProvider, or None if the c++ toolchain is declared as
+ optional, mandatory is False and no toolchain has been found.
"""
# Check the incompatible flag for toolchain resolution.
@@ -221,6 +225,9 @@
fail("In order to use find_cpp_toolchain, you must include the '//tools/cpp:toolchain_type' in the toolchains argument to your rule.")
toolchain_info = ctx.toolchains[_CPP_TOOLCHAIN_TYPE]
if toolchain_info == None:
+ if not mandatory:
+ return None
+
# No cpp toolchain was found, so report an error.
fail("Unable to find a CC toolchain using toolchain resolution. Target: %s, Platform: %s, Exec platform: %s" %
(ctx.label, ctx.fragments.platform.platform, ctx.fragments.platform.host_platform))
diff --git a/src/main/starlark/builtins_bzl/common/cc/cc_toolchain_alias.bzl b/src/main/starlark/builtins_bzl/common/cc/cc_toolchain_alias.bzl
index a46de25..34c7c94 100644
--- a/src/main/starlark/builtins_bzl/common/cc/cc_toolchain_alias.bzl
+++ b/src/main/starlark/builtins_bzl/common/cc/cc_toolchain_alias.bzl
@@ -22,7 +22,9 @@
ToolchainInfo = _builtins.toplevel.platform_common.ToolchainInfo
def _impl(ctx):
- cc_toolchain = cc_helper.find_cpp_toolchain(ctx)
+ cc_toolchain = cc_helper.find_cpp_toolchain(ctx, mandatory = ctx.attr.mandatory)
+ if not cc_toolchain:
+ return []
make_variables = cc_toolchain.get_additional_make_variables()
cc_provider_make_variables = cc_helper.get_toolchain_global_make_variables(cc_toolchain)
template_variable_info = TemplateVariableInfo(make_variables | cc_provider_make_variables)
@@ -43,6 +45,7 @@
implementation = _impl,
fragments = ["cpp", "platform"],
attrs = {
+ "mandatory": attr.bool(default = True),
"_cc_toolchain": attr.label(default = configuration_field(fragment = "cpp", name = "cc_toolchain"), providers = [CcToolchainInfo]),
"_cc_toolchain_type": attr.label(default = "@" + semantics.get_repo() + "//tools/cpp:toolchain_type"),
},
diff --git a/src/test/shell/bazel/cc_integration_test.sh b/src/test/shell/bazel/cc_integration_test.sh
index db19f50..9bcb7be 100755
--- a/src/test/shell/bazel/cc_integration_test.sh
+++ b/src/test/shell/bazel/cc_integration_test.sh
@@ -1910,4 +1910,71 @@
expect_log "runtime error: index 10 out of bounds"
}
+function setup_find_optional_cpp_toolchain() {
+ mkdir -p pkg
+
+ cat > pkg/BUILD <<'EOF'
+load(":rules.bzl", "my_rule")
+
+my_rule(
+ name = "my_rule",
+)
+
+platform(
+ name = "exotic_platform",
+ constraint_values = [
+ "@platforms//cpu:wasm64",
+ "@platforms//os:windows",
+ ],
+)
+EOF
+
+ cat > pkg/rules.bzl <<'EOF'
+load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain", "use_cpp_toolchain")
+
+def _my_rule_impl(ctx):
+ out = ctx.actions.declare_file(ctx.attr.name)
+ toolchain = find_cpp_toolchain(ctx, mandatory = False)
+ if toolchain:
+ ctx.actions.write(out, "Toolchain found")
+ else:
+ ctx.actions.write(out, "Toolchain not found")
+ return [DefaultInfo(files = depset([out]))]
+
+my_rule = rule(
+ implementation = _my_rule_impl,
+ attrs = {
+ "_cc_toolchain": attr.label(
+ default = "@bazel_tools//tools/cpp:optional_current_cc_toolchain",
+ ),
+ },
+ toolchains = use_cpp_toolchain(mandatory = False),
+)
+EOF
+}
+
+function test_find_optional_cpp_toolchain_present_without_toolchain_resolution() {
+ setup_find_optional_cpp_toolchain
+
+ bazel build //pkg:my_rule --noincompatible_enable_cc_toolchain_resolution \
+ &> "$TEST_log" || fail "Build failed"
+ assert_contains "Toolchain found" bazel-bin/pkg/my_rule
+}
+
+function test_find_optional_cpp_toolchain_present_with_toolchain_resolution() {
+ setup_find_optional_cpp_toolchain
+
+ bazel build //pkg:my_rule --incompatible_enable_cc_toolchain_resolution \
+ &> "$TEST_log" || fail "Build failed"
+ assert_contains "Toolchain found" bazel-bin/pkg/my_rule
+}
+
+function test_find_optional_cpp_toolchain_not_present_with_toolchain_resolution() {
+ setup_find_optional_cpp_toolchain
+
+ bazel build //pkg:my_rule --incompatible_enable_cc_toolchain_resolution \
+ --platforms=//pkg:exotic_platform &> "$TEST_log" || fail "Build failed"
+ assert_contains "Toolchain not found" bazel-bin/pkg/my_rule
+}
+
run_suite "cc_integration_test"
diff --git a/tools/cpp/BUILD.tools b/tools/cpp/BUILD.tools
index 1e292c3..41b5d66 100644
--- a/tools/cpp/BUILD.tools
+++ b/tools/cpp/BUILD.tools
@@ -57,12 +57,9 @@
cc_toolchain_alias(name = "current_cc_toolchain")
-# In future versions of Bazel, this target will not fail if no C++ toolchain is
-# available. Instead, it will not advertise the cc_common.CcToolchainInfo
-# provider.
-alias(
+cc_toolchain_alias(
name = "optional_current_cc_toolchain",
- actual = ":current_cc_toolchain",
+ mandatory = False,
)
cc_host_toolchain_alias(name = "current_cc_host_toolchain")
diff --git a/tools/cpp/toolchain_utils.bzl b/tools/cpp/toolchain_utils.bzl
index 1583aed..9bf5bc4 100644
--- a/tools/cpp/toolchain_utils.bzl
+++ b/tools/cpp/toolchain_utils.bzl
@@ -29,12 +29,14 @@
Args:
ctx: The rule context for which to find a toolchain.
- mandatory: This is currently a no-op. In future releases of Bazel, if this
- is set to False, this function will return None rather than fail if no
- toolchain is found.
+ mandatory: If this is set to False, this function will return None rather
+ than fail if no toolchain is found. To use this parameter, the calling
+ rule should have a `_cc_toolchain` label attribute with default
+ `@bazel_tools//tools/cpp:optional_current_cc_toolchain`.
Returns:
- A CcToolchainProvider.
+ A CcToolchainProvider, or None if the c++ toolchain is declared as
+ optional, mandatory is False and no toolchain has been found.
"""
# Check the incompatible flag for toolchain resolution.
@@ -43,6 +45,9 @@
fail("In order to use find_cpp_toolchain, you must include the '%s' in the toolchains argument to your rule." % CPP_TOOLCHAIN_TYPE)
toolchain_info = ctx.toolchains[CPP_TOOLCHAIN_TYPE]
if toolchain_info == None:
+ if not mandatory:
+ return None
+
# No cpp toolchain was found, so report an error.
fail("Unable to find a CC toolchain using toolchain resolution. Target: %s, Platform: %s, Exec platform: %s" %
(ctx.label, ctx.fragments.platform.platform, ctx.fragments.platform.host_platform))