xref: /llvm-project/utils/bazel/llvm-project-overlay/llvm/driver.bzl (revision 2e4e918bf03868bb4cd0d0415766cfba8dc1d899)
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
7load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
8load("@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_TOOLS = {
12    "clang-scan-deps": "//clang:clang-scan-deps-lib",
13    "clang": "//clang:clang-driver",
14    "dsymutil": "//llvm:dsymutil-lib",
15    "lld": "//lld:lld-lib",
16    "llvm-ar": "//llvm:llvm-ar-lib",
17    "llvm-cgdata": "//llvm:llvm-cgdata-lib",
18    "llvm-cxxfilt": "//llvm:llvm-cxxfilt-lib",
19    "llvm-debuginfod-find": "//llvm:llvm-debuginfod-find-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": ["ld", "lld-link", "ld.lld", "ld64.lld", "wasm-ld"],
43    "llvm-ar": ["ranlib", "lib", "dlltool"],
44    "llvm-cxxfilt": ["c++filt"],
45    "llvm-objcopy": ["bitcode-strip", "install-name-tool", "strip"],
46    "llvm-objdump": ["otool"],
47    "llvm-rc": ["windres"],
48    "llvm-readobj": ["readelf"],
49    "llvm-symbolizer": ["addr2line"],
50}
51
52def _validated_string_list_flag_impl(ctx):
53    invalid_values = [v for v in ctx.build_setting_value if v not in ctx.attr.values]
54    if invalid_values:
55        fail("Tool(s) [{}] are not in the known list of tools: {}".format(
56            ", ".join(invalid_values),
57            ", ".join(ctx.attr.values),
58        ))
59    return BuildSettingInfo(value = ctx.build_setting_value)
60
61# Like string_list_flag, but with the validation that string_flag provides.
62_validated_string_list_flag = rule(
63    implementation = _validated_string_list_flag_impl,
64    build_setting = config.string_list(flag = True),
65    attrs = {
66        "values": attr.string_list(
67            doc = "The list of allowed values for this setting. An error is raised if any other value is given.",
68        ),
69    },
70    doc = "A string list-typed build setting that can be set on the command line",
71)
72
73def generate_driver_selects(name):
74    """Generates flags and config settings to configure the tool list.
75
76    By default, all supported tools are included in the "llvm" driver binary.
77    To build only a subset, specify just the subset you want as the flag.
78    For example, to produce a binary with just llvm-nm and llvm-size, run:
79
80        $ bazel build \
81            --@llvm-project//llvm:driver-tools=llvm-nm,llvm-size \
82            @llvm-project//llvm:llvm
83
84    Note: this assumes the flag name is "driver-tools" by being invoked as:
85        generate_driver_selects(name = "driver-tools")
86
87    Args:
88      name: the name of the flag that configures which tools are included.
89    """
90
91    _validated_string_list_flag(
92        name = name,
93        build_setting_default = _TOOLS.keys(),
94        values = _TOOLS.keys(),
95    )
96    for tool in _TOOLS.keys():
97        native.config_setting(
98            name = "{}-include-{}".format(name, tool),
99            flag_values = {name: tool},
100        )
101
102def select_driver_tools(flag):
103    """Produce a list of tool deps based on generate_driver_selects().
104
105    Args:
106      flag: name that was used for generate_driver_selects().
107    Returns:
108      List of tool deps based on generate_driver_selects().
109    """
110    tools = []
111    for tool, target in _TOOLS.items():
112        tools += select({
113            "{}-include-{}".format(flag, tool): [target],
114            "//conditions:default": [],
115        })
116    return tools
117
118def _generate_driver_tools_def_impl(ctx):
119    # Depending on how the LLVM build files are included,
120    # it may or may not have the @llvm-project repo prefix.
121    # Compare just on the name. We could also include the package,
122    # but the name itself is unique in practice.
123    label_to_name = {Label(v).name: k for k, v in _TOOLS.items()}
124
125    # Reverse sort by the *main* tool name, but keep aliases together.
126    # This is consistent with how tools/llvm-driver/CMakeLists.txt does it,
127    # and this makes sure that more specific tools are checked first.
128    # For example, "clang-scan-deps" should not match "clang".
129    tools = [label_to_name[tool.label.name] for tool in ctx.attr.driver_tools]
130    tool_alias_pairs = []
131    for tool_name in reversed(tools):
132        tool_alias_pairs.append((tool_name, tool_name))
133        for extra_alias in _EXTRA_ALIASES.get(tool_name, []):
134            tool_alias_pairs.append((tool_name, extra_alias))
135
136    lines = [
137        'LLVM_DRIVER_TOOL("{alias}", {tool})'.format(
138            tool = tool_name.replace("-", "_"),
139            alias = alias.removeprefix("llvm-"),
140        )
141        for (tool_name, alias) in tool_alias_pairs
142    ]
143    lines.append("#undef LLVM_DRIVER_TOOL")
144
145    ctx.actions.write(
146        output = ctx.outputs.out,
147        content = "\n".join(lines),
148    )
149
150generate_driver_tools_def = rule(
151    implementation = _generate_driver_tools_def_impl,
152    doc = """Generate a list of LLVM_DRIVER_TOOL macros.
153See tools/llvm-driver/CMakeLists.txt for the reference implementation.""",
154    attrs = {
155        "driver_tools": attr.label_list(
156            doc = "List of tools to include in the generated header. Use select_driver_tools() to provide this.",
157            providers = [CcInfo],
158        ),
159        "out": attr.output(
160            doc = "Name of the generated .def output file.",
161            mandatory = True,
162        ),
163    },
164)
165
166def llvm_driver_cc_binary(
167        name,
168        deps = None,
169        **kwargs):
170    """cc_binary wrapper for binaries using the llvm-driver template."""
171    expand_template(
172        name = "_gen_" + name,
173        out = name + "-driver.cpp",
174        substitutions = {"@TOOL_NAME@": name.replace("-", "_")},
175        template = "//llvm:cmake/modules/llvm-driver-template.cpp.in",
176    )
177    deps = deps or []
178    native.cc_binary(
179        name = name,
180        srcs = [name + "-driver.cpp"],
181        deps = deps + ["//llvm:Support"],
182        **kwargs
183    )
184