[libc-commits] [libc] [libc] Add hdrgen --proxy mode (PR #174823)

Roland McGrath via libc-commits libc-commits at lists.llvm.org
Wed Jan 7 10:25:20 PST 2026


https://github.com/frobtech updated https://github.com/llvm/llvm-project/pull/174823

>From 564d0fbf640feb95b1e3631ebeaf3e1a2b12b5bb Mon Sep 17 00:00:00 2001
From: Roland McGrath <mcgrathr at google.com>
Date: Wed, 7 Jan 2026 10:21:17 -0800
Subject: [PATCH] [libc] Add hdrgen --proxy mode

This adds the --proxy switch to generate headers like go
into libc/src/hdr/foo-proxy.h instead of public headers.
---
 libc/utils/hdrgen/hdrgen/header.py            | 75 ++++++++++++++-----
 libc/utils/hdrgen/hdrgen/main.py              | 15 +++-
 .../tests/expected_output/test_small_proxy.h  | 27 +++++++
 libc/utils/hdrgen/tests/test_integration.py   |  6 ++
 4 files changed, 103 insertions(+), 20 deletions(-)
 create mode 100644 libc/utils/hdrgen/tests/expected_output/test_small_proxy.h

diff --git a/libc/utils/hdrgen/hdrgen/header.py b/libc/utils/hdrgen/hdrgen/header.py
index f592327f06ad6..5d2350ab14367 100644
--- a/libc/utils/hdrgen/hdrgen/header.py
+++ b/libc/utils/hdrgen/hdrgen/header.py
@@ -75,6 +75,29 @@
     "SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception",
 ]
 
+PROXY_TEMPLATE = """\
+//===-- Implementation proxy header for <{header}> --===//
+//
+{license_lines}
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef {guard}
+#define {guard}
+
+#ifdef LIBC_FULL_BUILD
+
+{include_lines}
+
+#else // Overlay mode
+
+#include <{header}>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // {guard}
+"""
+
 
 class HeaderFile:
     def __init__(self, name):
@@ -158,10 +181,11 @@ def includes(self):
             }
         )
 
-    def header_guard(self):
-        return "_LLVM_LIBC_" + "_".join(
-            word.upper() for word in NONIDENTIFIER.split(self.name) if word
-        )
+    def header_guard(self, proxy=False):
+        words = [word.upper() for word in NONIDENTIFIER.split(self.name) if word]
+        if proxy:
+            return "LLVM_LIBC_HDR_" + "_".join(words[:-1]) + "_PROXY_H"
+        return "_LLVM_LIBC_" + "_".join(words)
 
     def library_description(self):
         descriptions = LIBRARY_DESCRIPTIONS | self.extra_standards
@@ -203,43 +227,60 @@ def template(self, dir, files_read):
             license_lines=self.license_lines(),
         )
 
-    def public_api(self):
+    def include_lines(self, with_common=False):
         # Python 3.12 has .relative_to(dir, walk_up=True) for this.
         path_prefix = PurePosixPath("../" * (len(PurePosixPath(self.name).parents) - 1))
 
         def relpath(file):
             return path_prefix / file
 
-        content = []
-
-        if self.template_file is None:
-            # This always goes before all the other includes, which are sorted.
-            # It's implicitly emitted here when using the default template so
-            # it can get the right relative path.  Custom template files should
-            # all have it explicitly with their right particular relative path.
-            content.append('#include "{file!s}"'.format(file=relpath(COMMON_HEADER)))
-
-        content += [
+        # This always goes before all the other includes, which are sorted.
+        # It's implicitly emitted here when using the default template so
+        # it can get the right relative path.  Custom template files should
+        # all have it explicitly with their right particular relative path.
+        return [
             f"#include {file}"
-            for file in sorted(
+            for file in ([f'"{relpath(COMMON_HEADER)!s}"'] if with_common else [])
+            + sorted(
                 file if isinstance(file, str) else f'"{relpath(file)!s}"'
                 for file in self.includes()
             )
         ]
 
+    def macro_lines(self):
+        content = []
         for macro in sorted(self.macros):
             # When there is nothing to define, the Macro object converts to str
             # as an empty string.  Don't emit a blank line for those cases.
             if str(macro):
                 content.extend(["", f"{macro}"])
+        return content
 
+    def enum_lines(self):
+        content = []
         if self.enumerations:
             combined_enum_content = ",\n  ".join(
                 str(enum) for enum in self.enumerations
             )
             content.append(f"\nenum {{\n  {combined_enum_content},\n}};")
+        return content
 
-        content.append("\n__BEGIN_C_DECLS\n")
+    def proxy_contents(self):
+        return PROXY_TEMPLATE.format(
+            header=self.name,
+            guard=self.header_guard(proxy=True),
+            license_lines=self.license_lines(),
+            include_lines="\n".join(self.include_lines()),
+            macro_lines="\n".join(self.macro_lines()),
+        )
+
+    def public_api(self):
+        content = (
+            self.include_lines(self.template_file is None)
+            + self.macro_lines()
+            + self.enum_lines()
+            + ["\n__BEGIN_C_DECLS\n"]
+        )
 
         current_guard = None
         last_name = None
diff --git a/libc/utils/hdrgen/hdrgen/main.py b/libc/utils/hdrgen/hdrgen/main.py
index c12e89ef771d1..eaeb9c6aedb04 100755
--- a/libc/utils/hdrgen/hdrgen/main.py
+++ b/libc/utils/hdrgen/hdrgen/main.py
@@ -49,6 +49,12 @@ def main():
         action="store_true",
         default=False,
     )
+    parser.add_argument(
+        "--proxy",
+        help="Generate a libc/hdr proxy header of a public header",
+        action="store_true",
+        default=False,
+    )
     parser.add_argument(
         "-e",
         "--entry-point",
@@ -116,9 +122,12 @@ def merge_from(paths):
     else:
         [yaml_file] = args.yaml_file
         header = load_header(yaml_file)
-        # The header_template path is relative to the containing YAML file.
-        template = header.template(yaml_file.parent, files_read)
-        contents = fill_public_api(header.public_api(), template)
+        if args.proxy:
+            contents = header.proxy_contents()
+        else:
+            # The header_template path is relative to the containing YAML file.
+            template = header.template(yaml_file.parent, files_read)
+            contents = fill_public_api(header.public_api(), template)
 
     write_depfile()
 
diff --git a/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h b/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h
new file mode 100644
index 0000000000000..4cb950f1f51c8
--- /dev/null
+++ b/libc/utils/hdrgen/tests/expected_output/test_small_proxy.h
@@ -0,0 +1,27 @@
+//===-- Implementation proxy header for <test_small.h> --===//
+//
+// Part of the LLVM Project, 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
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TEST_SMALL_PROXY_H
+#define LLVM_LIBC_HDR_TEST_SMALL_PROXY_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "llvm-libc-macros/CONST_FUNC_A.h"
+#include "llvm-libc-macros/test_more-macros.h"
+#include "llvm-libc-macros/test_small-macros.h"
+#include "llvm-libc-types/float128.h"
+#include "llvm-libc-types/type_a.h"
+#include "llvm-libc-types/type_b.h"
+
+#else // Overlay mode
+
+#include <test_small.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TEST_SMALL_PROXY_H
diff --git a/libc/utils/hdrgen/tests/test_integration.py b/libc/utils/hdrgen/tests/test_integration.py
index b975d8ff007b1..4ec3fa566c003 100644
--- a/libc/utils/hdrgen/tests/test_integration.py
+++ b/libc/utils/hdrgen/tests/test_integration.py
@@ -82,6 +82,12 @@ def test_sorting(self):
         self.run_script(yaml_file, output_file)
         self.compare_files(output_file, expected_output_file)
 
+    def test_generate_header(self):
+        yaml_file = self.source_dir / "input/test_small.yaml"
+        expected_output_file = self.source_dir / "expected_output/test_small_proxy.h"
+        output_file = self.output_dir / "test_small.h"
+        self.run_script(yaml_file, output_file, switches=["--proxy"])
+        self.compare_files(output_file, expected_output_file)
 
 def main():
     parser = argparse.ArgumentParser(description="TestHeaderGenIntegration arguments")



More information about the libc-commits mailing list