[llvm] [bazel] Add support for LLVM_TOOL_LLVM_DRIVER_BUILD (PR #86879)

Jordan Rupprecht via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 28 17:52:12 PDT 2024


https://github.com/rupprecht updated https://github.com/llvm/llvm-project/pull/86879

>From 1d7b84b12c26f0756d3d9b5d00860a8f15cd8d28 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Wed, 27 Mar 2024 21:53:32 +0000
Subject: [PATCH 1/5] [bazel] Add support for LLVM_TOOL_LLVM_DRIVER_BUILD

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 \
    -- at llvm-project//llvm:driver-tools=llvm-nm,llvm-size \
    @llvm-project//llvm:llvm
```
---
 .../llvm-project-overlay/llvm/BUILD.bazel     |  99 +++++-----
 .../llvm-project-overlay/llvm/driver.bzl      | 179 ++++++++++++++++++
 2 files changed, 233 insertions(+), 45 deletions(-)
 create mode 100644 utils/bazel/llvm-project-overlay/llvm/driver.bzl

diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
index 0e658353c36f3d..d66f639a068cd1 100644
--- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
@@ -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")
@@ -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 = [
@@ -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",
@@ -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(
@@ -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",
@@ -4178,6 +4187,12 @@ cc_binary(
     ],
 )
 
+llvm_driver_cc_binary(
+    name = "llvm-nm",
+    stamp = 0,
+    deps = [":llvm-nm-lib"],
+)
+
 gentbl(
     name = "llvm-objcopy-opts",
     strip_include_prefix = "tools/llvm-objcopy",
@@ -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",
@@ -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([
diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
new file mode 100644
index 00000000000000..85dbce82f041f6
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -0,0 +1,179 @@
+# 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": "//clang:clang-driver",
+    # "clang-scan-deps": "//clang:clang-scan-deps-lib",
+    # "dsymutil": "//llvm:dsymutil-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",
+    # "lld": "//lld:lld-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 tools in LLVM 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 \
+            -- at 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.
+    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
+    )

>From 0b78bfa9f7ec090e433541d9b9b54e3cf8303808 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Wed, 27 Mar 2024 22:19:06 +0000
Subject: [PATCH 2/5] Add comment about reverse sorting

---
 utils/bazel/llvm-project-overlay/llvm/driver.bzl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
index 85dbce82f041f6..63facf6315bf3e 100644
--- a/utils/bazel/llvm-project-overlay/llvm/driver.bzl
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -122,6 +122,9 @@ def _generate_driver_tools_def_impl(ctx):
     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):

>From aa791e0aebfdd03651d27bdaf13bf0dca27aeab7 Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Thu, 28 Mar 2024 21:10:28 +0000
Subject: [PATCH 3/5] sort list of tools

---
 utils/bazel/llvm-project-overlay/llvm/driver.bzl | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
index 63facf6315bf3e..40afbe862e4139 100644
--- a/utils/bazel/llvm-project-overlay/llvm/driver.bzl
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -11,9 +11,10 @@ load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
 # TODO: uncomment the remaining targets after splitting them
 # into separate library/binary targets.
 _TOOLS = {
-    # "clang": "//clang:clang-driver",
     # "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",
@@ -32,7 +33,6 @@ _TOOLS = {
     "llvm-size": "//llvm:llvm-size-lib",
     # "llvm-symbolizer": "//llvm:llvm-symbolizer-lib",
     # "sancov": "//llvm:sancov-lib",
-    # "lld": "//lld:lld-lib",
 }
 
 # Tools automatically get their own name as an alias, but there may be additional

>From ee69ec8b198732f5d1dea475ebc488671295bbfa Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Thu, 28 Mar 2024 21:16:06 +0000
Subject: [PATCH 4/5] all _supported_ tools

---
 utils/bazel/llvm-project-overlay/llvm/driver.bzl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
index 40afbe862e4139..7dea1577b6f920 100644
--- a/utils/bazel/llvm-project-overlay/llvm/driver.bzl
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -72,7 +72,7 @@ _validated_string_list_flag = rule(
 def generate_driver_selects(name):
     """Generates flags and config settings to configure the tool list.
 
-    By default, all tools in LLVM are included in the "llvm" driver binary.
+    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:
 

>From e4998a1f447422172768eb5a7c2cebd7cd4bc72a Mon Sep 17 00:00:00 2001
From: Jordan Rupprecht <rupprecht at google.com>
Date: Fri, 29 Mar 2024 00:28:26 +0000
Subject: [PATCH 5/5] Minor comment change

---
 utils/bazel/llvm-project-overlay/llvm/driver.bzl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/bazel/llvm-project-overlay/llvm/driver.bzl b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
index 7dea1577b6f920..bd0d26d64f481b 100644
--- a/utils/bazel/llvm-project-overlay/llvm/driver.bzl
+++ b/utils/bazel/llvm-project-overlay/llvm/driver.bzl
@@ -116,7 +116,7 @@ def select_driver_tools(flag):
 
 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.
+    # 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()}



More information about the llvm-commits mailing list