Skip to content

Commit bbd61dc

Browse files
committed
Add cc_legacy_tool stopgap
Some tools in rules_cc like gcov and llvm-cov, expect absolute paths in the toolchain. This clearly isn't ideal but updating all callers as a blocker to being able to use rules based toolchains also isn't ideal. This allows backfilling those absolute tool paths with the new cc_legacy_tool rule.
1 parent 8a95356 commit bbd61dc

12 files changed

Lines changed: 182 additions & 5 deletions

File tree

cc/toolchains/cc_toolchain_info.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,16 @@ ToolConfigInfo = provider(
221221
},
222222
)
223223

224+
LegacyToolInfo = provider(
225+
doc = "A tool specified by path rather than by label, for legacy toolchain configurations.",
226+
# @unsorted-dict-items
227+
fields = {
228+
"label": "(Label) The label defining this provider. Place in error messages to simplify debugging",
229+
"name": "(str) The tool name (eg. gcc, ar, ld, strip)",
230+
"path": "(str) The filesystem path to the tool",
231+
},
232+
)
233+
224234
ToolchainConfigInfo = provider(
225235
doc = "The configuration for a toolchain",
226236
# @unsorted-dict-items
@@ -235,5 +245,6 @@ ToolchainConfigInfo = provider(
235245
"files": "(dict[ActionTypeInfo, depset[File]]) Files required for the toolchain, keyed by the action type.",
236246
"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",
237247
"allowlist_absolute_include_directories": "(List[str]) Built-in include directories allowed the sandbox. Use with care",
248+
"legacy_tools": "(Sequence[LegacyToolInfo]) Legacy tools specified by path rather than by label",
238249
},
239250
)

cc/toolchains/impl/legacy_converter.bzl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ load(
2424
legacy_flag_set = "flag_set",
2525
legacy_make_variable = "make_variable",
2626
legacy_tool = "tool",
27+
legacy_tool_path = "tool_path",
2728
legacy_with_feature_set = "with_feature_set",
2829
)
2930

@@ -226,10 +227,19 @@ def convert_toolchain(toolchain):
226227
for m in toolchain.make_variables
227228
]
228229

230+
tool_paths = [
231+
legacy_tool_path(
232+
name = lt.name,
233+
path = lt.path,
234+
)
235+
for lt in toolchain.legacy_tools
236+
]
237+
229238
return struct(
230239
features = [ft for ft in features if ft != None],
231240
action_configs = sorted(action_configs, key = lambda ac: ac.action_name),
232241
cxx_builtin_include_directories = cxx_builtin_include_directories,
233242
artifact_name_patterns = artifact_name_patterns,
234243
make_variables = make_variables,
244+
tool_paths = tool_paths,
235245
)

cc/toolchains/impl/toolchain_config.bzl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ load(
2020
"ArgsListInfo",
2121
"ArtifactNamePatternInfo",
2222
"FeatureSetInfo",
23+
"LegacyToolInfo",
2324
"MakeVariableInfo",
2425
"ToolConfigInfo",
2526
"ToolchainConfigInfo",
@@ -62,6 +63,7 @@ def _cc_toolchain_config_impl(ctx):
6263
args = ctx.attr.args,
6364
artifact_name_patterns = ctx.attr.artifact_name_patterns,
6465
make_variables = ctx.attr.make_variables,
66+
legacy_tools = ctx.attr.legacy_tools,
6567
)
6668

6769
legacy = convert_toolchain(toolchain_config)
@@ -75,6 +77,7 @@ def _cc_toolchain_config_impl(ctx):
7577
make_variables = legacy.make_variables,
7678
features = legacy.features,
7779
cxx_builtin_include_directories = legacy.cxx_builtin_include_directories,
80+
tool_paths = legacy.tool_paths,
7881
# toolchain_identifier is deprecated, but setting it to None results
7982
# in an error that it expected a string, and for safety's sake, I'd
8083
# prefer to provide something unique.
@@ -111,6 +114,7 @@ cc_toolchain_config = rule(
111114
"enabled_features": attr.label_list(providers = [FeatureSetInfo]),
112115
"artifact_name_patterns": attr.label_list(providers = [ArtifactNamePatternInfo]),
113116
"make_variables": attr.label_list(providers = [MakeVariableInfo]),
117+
"legacy_tools": attr.label_list(providers = [LegacyToolInfo]),
114118
"_builtin_features": attr.label(default = "//cc/toolchains/features:all_builtin_features"),
115119
},
116120
provides = [ToolchainConfigInfo],

cc/toolchains/impl/toolchain_config_info.bzl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
"""Helper functions to create and validate a ToolchainConfigInfo."""
1515

16-
load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo")
16+
load("//cc/toolchains:cc_toolchain_info.bzl", "ArtifactNamePatternInfo", "LegacyToolInfo", "MakeVariableInfo", "ToolConfigInfo", "ToolchainConfigInfo")
1717
load(":args_utils.bzl", "get_action_type")
1818
load(":collect.bzl", "collect_args_lists", "collect_features")
1919

@@ -162,7 +162,7 @@ def _collect_make_variables(targets, fail):
162162

163163
return make_variables.values()
164164

165-
def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], make_variables = [], tool_map = None, fail = fail):
165+
def toolchain_config_info(label, known_features = [], enabled_features = [], args = [], artifact_name_patterns = [], make_variables = [], legacy_tools = [], tool_map = None, fail = fail):
166166
"""Generates and validates a ToolchainConfigInfo from lists of labels.
167167
168168
Args:
@@ -173,6 +173,7 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
173173
args: (List[Target]) A list of targets providing ArgsListInfo
174174
artifact_name_patterns: (List[Target]) A list of targets providing ArtifactNamePatternInfo.
175175
make_variables: (List[Target]) A list of targets providing MakeVariableInfo.
176+
legacy_tools: (List[Target]) A list of targets providing LegacyToolInfo.
176177
tool_map: (Target) A target providing ToolMapInfo.
177178
fail: A fail function. Use only during tests.
178179
Returns:
@@ -193,6 +194,8 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
193194
# side-effect of allowing code to continue.
194195
return None # buildifier: disable=unreachable
195196

197+
legacy_tools_infos = tuple([t[LegacyToolInfo] for t in legacy_tools])
198+
196199
args = collect_args_lists(args, label = label)
197200
tools = tool_map[ToolConfigInfo].configs
198201
files = {
@@ -222,6 +225,7 @@ def toolchain_config_info(label, known_features = [], enabled_features = [], arg
222225
allowlist_absolute_include_directories = allowlist_absolute_include_directories,
223226
artifact_name_patterns = _collect_artifact_name_patterns(artifact_name_patterns, fail),
224227
make_variables = _collect_make_variables(make_variables, fail),
228+
legacy_tools = legacy_tools_infos,
225229
)
226230
_validate_toolchain(toolchain_config, fail = fail)
227231
return toolchain_config

cc/toolchains/legacy_tool.bzl

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright 2026 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Implementation of cc_legacy_tool"""
15+
16+
load(":cc_toolchain_info.bzl", "LegacyToolInfo")
17+
18+
def _cc_legacy_tool_impl(ctx):
19+
return [LegacyToolInfo(
20+
label = ctx.label,
21+
name = ctx.attr.tool_name,
22+
path = ctx.attr.path,
23+
)]
24+
25+
cc_legacy_tool = rule(
26+
implementation = _cc_legacy_tool_impl,
27+
attrs = {
28+
"tool_name": attr.string(
29+
mandatory = True,
30+
doc = """The name of the tool (eg. "gcc", "ar", "ld", "strip").
31+
32+
This corresponds to the tool name used by Bazel's legacy toolchain resolution.
33+
""",
34+
),
35+
"path": attr.string(
36+
mandatory = True,
37+
doc = """The filesystem path to the tool.
38+
39+
Can be an absolute path (for non-hermetic toolchains) or a relative path
40+
starting from the package that provides the toolchain.
41+
""",
42+
),
43+
},
44+
provides = [LegacyToolInfo],
45+
doc = """Declares a tool by filesystem path for use in legacy toolchain configurations.
46+
47+
`cc_legacy_tool` allows specifying tools by their filesystem path rather than as
48+
Bazel targets. This is useful where bazel doesn't handle relative paths well.
49+
50+
These tools are passed via the `legacy_tools` attribute of `cc_toolchain`.
51+
52+
Example:
53+
```
54+
load("@rules_cc//cc/toolchains:legacy_tool.bzl", "cc_legacy_tool")
55+
56+
cc_legacy_tool(
57+
name = "gcov",
58+
tool_name = "gcov",
59+
path = "/usr/bin/gcov",
60+
)
61+
```
62+
""",
63+
)

cc/toolchains/toolchain.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def cc_toolchain(
6161
args = [],
6262
artifact_name_patterns = [],
6363
make_variables = [],
64+
legacy_tools = [],
6465
known_features = [],
6566
enabled_features = [],
6667
libc_top = None,
@@ -118,6 +119,9 @@ def cc_toolchain(
118119
artifact_name_patterns: (List[Label]) A list of `cc_artifact_name_pattern` defining patterns
119120
for names of artifacts created by this toolchain.
120121
make_variables: (List[Label]) A list of `cc_make_variable` defining variable substitutions.
122+
legacy_tools: (List[Label]) A list of `cc_legacy_tool` rules that specify tools by filesystem
123+
path. These are used to populate the legacy `tool_paths` parameter of the toolchain
124+
configuration, which is required by some Bazel features (e.g. coverage).
121125
known_features: (List[Label]) A list of `cc_feature` rules that this toolchain supports.
122126
Whether or not these
123127
[features](https://bazel.build/docs/cc-toolchain-config-reference#features)
@@ -174,6 +178,7 @@ def cc_toolchain(
174178
args = args,
175179
artifact_name_patterns = artifact_name_patterns,
176180
make_variables = make_variables,
181+
legacy_tools = legacy_tools,
177182
known_features = known_features,
178183
enabled_features = enabled_features,
179184
compiler = compiler,

docs/toolchain_api.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -785,9 +785,9 @@ cc_tool_map(
785785
<pre>
786786
load("@rules_cc//cc/toolchains/impl:documented_api.bzl", "cc_toolchain")
787787

788-
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>,
789-
<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>,
790-
<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>)
788+
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>,
789+
<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>,
790+
<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>)
791791
</pre>
792792

793793
A C/C++ toolchain configuration.
@@ -840,6 +840,7 @@ Generated rules:
840840
| <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. | `[]` |
841841
| <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. | `[]` |
842842
| <a id="cc_toolchain-make_variables"></a>make_variables | (List[Label]) A list of `cc_make_variable` defining variable substitutions. | `[]` |
843+
| <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). | `[]` |
843844
| <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. | `[]` |
844845
| <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. | `[]` |
845846
| <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` |
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
load("//cc/toolchains:legacy_tool.bzl", "cc_legacy_tool")
2+
load("//tests/rule_based_toolchain:analysis_test_suite.bzl", "analysis_test_suite")
3+
load(":legacy_tool_test.bzl", "TARGETS", "TESTS")
4+
5+
cc_legacy_tool(
6+
name = "gcov_tool",
7+
path = "/usr/bin/gcov",
8+
tool_name = "gcov",
9+
visibility = ["//tests/rule_based_toolchain:__subpackages__"],
10+
)
11+
12+
analysis_test_suite(
13+
name = "test_suite",
14+
targets = TARGETS,
15+
tests = TESTS,
16+
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Copyright 2026 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Tests for the cc_legacy_tool rule."""
15+
16+
load("//cc/toolchains:cc_toolchain_info.bzl", "LegacyToolInfo")
17+
18+
visibility("private")
19+
20+
def _legacy_tool_provides_info_test(env, targets):
21+
info = targets.gcov_tool[LegacyToolInfo]
22+
env.expect.that_str(info.name).equals("gcov")
23+
env.expect.that_str(info.path).equals("/usr/bin/gcov")
24+
25+
TARGETS = [
26+
":gcov_tool",
27+
]
28+
29+
# @unsorted-dict-items
30+
TESTS = {
31+
"legacy_tool_provides_info_test": _legacy_tool_provides_info_test,
32+
}

tests/rule_based_toolchain/subjects.bzl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ _ToolchainConfigFactory = generate_factory(
238238
allowlist_absolute_include_directories = ProviderDepset(_subjects.str),
239239
artifact_name_patterns = [],
240240
make_variables = [],
241+
legacy_tools = _subjects.collection,
241242
),
242243
)
243244

0 commit comments

Comments
 (0)