[llvm] [bazel] Generate InstrumentorVariables.inc from its .inc.in template (PR #214242)

David Young via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 07:38:26 PDT 2026


https://github.com/youngd007 created https://github.com/llvm/llvm-project/pull/214242

The Bazel build is the odd one out for this generated file. CMake (llvm/include/CMakeLists.txt) does file(READ) + configure_file(@ONLY) on InstrumentorVariables.inc.in, and GN (write_cmake_config, with the LLVM_INSTRUMENTOR_RUNTIME_HELPER=@file:... value) expands the same template. Bazel instead synthesizes the raw-string wrapper inline in a genrule and never reads the template at all:

  cmd = "printf 'constexpr char InstrumentorRuntimeHelper[] = R\"(' > $@ && \
         cat $(SRCS) >> $@ && echo ')\";' >> $@"

So the Bazel output silently omits the template's license header and its "autogenerated by the LLVM build. Do not edit!" banner, and any future edit to the .inc.in is picked up by CMake and GN but not by Bazel.

Expand the template instead, via a small configure_file.py helper added to the overlay. --substitution NAME=VALUE takes a literal value; --substitution-file NAME=PATH takes the value from a file's contents, which is the direct analogue of GN's `@file:` syntax and of CMake's file(READ) + configure_file. The helper lives in the overlay directory, so it adds no files to the LLVM source tree and is reusable for the next *.inc.in.

Two things fall out of this:

- This was the only genrule() in llvm/BUILD.bazel; every other generated file already goes through expand_template / write_file / gentbl_*. The one-off now matches the file's own conventions.

- The command no longer depends on a POSIX shell. printf and cat do not exist under cmd.exe, and it does not strip the single quotes, so on a Windows exec platform the rule produced only the `R"(` prefix -- an unterminated raw string that fails to compile in InstrumentorStubPrinter.cpp.

Output verified byte-identical to the CMake-generated InstrumentorVariables.inc.

>From adf91f4bf699eb0bc1f54865ecf7c7f685f06b77 Mon Sep 17 00:00:00 2001
From: David Young <davidayoung at meta.com>
Date: Wed, 5 Aug 2026 07:31:53 -0700
Subject: [PATCH] [bazel] Generate InstrumentorVariables.inc from its .inc.in
 template

The Bazel build is the odd one out for this generated file. CMake
(llvm/include/CMakeLists.txt) does file(READ) + configure_file(@ONLY) on
InstrumentorVariables.inc.in, and GN (write_cmake_config, with the
LLVM_INSTRUMENTOR_RUNTIME_HELPER=@file:... value) expands the same template.
Bazel instead synthesizes the raw-string wrapper inline in a genrule and never
reads the template at all:

  cmd = "printf 'constexpr char InstrumentorRuntimeHelper[] = R\"(' > $@ && \
         cat $(SRCS) >> $@ && echo ')\";' >> $@"

So the Bazel output silently omits the template's license header and its
"autogenerated by the LLVM build. Do not edit!" banner, and any future edit to
the .inc.in is picked up by CMake and GN but not by Bazel.

Expand the template instead, via a small configure_file.py helper added to the
overlay. --substitution NAME=VALUE takes a literal value; --substitution-file
NAME=PATH takes the value from a file's contents, which is the direct analogue
of GN's `@file:` syntax and of CMake's file(READ) + configure_file. The helper
lives in the overlay directory, so it adds no files to the LLVM source tree and
is reusable for the next *.inc.in.

Two things fall out of this:

- This was the only genrule() in llvm/BUILD.bazel; every other generated file
  already goes through expand_template / write_file / gentbl_*. The one-off now
  matches the file's own conventions.

- The command no longer depends on a POSIX shell. printf and cat do not exist
  under cmd.exe, and it does not strip the single quotes, so on a Windows exec
  platform the rule produced only the `R"(` prefix -- an unterminated raw
  string that fails to compile in InstrumentorStubPrinter.cpp.

Output verified byte-identical to the CMake-generated InstrumentorVariables.inc.
---
 .../llvm-project-overlay/llvm/BUILD.bazel     | 19 +++++++-
 .../llvm/configure_file.py                    | 44 +++++++++++++++++++
 2 files changed, 61 insertions(+), 2 deletions(-)
 create mode 100644 utils/bazel/llvm-project-overlay/llvm/configure_file.py

diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
index f57e3f8db2f5b..7a0a063232ef3 100644
--- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel
@@ -2156,11 +2156,26 @@ cc_library(
     ],
 )
 
+# Bazel equivalent of CMake's configure_file / GN's write_cmake_config, so the
+# same *.inc.in template can be expanded by all three build systems.
+py_binary(
+    name = "configure_file",
+    srcs = ["configure_file.py"],
+)
+
 genrule(
     name = "instrumentor_variables_gen",
-    srcs = ["include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h"],
+    srcs = [
+        "include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h",
+        "include/llvm/Transforms/IPO/InstrumentorVariables.inc.in",
+    ],
     outs = ["include/llvm/Transforms/IPO/InstrumentorVariables.inc"],
-    cmd = "printf 'constexpr char InstrumentorRuntimeHelper[] = R\"(' > $@ && cat $(SRCS) >> $@ && echo ')\";' >> $@",
+    cmd = ("$(execpath :configure_file)" +
+           " --template $(execpath include/llvm/Transforms/IPO/InstrumentorVariables.inc.in)" +
+           " --substitution-file LLVM_INSTRUMENTOR_RUNTIME_HELPER=" +
+           "$(execpath include/llvm/Transforms/IPO/InstrumentorRuntimeHelper.h)" +
+           " --out $@"),
+    tools = [":configure_file"],
 )
 
 cc_library(
diff --git a/utils/bazel/llvm-project-overlay/llvm/configure_file.py b/utils/bazel/llvm-project-overlay/llvm/configure_file.py
new file mode 100644
index 0000000000000..b0a4652ebcba9
--- /dev/null
+++ b/utils/bazel/llvm-project-overlay/llvm/configure_file.py
@@ -0,0 +1,44 @@
+"""Expand @VAR@ placeholders in a template file.
+
+The Bazel counterpart of CMake's configure_file(@ONLY) and GN's
+write_cmake_config, so a template such as InstrumentorVariables.inc.in produces
+identical output under all three build systems.
+
+Values come from --substitution NAME=VALUE, or --substitution-file NAME=PATH to
+take the value from a file's contents -- the analogue of GN's `@file:` syntax
+and CMake's file(READ) + configure_file.
+"""
+
+import argparse
+import sys
+from pathlib import Path
+
+
+def main(argv=None):
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("--template", required=True)
+    parser.add_argument("--out", required=True)
+    parser.add_argument(
+        "--substitution", action="append", default=[], metavar="NAME=VALUE"
+    )
+    parser.add_argument(
+        "--substitution-file", action="append", default=[], metavar="NAME=PATH"
+    )
+    args = parser.parse_args(argv)
+
+    values = {}
+    for spec in args.substitution:
+        name, _, value = spec.partition("=")
+        values[name] = value
+    for spec in args.substitution_file:
+        name, _, path = spec.partition("=")
+        values[name] = Path(path).read_text(encoding="utf-8")
+
+    text = Path(args.template).read_text(encoding="utf-8")
+    for name, value in values.items():
+        text = text.replace("@%s@" % name, value)
+    Path(args.out).write_text(text, encoding="utf-8")
+
+
+if __name__ == "__main__":
+    sys.exit(main())



More information about the llvm-commits mailing list