Skip to content

Commit

Permalink
Support optional toolchains with find_cpp_toolchain
Browse files Browse the repository at this point in the history
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
  • Loading branch information
fmeum authored and hvadehra committed Feb 14, 2023
1 parent 4a1e85e commit 2f84a2f
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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();
Expand Down
13 changes: 10 additions & 3 deletions src/main/starlark/builtins_bzl/common/cc/cc_helper.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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"),
},
Expand Down
67 changes: 67 additions & 0 deletions src/test/shell/bazel/cc_integration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
7 changes: 2 additions & 5 deletions tools/cpp/BUILD.tools
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
13 changes: 9 additions & 4 deletions tools/cpp/toolchain_utils.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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))
Expand Down

0 comments on commit 2f84a2f

Please sign in to comment.