From 6b126d60c319c574ed598eb38c0bdcc6b10fde03 Mon Sep 17 00:00:00 2001 From: Fabian Meumertzheim Date: Mon, 30 Jan 2023 08:57:23 -0800 Subject: [PATCH] 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 --- .../lib/rules/cpp/CcToolchainAliasRule.java | 2 + .../builtins_bzl/common/cc/cc_helper.bzl | 13 +++- .../common/cc/cc_toolchain_alias.bzl | 5 +- src/test/shell/bazel/cc_integration_test.sh | 67 +++++++++++++++++++ tools/cpp/BUILD.tools | 7 +- tools/cpp/toolchain_utils.bzl | 13 ++-- 6 files changed, 94 insertions(+), 13 deletions(-) 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 8ed40b6fb31116..5fb5f9bd794fd9 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 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env) .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 31eb6f47bef1e9..7c2e20865ffd9b 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 @@ def _get_dynamic_library_for_runtime_or_none(library, linking_statically): _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 @@ def _find_cpp_toolchain(ctx): 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 a46de25761e473..34c7c945680b7d 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 @@ TemplateVariableInfo = _builtins.toplevel.platform_common.TemplateVariableInfo 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 @@ cc_toolchain_alias = rule( 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 db19f5026a23ef..9bcb7bed650dc8 100755 --- a/src/test/shell/bazel/cc_integration_test.sh +++ b/src/test/shell/bazel/cc_integration_test.sh @@ -1910,4 +1910,71 @@ EOF 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 1e292c3b130404..41b5d666002be7 100644 --- a/tools/cpp/BUILD.tools +++ b/tools/cpp/BUILD.tools @@ -57,12 +57,9 @@ constraint_value( 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 1583aedcc1873f..9bf5bc4ecc980a 100644 --- a/tools/cpp/toolchain_utils.bzl +++ b/tools/cpp/toolchain_utils.bzl @@ -29,12 +29,14 @@ def find_cpp_toolchain(ctx, *, mandatory = True): 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 @@ def find_cpp_toolchain(ctx, *, mandatory = True): 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))