Skip to content

[bazel] Add support for LLVM_TOOL_LLVM_DRIVER_BUILD #86879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Mar 29, 2024
Merged
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
99 changes: 54 additions & 45 deletions utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
load("//mlir:tblgen.bzl", "td_library")
load(":binary_alias.bzl", "binary_alias")
load(":config.bzl", "llvm_config_defines")
load(":driver.bzl", "generate_driver_selects", "generate_driver_tools_def", "llvm_driver_cc_binary", "select_driver_tools")
load(":enum_targets_gen.bzl", "enum_targets_gen")
load(":targets.bzl", "llvm_targets")
load(":tblgen.bzl", "gentbl")
Expand Down Expand Up @@ -601,6 +602,32 @@ cc_library(
],
)

# Command line flag to control which tools get included in the llvm driver binary.
# The macro also generates config_setting targets used by select_driver_tools().
generate_driver_selects(name = "driver-tools")

generate_driver_tools_def(
name = "gen_llvm_driver_tools_def",
out = "LLVMDriverTools.def",
driver_tools = select_driver_tools(":driver-tools"),
)

# Workaround inability to put `.def` files into `srcs` with a library
cc_library(
name = "llvm_driver_tools_def_lib",
includes = ["."],
textual_hdrs = ["LLVMDriverTools.def"],
)

cc_binary(
name = "llvm",
srcs = glob(["tools/llvm-driver/*.cpp"]),
deps = [
":Support",
":llvm_driver_tools_def_lib",
] + select_driver_tools(":driver-tools"),
)

cc_binary(
name = "llvm-min-tblgen",
srcs = [
Expand Down Expand Up @@ -3300,22 +3327,10 @@ cc_binary(
],
)

expand_template(
name = "ar_main",
out = "llvm-ar-driver.cpp",
substitutions = {
"@TOOL_NAME@": "llvm_ar",
},
template = "cmake/modules/llvm-driver-template.cpp.in",
)

cc_binary(
name = "llvm-ar",
srcs = glob([
"tools/llvm-ar/*.cpp",
]) + ["llvm-ar-driver.cpp"],
cc_library(
name = "llvm-ar-lib",
srcs = glob(["tools/llvm-ar/*.cpp"]),
copts = llvm_copts,
stamp = 0,
deps = [
":AllTargetsAsmParsers",
":AllTargetsCodeGens",
Expand All @@ -3329,6 +3344,12 @@ cc_binary(
],
)

llvm_driver_cc_binary(
name = "llvm-ar",
stamp = 0,
deps = [":llvm-ar-lib"],
)

# We need to run llvm-ar with different basenames to make it run with
# different behavior.
binary_alias(
Expand Down Expand Up @@ -4146,22 +4167,10 @@ gentbl(
td_srcs = ["include/llvm/Option/OptParser.td"],
)

expand_template(
name = "nm_main",
out = "llvm-nm-driver.cpp",
substitutions = {
"@TOOL_NAME@": "llvm_nm",
},
template = "cmake/modules/llvm-driver-template.cpp.in",
)

cc_binary(
name = "llvm-nm",
srcs = glob([
"tools/llvm-nm/*.cpp",
]) + ["llvm-nm-driver.cpp"],
cc_library(
name = "llvm-nm-lib",
srcs = glob(["tools/llvm-nm/*.cpp"]),
copts = llvm_copts,
stamp = 0,
deps = [
":AllTargetsAsmParsers",
":AllTargetsCodeGens",
Expand All @@ -4178,6 +4187,12 @@ cc_binary(
],
)

llvm_driver_cc_binary(
name = "llvm-nm",
stamp = 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need guidance when and where we use stamp = 0https://bazel.build/reference/be/c-cpp) as a comment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. Some people may have strong opinions on stamping. I'm not sure if there's a reason we have stamp = 0 in these build files. Obviously there are caching benefits, but you also get those more broadly by building with bazel build --nostamp. I can't find advice on this, e.g. in a style guide or best practices list, but I'll add a comment later if I can figure anything out.

(btw, this looks like a new line, but it's just moved from a few lines above)

deps = [":llvm-nm-lib"],
)

gentbl(
name = "llvm-objcopy-opts",
strip_include_prefix = "tools/llvm-objcopy",
Expand Down Expand Up @@ -4629,22 +4644,10 @@ gentbl(
td_srcs = ["include/llvm/Option/OptParser.td"],
)

expand_template(
name = "size_main",
out = "llvm-size-driver.cpp",
substitutions = {
"@TOOL_NAME@": "llvm_size",
},
template = "cmake/modules/llvm-driver-template.cpp.in",
)

cc_binary(
name = "llvm-size",
srcs = glob([
"tools/llvm-size/*.cpp",
]) + ["llvm-size-driver.cpp"],
cc_library(
name = "llvm-size-lib",
srcs = glob(["tools/llvm-size/*.cpp"]),
copts = llvm_copts,
stamp = 0,
deps = [
":Object",
":Option",
Expand All @@ -4653,6 +4656,12 @@ cc_binary(
],
)

llvm_driver_cc_binary(
name = "llvm-size",
stamp = 0,
deps = [":llvm-size-lib"],
)

cc_binary(
name = "llvm-split",
srcs = glob([
Expand Down
182 changes: 182 additions & 0 deletions utils/bazel/llvm-project-overlay/llvm/driver.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

"""Configuration for the llvm-driver tool."""

load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")

# Mapping from every tool to the cc_library that implements the tool's entrypoint.
# TODO: uncomment the remaining targets after splitting them
# into separate library/binary targets.
_TOOLS = {
# "clang-scan-deps": "//clang:clang-scan-deps-lib",
# "clang": "//clang:clang-driver",
# "dsymutil": "//llvm:dsymutil-lib",
# "lld": "//lld:lld-lib",
"llvm-ar": "//llvm:llvm-ar-lib",
# "llvm-cxxfilt": "//llvm:llvm-cxxfilt-lib",
# "llvm-dwp": "//llvm:llvm-dwp-lib",
# "llvm-gsymutil": "//llvm:llvm-gsymutil-lib",
# "llvm-ifs": "//llvm:llvm-ifs-lib",
# "llvm-libtool-darwin": "//llvm:llvm-libtool-darwin-lib",
# "llvm-lipo": "//llvm:llvm-lipo-lib",
# "llvm-ml": "//llvm:llvm-ml-lib",
# "llvm-mt": "//llvm:llvm-mt-lib",
"llvm-nm": "//llvm:llvm-nm-lib",
# "llvm-objcopy": "//llvm:llvm-objcopy-lib",
# "llvm-objdump": "//llvm:llvm-objdump-lib",
# "llvm-profdata": "//llvm:llvm-profdata-lib",
# "llvm-rc": "//llvm:llvm-rc-lib",
# "llvm-readobj": "//llvm:llvm-readobj-lib",
"llvm-size": "//llvm:llvm-size-lib",
# "llvm-symbolizer": "//llvm:llvm-symbolizer-lib",
# "sancov": "//llvm:sancov-lib",
}

# Tools automatically get their own name as an alias, but there may be additional
# aliases for a given tool.
_EXTRA_ALIASES = {
"clang": ["clang++", "clang-cl", "clang-cpp"],
"lld": ["lld-link", "ld.lld", "ld64.lld", "wasm-ld"],
"llvm-ar": ["ranlib", "lib", "dlltool"],
"llvm-objcopy": ["bitcode-strip", "install-name-tool", "strip"],
"llvm-objdump": ["otool"],
"llvm-rc": ["windres"],
"llvm-readobj": ["readelf"],
"llvm-symbolizer": ["addr2line"],
}

def _validated_string_list_flag_impl(ctx):
invalid_values = [v for v in ctx.build_setting_value if v not in ctx.attr.values]
if invalid_values:
fail("Tool(s) [{}] are not in the known list of tools: {}".format(
", ".join(invalid_values),
", ".join(ctx.attr.values),
))
return BuildSettingInfo(value = ctx.build_setting_value)

# Like string_list_flag, but with the validation that string_flag provides.
_validated_string_list_flag = rule(
implementation = _validated_string_list_flag_impl,
build_setting = config.string_list(flag = True),
attrs = {
"values": attr.string_list(
doc = "The list of allowed values for this setting. An error is raised if any other value is given.",
),
},
doc = "A string list-typed build setting that can be set on the command line",
)

def generate_driver_selects(name):
"""Generates flags and config settings to configure the tool list.

By default, all supported tools are included in the "llvm" driver binary.
To build only a subset, specify just the subset you want as the flag.
For example, to produce a binary with just llvm-nm and llvm-size, run:

$ bazel build \
--@llvm-project//llvm:driver-tools=llvm-nm,llvm-size \
@llvm-project//llvm:llvm

Note: this assumes the flag name is "driver-tools" by being invoked as:
generate_driver_selects(name = "driver-tools")

Args:
name: the name of the flag that configures which tools are included.
"""

_validated_string_list_flag(
name = name,
build_setting_default = _TOOLS.keys(),
values = _TOOLS.keys(),
)
for tool in _TOOLS.keys():
native.config_setting(
name = "{}-include-{}".format(name, tool),
flag_values = {name: tool},
)

def select_driver_tools(flag):
"""Produce a list of tool deps based on generate_driver_selects().

Args:
flag: name that was used for generate_driver_selects().
Returns:
List of tool deps based on generate_driver_selects().
"""
tools = []
for tool, target in _TOOLS.items():
tools += select({
"{}-include-{}".format(flag, tool): [target],
"//conditions:default": [],
})
return tools

def _generate_driver_tools_def_impl(ctx):
# Depending on how the LLVM build files are included,
# it may or may not have the @llvm-project repo prefix.
# Compare just on the name. We could also include the package,
# but the name itself is unique in practice.
label_to_name = {Label(v).name: k for k, v in _TOOLS.items()}

# Reverse sort by the *main* tool name, but keep aliases together.
# This is consistent with how tools/llvm-driver/CMakeLists.txt does it,
# and this makes sure that more specific tools are checked first.
# For example, "clang-scan-deps" should not match "clang".
tools = [label_to_name[tool.label.name] for tool in ctx.attr.driver_tools]
tool_alias_pairs = []
for tool_name in reversed(tools):
tool_alias_pairs.append((tool_name, tool_name))
for extra_alias in _EXTRA_ALIASES.get(tool_name, []):
tool_alias_pairs.append((tool_name, extra_alias))

lines = [
'LLVM_DRIVER_TOOL("{alias}", {tool})'.format(
tool = tool_name.replace("-", "_"),
alias = alias.removeprefix("llvm-"),
)
for (tool_name, alias) in tool_alias_pairs
]
lines.append("#undef LLVM_DRIVER_TOOL")

ctx.actions.write(
output = ctx.outputs.out,
content = "\n".join(lines),
)

generate_driver_tools_def = rule(
implementation = _generate_driver_tools_def_impl,
doc = """Generate a list of LLVM_DRIVER_TOOL macros.
See tools/llvm-driver/CMakeLists.txt for the reference implementation.""",
attrs = {
"driver_tools": attr.label_list(
doc = "List of tools to include in the generated header. Use select_driver_tools() to provide this.",
providers = [CcInfo],
),
"out": attr.output(
doc = "Name of the generated .def output file.",
mandatory = True,
),
},
)

def llvm_driver_cc_binary(
name,
deps = None,
**kwargs):
"""cc_binary wrapper for binaries using the llvm-driver template."""
expand_template(
name = "_gen_" + name,
out = name + "-driver.cpp",
substitutions = {"@TOOL_NAME@": name.replace("-", "_")},
template = "//llvm:cmake/modules/llvm-driver-template.cpp.in",
)
deps = deps or []
native.cc_binary(
name = name,
srcs = [name + "-driver.cpp"],
deps = deps + ["//llvm:Support"],
**kwargs
)