Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions cc/toolchains/cc_toolchain_info.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ ToolConfigInfo = provider(
},
)

LegacyToolInfo = provider(
doc = "A tool specified by path rather than by label, for legacy toolchain configurations.",
# @unsorted-dict-items
fields = {
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
"name": "(str) The tool name (eg. gcc, ar, ld, strip)",
"path": "(str) The filesystem path to the tool",
},
)

ToolchainConfigInfo = provider(
doc = "The configuration for a toolchain",
# @unsorted-dict-items
Expand All @@ -235,5 +245,6 @@ ToolchainConfigInfo = provider(
"files": "(dict[ActionTypeInfo, depset[File]]) Files required for the toolchain, keyed by the action type.",
"allowlist_include_directories": "(depset[DirectoryInfo]) Built-in include directories implied by this toolchain's args and tools that should be allowlisted in Bazel's include checker",
"allowlist_absolute_include_directories": "(List[str]) Built-in include directories allowed the sandbox. Use with care",
"legacy_tools": "(Sequence[LegacyToolInfo]) Legacy tools specified by path rather than by label",
},
)
10 changes: 10 additions & 0 deletions cc/toolchains/impl/legacy_converter.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ load(
legacy_flag_set = "flag_set",
legacy_make_variable = "make_variable",
legacy_tool = "tool",
legacy_tool_path = "tool_path", # buildifier: disable=deprecated-function
legacy_with_feature_set = "with_feature_set",
)

Expand Down Expand Up @@ -226,10 +227,19 @@ def convert_toolchain(toolchain):
for m in toolchain.make_variables
]

tool_paths = [
legacy_tool_path(
name = lt.name,
path = lt.path,
)
for lt in toolchain.legacy_tools
]

return struct(
features = [ft for ft in features if ft != None],
action_configs = sorted(action_configs, key = lambda ac: ac.action_name),
cxx_builtin_include_directories = cxx_builtin_include_directories,
artifact_name_patterns = artifact_name_patterns,
make_variables = make_variables,
tool_paths = tool_paths,
)
4 changes: 4 additions & 0 deletions cc/toolchains/impl/toolchain_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ load(
"ArgsListInfo",
"ArtifactNamePatternInfo",
"FeatureSetInfo",
"LegacyToolInfo",
"MakeVariableInfo",
"ToolConfigInfo",
"ToolchainConfigInfo",
Expand Down Expand Up @@ -62,6 +63,7 @@ def _cc_toolchain_config_impl(ctx):
args = ctx.attr.args,
artifact_name_patterns = ctx.attr.artifact_name_patterns,
make_variables = ctx.attr.make_variables,
legacy_tools = ctx.attr.legacy_tools,
)

legacy = convert_toolchain(toolchain_config)
Expand All @@ -75,6 +77,7 @@ def _cc_toolchain_config_impl(ctx):
make_variables = legacy.make_variables,
features = legacy.features,
cxx_builtin_include_directories = legacy.cxx_builtin_include_directories,
tool_paths = legacy.tool_paths,
# toolchain_identifier is deprecated, but setting it to None results
# in an error that it expected a string, and for safety's sake, I'd
# prefer to provide something unique.
Expand Down Expand Up @@ -111,6 +114,7 @@ cc_toolchain_config = rule(
"enabled_features": attr.label_list(providers = [FeatureSetInfo]),
"artifact_name_patterns": attr.label_list(providers = [ArtifactNamePatternInfo]),
"make_variables": attr.label_list(providers = [MakeVariableInfo]),
"legacy_tools": attr.label_list(providers = [LegacyToolInfo]),
"_builtin_features": attr.label(default = "//cc/toolchains/features:all_builtin_features"),
},
provides = [ToolchainConfigInfo],
Expand Down
8 changes: 6 additions & 2 deletions cc/toolchains/impl/toolchain_config_info.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.
"""Helper functions to create and validate a ToolchainConfigInfo."""

load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo")
load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "LegacyToolInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo")
load(":args_utils.bzl", "get_action_type")
load(":collect.bzl", "collect_args_lists", "collect_features")

Expand Down Expand Up @@ -162,7 +162,7 @@ def _collect_make_variables(targets, fail):

return make_variables.values()

def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], make_variables = [], tool_map = None, fail = fail):
def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], make_variables = [], legacy_tools = [], tool_map = None, fail = fail):
"""Generates and validates a ToolchainConfigInfo from lists of labels.

Args:
Expand All @@ -173,6 +173,7 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
args: (List[Target]) A list of targets providing ArgsListInfo
artifact_name_patterns: (List[Target]) A list of targets providing ArtifactNamePatternInfo.
make_variables: (List[Target]) A list of targets providing MakeVariableInfo.
legacy_tools: (List[Target]) A list of targets providing LegacyToolInfo.
tool_map: (Target) A target providing ToolMapInfo.
fail: A fail function. Use only during tests.
Returns:
Expand All @@ -193,6 +194,8 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
# side-effect of allowing code to continue.
return None # buildifier: disable=unreachable

legacy_tools_infos = tuple([t[LegacyToolInfo] for t in legacy_tools])

args = collect_args_lists(args, label = label)
tools = tool_map[ToolConfigInfo].configs
files = {
Expand Down Expand Up @@ -222,6 +225,7 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
allowlist_absolute_include_directories = allowlist_absolute_include_directories,
artifact_name_patterns = _collect_artifact_name_patterns(artifact_name_patterns, fail),
make_variables = _collect_make_variables(make_variables, fail),
legacy_tools = legacy_tools_infos,
)
_validate_toolchain(toolchain_config, fail = fail)
return toolchain_config
63 changes: 63 additions & 0 deletions cc/toolchains/legacy_tool.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2026 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Implementation of cc_legacy_tool"""

load(":cc_toolchain_info.bzl", "LegacyToolInfo")

def _cc_legacy_tool_impl(ctx):
return [LegacyToolInfo(
label = ctx.label,
name = ctx.attr.tool_name,
path = ctx.attr.path,
)]

cc_legacy_tool = rule(
implementation = _cc_legacy_tool_impl,
attrs = {
"tool_name": attr.string(
mandatory = True,
doc = """The name of the tool (eg. "gcc", "ar", "ld", "strip").

This corresponds to the tool name used by Bazel's legacy toolchain resolution.
""",
),
"path": attr.string(
mandatory = True,
doc = """The filesystem path to the tool.

Can be an absolute path (for non-hermetic toolchains) or a relative path
starting from the package that provides the toolchain.
""",
),
},
provides = [LegacyToolInfo],
doc = """Declares a tool by filesystem path for use in legacy toolchain configurations.

`cc_legacy_tool` allows specifying tools by their filesystem path rather than as
Bazel targets. This is useful where bazel doesn't handle relative paths well.

These tools are passed via the `legacy_tools` attribute of `cc_toolchain`.

Example:
```
load("@rules_cc//cc/toolchains:legacy_tool.bzl", "cc_legacy_tool")

cc_legacy_tool(
name = "gcov",
tool_name = "gcov",
path = "/usr/bin/gcov",
)
```
""",
)
5 changes: 5 additions & 0 deletions cc/toolchains/toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def cc_toolchain(
args = [],
artifact_name_patterns = [],
make_variables = [],
legacy_tools = [],
known_features = [],
enabled_features = [],
libc_top = None,
Expand Down Expand Up @@ -118,6 +119,9 @@ def cc_toolchain(
artifact_name_patterns: (List[Label]) A list of `cc_artifact_name_pattern` defining patterns
for names of artifacts created by this toolchain.
make_variables: (List[Label]) A list of `cc_make_variable` defining variable substitutions.
legacy_tools: (List[Label]) A list of `cc_legacy_tool` rules that specify tools by filesystem
path. These are used to populate the legacy `tool_paths` parameter of the toolchain
configuration, which is required by some Bazel features (e.g. coverage).
known_features: (List[Label]) A list of `cc_feature` rules that this toolchain supports.
Whether or not these
[features](https://bazel.build/docs/cc-toolchain-config-reference#features)
Expand Down Expand Up @@ -174,6 +178,7 @@ def cc_toolchain(
args = args,
artifact_name_patterns = artifact_name_patterns,
make_variables = make_variables,
legacy_tools = legacy_tools,
known_features = known_features,
enabled_features = enabled_features,
compiler = compiler,
Expand Down
7 changes: 4 additions & 3 deletions docs/toolchain_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,9 +785,9 @@ cc_tool_map(
<pre>
load("@rules_cc//cc/toolchains/impl:documented_api.bzl", "cc_toolchain")

cc_toolchain(*, <a href="#cc_toolchain-name">name</a>, <a href="#cc_toolchain-tool_map">tool_map</a>, <a href="#cc_toolchain-args">args</a>, <a href="#cc_toolchain-artifact_name_patterns">artifact_name_patterns</a>, <a href="#cc_toolchain-make_variables">make_variables</a>, <a href="#cc_toolchain-known_features">known_features</a>,
<a href="#cc_toolchain-enabled_features">enabled_features</a>, <a href="#cc_toolchain-libc_top">libc_top</a>, <a href="#cc_toolchain-module_map">module_map</a>, <a href="#cc_toolchain-dynamic_runtime_lib">dynamic_runtime_lib</a>, <a href="#cc_toolchain-static_runtime_lib">static_runtime_lib</a>,
<a href="#cc_toolchain-supports_header_parsing">supports_header_parsing</a>, <a href="#cc_toolchain-supports_param_files">supports_param_files</a>, <a href="#cc_toolchain-compiler">compiler</a>, <a href="#cc_toolchain-kwargs">**kwargs</a>)
cc_toolchain(*, <a href="#cc_toolchain-name">name</a>, <a href="#cc_toolchain-tool_map">tool_map</a>, <a href="#cc_toolchain-args">args</a>, <a href="#cc_toolchain-artifact_name_patterns">artifact_name_patterns</a>, <a href="#cc_toolchain-make_variables">make_variables</a>, <a href="#cc_toolchain-legacy_tools">legacy_tools</a>,
<a href="#cc_toolchain-known_features">known_features</a>, <a href="#cc_toolchain-enabled_features">enabled_features</a>, <a href="#cc_toolchain-libc_top">libc_top</a>, <a href="#cc_toolchain-module_map">module_map</a>, <a href="#cc_toolchain-dynamic_runtime_lib">dynamic_runtime_lib</a>,
<a href="#cc_toolchain-static_runtime_lib">static_runtime_lib</a>, <a href="#cc_toolchain-supports_header_parsing">supports_header_parsing</a>, <a href="#cc_toolchain-supports_param_files">supports_param_files</a>, <a href="#cc_toolchain-compiler">compiler</a>, <a href="#cc_toolchain-kwargs">**kwargs</a>)
</pre>

A C/C++ toolchain configuration.
Expand Down Expand Up @@ -840,6 +840,7 @@ Generated rules:
| <a id="cc_toolchain-args"></a>args | (List[Label]) A list of [`cc_args`](#cc_args) and `cc_arg_list` to apply across this toolchain. | `[]` |
| <a id="cc_toolchain-artifact_name_patterns"></a>artifact_name_patterns | (List[Label]) A list of `cc_artifact_name_pattern` defining patterns for names of artifacts created by this toolchain. | `[]` |
| <a id="cc_toolchain-make_variables"></a>make_variables | (List[Label]) A list of `cc_make_variable` defining variable substitutions. | `[]` |
| <a id="cc_toolchain-legacy_tools"></a>legacy_tools | (List[Label]) A list of `cc_legacy_tool` rules that specify tools by filesystem path. These are used to populate the legacy `tool_paths` parameter of the toolchain configuration, which is required by some Bazel features (e.g. coverage). | `[]` |
| <a id="cc_toolchain-known_features"></a>known_features | (List[Label]) A list of [`cc_feature`](#cc_feature) rules that this toolchain supports. Whether or not these [features](https://bazel.build/docs/cc-toolchain-config-reference#features) are enabled may change over the course of a build. See the documentation for [`cc_feature`](#cc_feature) for more information. | `[]` |
| <a id="cc_toolchain-enabled_features"></a>enabled_features | (List[Label]) A list of [`cc_feature`](#cc_feature) rules whose initial state should be `enabled`. Note that it is still possible for these [features](https://bazel.build/docs/cc-toolchain-config-reference#features) to be disabled over the course of a build through other mechanisms. See the documentation for [`cc_feature`](#cc_feature) for more information. | `[]` |
| <a id="cc_toolchain-libc_top"></a>libc_top | (Label) A collection of artifacts for libc passed as inputs to compile/linking actions. See [`cc_toolchain.libc_top`](https://bazel.build/reference/be/c-cpp#cc_toolchain.libc_top) for more information. | `None` |
Expand Down
16 changes: 16 additions & 0 deletions tests/rule_based_toolchain/legacy_tool/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("//cc/toolchains:legacy_tool.bzl", "cc_legacy_tool")
load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite")
load(":legacy_tool_test.bzl", "TARGETS", "TESTS")

cc_legacy_tool(
name = "gcov_tool",
path = "/usr/bin/gcov",
tool_name = "gcov",
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
)

analysis_test_suite(
name = "test_suite",
targets = TARGETS,
tests = TESTS,
)
32 changes: 32 additions & 0 deletions tests/rule_based_toolchain/legacy_tool/legacy_tool_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2026 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for the cc_legacy_tool rule."""

load("//cc/toolchains:cc_toolchain_info.bzl", "LegacyToolInfo")

visibility("private")

def _legacy_tool_provides_info_test(env, targets):
info = targets.gcov_tool[LegacyToolInfo]
env.expect.that_str(info.name).equals("gcov")
env.expect.that_str(info.path).equals("/usr/bin/gcov")

TARGETS = [
":gcov_tool",
]

# @unsorted-dict-items
TESTS = {
"legacy_tool_provides_info_test": _legacy_tool_provides_info_test,
}
1 change: 1 addition & 0 deletions tests/rule_based_toolchain/subjects.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ _ToolchainConfigFactory = generate_factory(
allowlist_absolute_include_directories = ProviderDepset(_subjects.str),
artifact_name_patterns = [],
make_variables = [],
legacy_tools = _subjects.collection,
),
)

Expand Down
16 changes: 16 additions & 0 deletions tests/rule_based_toolchain/toolchain_config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ load("@rules_testing//lib:util.bzl", "util")
load("//cc/toolchains:args.bzl", "cc_args")
load("//cc/toolchains:feature.bzl", "cc_feature")
load("//cc/toolchains:feature_set.bzl", "cc_feature_set")
load("//cc/toolchains:legacy_tool.bzl", "cc_legacy_tool")
load("//cc/toolchains:tool.bzl", "cc_tool")
load("//cc/toolchains:tool_map.bzl", "cc_tool_map")
load("//cc/toolchains/args:sysroot.bzl", "cc_sysroot")
Expand Down Expand Up @@ -199,6 +200,21 @@ util.helper_target(
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
)

cc_legacy_tool(
name = "legacy_gcov",
path = "/usr/bin/gcov",
tool_name = "gcov",
)

util.helper_target(
cc_toolchain_config,
name = "toolchain_config_with_legacy_tools",
legacy_tools = [
":legacy_gcov",
],
tool_map = ":empty_tool_map",
)

analysis_test_suite(
name = "test_suite",
targets = TARGETS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ load(
legacy_flag_group = "flag_group",
legacy_flag_set = "flag_set",
legacy_tool = "tool",
legacy_tool_path = "tool_path", # buildifier: disable=deprecated-function
)
load("//cc/toolchains:cc_toolchain_info.bzl", "ActionTypeInfo", "ToolchainConfigInfo")
load("//cc/toolchains/impl:legacy_converter.bzl", "convert_toolchain")
Expand Down Expand Up @@ -258,6 +259,16 @@ def _toolchain_collects_files_test(env, targets):
),
]).in_order()

def _legacy_tools_produce_tool_paths_test(env, targets):
tc = env.expect.that_target(
targets.toolchain_config_with_legacy_tools,
).provider(ToolchainConfigInfo)

legacy = convert_toolchain(tc.actual)
env.expect.that_collection(legacy.tool_paths).contains_exactly([
legacy_tool_path(name = "gcov", path = "/usr/bin/gcov"),
])

TARGETS = [
"//tests/rule_based_toolchain/actions:c_compile",
"//tests/rule_based_toolchain/actions:cpp_compile",
Expand All @@ -271,13 +282,15 @@ TARGETS = [
":c_compile_tool_map",
":empty_tool_map",
":implies_simple_feature",
":legacy_gcov",
":overrides_feature",
":requires_any_simple_feature",
":requires_all_simple_feature",
":requires_all_simple_args",
":simple_feature",
":simple_feature2",
":same_feature_name",
":toolchain_config_with_legacy_tools",
]

# @unsorted-dict-items
Expand All @@ -289,4 +302,5 @@ TESTS = {
"feature_missing_requirements_invalid_test": _feature_missing_requirements_invalid_test,
"args_missing_requirements_invalid_test": _args_missing_requirements_invalid_test,
"toolchain_collects_files_test": _toolchain_collects_files_test,
"legacy_tools_produce_tool_paths_test": _legacy_tools_produce_tool_paths_test,
}