Skip to content

Commit c09b6fa

Browse files
authored
[bazel] Add support for LLVM_TOOL_LLVM_DRIVER_BUILD (#86879)
This adds the bazel equivalent of the `llvm` binary produced by `LLVM_TOOL_LLVM_DRIVER_BUILD` in cmake. For the initial commit, this only includes `llvm-ar`, `llvm-nm`, and `llvm-size`. The rest are trivial to add in a followup commit, following the same pattern as here. By default it will include everything that supports the llvm-driver model, but it can be reduced to only build a subset, e.g. this will build only nm and size: ``` $ bazel build \ --@llvm-project//llvm:driver-tools=llvm-nm,llvm-size \ @llvm-project//llvm:llvm ```
1 parent 85a5f64 commit c09b6fa

File tree

2 files changed

+236
-45
lines changed

2 files changed

+236
-45
lines changed

utils/bazel/llvm-project-overlay/llvm/BUILD.bazel

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
77
load("//mlir:tblgen.bzl", "td_library")
88
load(":binary_alias.bzl", "binary_alias")
99
load(":config.bzl", "llvm_config_defines")
10+
load(":driver.bzl", "generate_driver_selects", "generate_driver_tools_def", "llvm_driver_cc_binary", "select_driver_tools")
1011
load(":enum_targets_gen.bzl", "enum_targets_gen")
1112
load(":targets.bzl", "llvm_targets")
1213
load(":tblgen.bzl", "gentbl")
@@ -605,6 +606,32 @@ cc_library(
605606
],
606607
)
607608

609+
# Command line flag to control which tools get included in the llvm driver binary.
610+
# The macro also generates config_setting targets used by select_driver_tools().
611+
generate_driver_selects(name = "driver-tools")
612+
613+
generate_driver_tools_def(
614+
name = "gen_llvm_driver_tools_def",
615+
out = "LLVMDriverTools.def",
616+
driver_tools = select_driver_tools(":driver-tools"),
617+
)
618+
619+
# Workaround inability to put `.def` files into `srcs` with a library
620+
cc_library(
621+
name = "llvm_driver_tools_def_lib",
622+
includes = ["."],
623+
textual_hdrs = ["LLVMDriverTools.def"],
624+
)
625+
626+
cc_binary(
627+
name = "llvm",
628+
srcs = glob(["tools/llvm-driver/*.cpp"]),
629+
deps = [
630+
":Support",
631+
":llvm_driver_tools_def_lib",
632+
] + select_driver_tools(":driver-tools"),
633+
)
634+
608635
cc_binary(
609636
name = "llvm-min-tblgen",
610637
srcs = [
@@ -3305,22 +3332,10 @@ cc_binary(
33053332
],
33063333
)
33073334

3308-
expand_template(
3309-
name = "ar_main",
3310-
out = "llvm-ar-driver.cpp",
3311-
substitutions = {
3312-
"@TOOL_NAME@": "llvm_ar",
3313-
},
3314-
template = "cmake/modules/llvm-driver-template.cpp.in",
3315-
)
3316-
3317-
cc_binary(
3318-
name = "llvm-ar",
3319-
srcs = glob([
3320-
"tools/llvm-ar/*.cpp",
3321-
]) + ["llvm-ar-driver.cpp"],
3335+
cc_library(
3336+
name = "llvm-ar-lib",
3337+
srcs = glob(["tools/llvm-ar/*.cpp"]),
33223338
copts = llvm_copts,
3323-
stamp = 0,
33243339
deps = [
33253340
":AllTargetsAsmParsers",
33263341
":AllTargetsCodeGens",
@@ -3334,6 +3349,12 @@ cc_binary(
33343349
],
33353350
)
33363351

3352+
llvm_driver_cc_binary(
3353+
name = "llvm-ar",
3354+
stamp = 0,
3355+
deps = [":llvm-ar-lib"],
3356+
)
3357+
33373358
# We need to run llvm-ar with different basenames to make it run with
33383359
# different behavior.
33393360
binary_alias(
@@ -4151,22 +4172,10 @@ gentbl(
41514172
td_srcs = ["include/llvm/Option/OptParser.td"],
41524173
)
41534174

4154-
expand_template(
4155-
name = "nm_main",
4156-
out = "llvm-nm-driver.cpp",
4157-
substitutions = {
4158-
"@TOOL_NAME@": "llvm_nm",
4159-
},
4160-
template = "cmake/modules/llvm-driver-template.cpp.in",
4161-
)
4162-
4163-
cc_binary(
4164-
name = "llvm-nm",
4165-
srcs = glob([
4166-
"tools/llvm-nm/*.cpp",
4167-
]) + ["llvm-nm-driver.cpp"],
4175+
cc_library(
4176+
name = "llvm-nm-lib",
4177+
srcs = glob(["tools/llvm-nm/*.cpp"]),
41684178
copts = llvm_copts,
4169-
stamp = 0,
41704179
deps = [
41714180
":AllTargetsAsmParsers",
41724181
":AllTargetsCodeGens",
@@ -4183,6 +4192,12 @@ cc_binary(
41834192
],
41844193
)
41854194

4195+
llvm_driver_cc_binary(
4196+
name = "llvm-nm",
4197+
stamp = 0,
4198+
deps = [":llvm-nm-lib"],
4199+
)
4200+
41864201
gentbl(
41874202
name = "llvm-objcopy-opts",
41884203
strip_include_prefix = "tools/llvm-objcopy",
@@ -4634,22 +4649,10 @@ gentbl(
46344649
td_srcs = ["include/llvm/Option/OptParser.td"],
46354650
)
46364651

4637-
expand_template(
4638-
name = "size_main",
4639-
out = "llvm-size-driver.cpp",
4640-
substitutions = {
4641-
"@TOOL_NAME@": "llvm_size",
4642-
},
4643-
template = "cmake/modules/llvm-driver-template.cpp.in",
4644-
)
4645-
4646-
cc_binary(
4647-
name = "llvm-size",
4648-
srcs = glob([
4649-
"tools/llvm-size/*.cpp",
4650-
]) + ["llvm-size-driver.cpp"],
4652+
cc_library(
4653+
name = "llvm-size-lib",
4654+
srcs = glob(["tools/llvm-size/*.cpp"]),
46514655
copts = llvm_copts,
4652-
stamp = 0,
46534656
deps = [
46544657
":Object",
46554658
":Option",
@@ -4658,6 +4661,12 @@ cc_binary(
46584661
],
46594662
)
46604663

4664+
llvm_driver_cc_binary(
4665+
name = "llvm-size",
4666+
stamp = 0,
4667+
deps = [":llvm-size-lib"],
4668+
)
4669+
46614670
cc_binary(
46624671
name = "llvm-split",
46634672
srcs = glob([
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
2+
# See https://llvm.org/LICENSE.txt for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4+
5+
"""Configuration for the llvm-driver tool."""
6+
7+
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
8+
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
9+
10+
# Mapping from every tool to the cc_library that implements the tool's entrypoint.
11+
# TODO: uncomment the remaining targets after splitting them
12+
# into separate library/binary targets.
13+
_TOOLS = {
14+
# "clang-scan-deps": "//clang:clang-scan-deps-lib",
15+
# "clang": "//clang:clang-driver",
16+
# "dsymutil": "//llvm:dsymutil-lib",
17+
# "lld": "//lld:lld-lib",
18+
"llvm-ar": "//llvm:llvm-ar-lib",
19+
# "llvm-cxxfilt": "//llvm:llvm-cxxfilt-lib",
20+
# "llvm-dwp": "//llvm:llvm-dwp-lib",
21+
# "llvm-gsymutil": "//llvm:llvm-gsymutil-lib",
22+
# "llvm-ifs": "//llvm:llvm-ifs-lib",
23+
# "llvm-libtool-darwin": "//llvm:llvm-libtool-darwin-lib",
24+
# "llvm-lipo": "//llvm:llvm-lipo-lib",
25+
# "llvm-ml": "//llvm:llvm-ml-lib",
26+
# "llvm-mt": "//llvm:llvm-mt-lib",
27+
"llvm-nm": "//llvm:llvm-nm-lib",
28+
# "llvm-objcopy": "//llvm:llvm-objcopy-lib",
29+
# "llvm-objdump": "//llvm:llvm-objdump-lib",
30+
# "llvm-profdata": "//llvm:llvm-profdata-lib",
31+
# "llvm-rc": "//llvm:llvm-rc-lib",
32+
# "llvm-readobj": "//llvm:llvm-readobj-lib",
33+
"llvm-size": "//llvm:llvm-size-lib",
34+
# "llvm-symbolizer": "//llvm:llvm-symbolizer-lib",
35+
# "sancov": "//llvm:sancov-lib",
36+
}
37+
38+
# Tools automatically get their own name as an alias, but there may be additional
39+
# aliases for a given tool.
40+
_EXTRA_ALIASES = {
41+
"clang": ["clang++", "clang-cl", "clang-cpp"],
42+
"lld": ["lld-link", "ld.lld", "ld64.lld", "wasm-ld"],
43+
"llvm-ar": ["ranlib", "lib", "dlltool"],
44+
"llvm-objcopy": ["bitcode-strip", "install-name-tool", "strip"],
45+
"llvm-objdump": ["otool"],
46+
"llvm-rc": ["windres"],
47+
"llvm-readobj": ["readelf"],
48+
"llvm-symbolizer": ["addr2line"],
49+
}
50+
51+
def _validated_string_list_flag_impl(ctx):
52+
invalid_values = [v for v in ctx.build_setting_value if v not in ctx.attr.values]
53+
if invalid_values:
54+
fail("Tool(s) [{}] are not in the known list of tools: {}".format(
55+
", ".join(invalid_values),
56+
", ".join(ctx.attr.values),
57+
))
58+
return BuildSettingInfo(value = ctx.build_setting_value)
59+
60+
# Like string_list_flag, but with the validation that string_flag provides.
61+
_validated_string_list_flag = rule(
62+
implementation = _validated_string_list_flag_impl,
63+
build_setting = config.string_list(flag = True),
64+
attrs = {
65+
"values": attr.string_list(
66+
doc = "The list of allowed values for this setting. An error is raised if any other value is given.",
67+
),
68+
},
69+
doc = "A string list-typed build setting that can be set on the command line",
70+
)
71+
72+
def generate_driver_selects(name):
73+
"""Generates flags and config settings to configure the tool list.
74+
75+
By default, all supported tools are included in the "llvm" driver binary.
76+
To build only a subset, specify just the subset you want as the flag.
77+
For example, to produce a binary with just llvm-nm and llvm-size, run:
78+
79+
$ bazel build \
80+
--@llvm-project//llvm:driver-tools=llvm-nm,llvm-size \
81+
@llvm-project//llvm:llvm
82+
83+
Note: this assumes the flag name is "driver-tools" by being invoked as:
84+
generate_driver_selects(name = "driver-tools")
85+
86+
Args:
87+
name: the name of the flag that configures which tools are included.
88+
"""
89+
90+
_validated_string_list_flag(
91+
name = name,
92+
build_setting_default = _TOOLS.keys(),
93+
values = _TOOLS.keys(),
94+
)
95+
for tool in _TOOLS.keys():
96+
native.config_setting(
97+
name = "{}-include-{}".format(name, tool),
98+
flag_values = {name: tool},
99+
)
100+
101+
def select_driver_tools(flag):
102+
"""Produce a list of tool deps based on generate_driver_selects().
103+
104+
Args:
105+
flag: name that was used for generate_driver_selects().
106+
Returns:
107+
List of tool deps based on generate_driver_selects().
108+
"""
109+
tools = []
110+
for tool, target in _TOOLS.items():
111+
tools += select({
112+
"{}-include-{}".format(flag, tool): [target],
113+
"//conditions:default": [],
114+
})
115+
return tools
116+
117+
def _generate_driver_tools_def_impl(ctx):
118+
# Depending on how the LLVM build files are included,
119+
# it may or may not have the @llvm-project repo prefix.
120+
# Compare just on the name. We could also include the package,
121+
# but the name itself is unique in practice.
122+
label_to_name = {Label(v).name: k for k, v in _TOOLS.items()}
123+
124+
# Reverse sort by the *main* tool name, but keep aliases together.
125+
# This is consistent with how tools/llvm-driver/CMakeLists.txt does it,
126+
# and this makes sure that more specific tools are checked first.
127+
# For example, "clang-scan-deps" should not match "clang".
128+
tools = [label_to_name[tool.label.name] for tool in ctx.attr.driver_tools]
129+
tool_alias_pairs = []
130+
for tool_name in reversed(tools):
131+
tool_alias_pairs.append((tool_name, tool_name))
132+
for extra_alias in _EXTRA_ALIASES.get(tool_name, []):
133+
tool_alias_pairs.append((tool_name, extra_alias))
134+
135+
lines = [
136+
'LLVM_DRIVER_TOOL("{alias}", {tool})'.format(
137+
tool = tool_name.replace("-", "_"),
138+
alias = alias.removeprefix("llvm-"),
139+
)
140+
for (tool_name, alias) in tool_alias_pairs
141+
]
142+
lines.append("#undef LLVM_DRIVER_TOOL")
143+
144+
ctx.actions.write(
145+
output = ctx.outputs.out,
146+
content = "\n".join(lines),
147+
)
148+
149+
generate_driver_tools_def = rule(
150+
implementation = _generate_driver_tools_def_impl,
151+
doc = """Generate a list of LLVM_DRIVER_TOOL macros.
152+
See tools/llvm-driver/CMakeLists.txt for the reference implementation.""",
153+
attrs = {
154+
"driver_tools": attr.label_list(
155+
doc = "List of tools to include in the generated header. Use select_driver_tools() to provide this.",
156+
providers = [CcInfo],
157+
),
158+
"out": attr.output(
159+
doc = "Name of the generated .def output file.",
160+
mandatory = True,
161+
),
162+
},
163+
)
164+
165+
def llvm_driver_cc_binary(
166+
name,
167+
deps = None,
168+
**kwargs):
169+
"""cc_binary wrapper for binaries using the llvm-driver template."""
170+
expand_template(
171+
name = "_gen_" + name,
172+
out = name + "-driver.cpp",
173+
substitutions = {"@TOOL_NAME@": name.replace("-", "_")},
174+
template = "//llvm:cmake/modules/llvm-driver-template.cpp.in",
175+
)
176+
deps = deps or []
177+
native.cc_binary(
178+
name = name,
179+
srcs = [name + "-driver.cpp"],
180+
deps = deps + ["//llvm:Support"],
181+
**kwargs
182+
)

0 commit comments

Comments
 (0)