[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:24:41 PST 2026
https://github.com/frobtech created https://github.com/llvm/llvm-project/pull/174823
This adds the --proxy switch to generate headers like go
into libc/src/hdr/foo-proxy.h instead of public headers.
>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