Skip to content

Commit 303f241

Browse files
authored
[libc] Make template_header optional for hdrgen (#127259)
This allows a YAML file to omit `template_header` and have no `.h.def` file. A default template is generated based purely on the information in the YAML file. This should handle most of the cases. For now, it's exercised (aside from the hdrgen tests) only for one of the simplest cases: <ctype.h>. This includes making the parser notice the "standards" YAML field at the top (header) level, not just in "functions" lists. The standards listed for the header overall and for the individual functions both feed into how a fully-generated header describes itself in comments. To go with this, files using the default generated template must stick to a new uniform set of spellings for the "standards" lists. As more custom template files are retired, the corresponding YAML files will need all their standards lists normalized. For now, ctype.yaml is updated with correct attribution for the POSIX `_l` extensions.
1 parent c662103 commit 303f241

File tree

8 files changed

+119
-64
lines changed

8 files changed

+119
-64
lines changed

libc/include/ctype.h.def

Lines changed: 0 additions & 16 deletions
This file was deleted.

libc/include/ctype.yaml

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
header: ctype.h
2-
header_template: ctype.h.def
3-
macros: []
4-
types:
5-
- type_name: locale_t
2+
standards:
3+
- stdc
64
enums: []
75
objects: []
86
functions:
@@ -104,98 +102,98 @@ functions:
104102
- type: int
105103
- name: isalnum_l
106104
standards:
107-
- stdc
105+
- posix
108106
return_type: int
109107
arguments:
110108
- type: int
111109
- type: locale_t
112110
- name: isalpha_l
113111
standards:
114-
- stdc
112+
- posix
115113
return_type: int
116114
arguments:
117115
- type: int
118116
- type: locale_t
119117
- name: isblank_l
120118
standards:
121-
- stdc
119+
- posix
122120
return_type: int
123121
arguments:
124122
- type: int
125123
- type: locale_t
126124
- name: iscntrl_l
127125
standards:
128-
- stdc
126+
- posix
129127
return_type: int
130128
arguments:
131129
- type: int
132130
- type: locale_t
133131
- name: isdigit_l
134132
standards:
135-
- stdc
133+
- posix
136134
return_type: int
137135
arguments:
138136
- type: int
139137
- type: locale_t
140138
- name: isgraph_l
141139
standards:
142-
- stdc
140+
- posix
143141
return_type: int
144142
arguments:
145143
- type: int
146144
- type: locale_t
147145
- name: islower_l
148146
standards:
149-
- stdc
147+
- posix
150148
return_type: int
151149
arguments:
152150
- type: int
153151
- type: locale_t
154152
- name: isprint_l
155153
standards:
156-
- stdc
154+
- posix
157155
return_type: int
158156
arguments:
159157
- type: int
160158
- type: locale_t
161159
- name: ispunct_l
162160
standards:
163-
- stdc
161+
- posix
164162
return_type: int
165163
arguments:
166164
- type: int
167165
- type: locale_t
168166
- name: isspace_l
169167
standards:
170-
- stdc
168+
- posix
171169
return_type: int
172170
arguments:
173171
- type: int
174172
- type: locale_t
175173
- name: isupper_l
176174
standards:
177-
- stdc
175+
- posix
178176
return_type: int
179177
arguments:
180178
- type: int
181179
- type: locale_t
182180
- name: isxdigit_l
183181
standards:
184-
- stdc
182+
- posix
185183
return_type: int
186184
arguments:
187185
- type: int
188186
- type: locale_t
189187
- name: tolower_l
190188
standards:
191-
- stdc
189+
- posix
192190
return_type: int
193191
arguments:
194192
- type: int
195193
- type: locale_t
196194
- name: toupper_l
197195
standards:
198-
- stdc
196+
- posix
199197
return_type: int
200198
arguments:
201199
- type: int

libc/utils/hdrgen/header.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77
# ==-------------------------------------------------------------------------==#
88

9+
import re
910
from functools import reduce
1011
from pathlib import PurePosixPath
1112

@@ -30,6 +31,37 @@
3031
COMPILER_HEADER_TYPES.update({f"int{size}_t": "<stdint.h>" for size in STDINT_SIZES})
3132
COMPILER_HEADER_TYPES.update({f"uint{size}_t": "<stdint.h>" for size in STDINT_SIZES})
3233

34+
NONIDENTIFIER = re.compile("[^a-zA-Z0-9_]+")
35+
36+
COMMON_HEADER = PurePosixPath("__llvm-libc-common.h")
37+
38+
# All the canonical identifiers are in lowercase for easy maintenance.
39+
# This maps them to the pretty descriptions to generate in header comments.
40+
LIBRARY_DESCRIPTIONS = {
41+
"stdc": "Standard C",
42+
"posix": "POSIX",
43+
"bsd": "BSD",
44+
"gnu": "GNU",
45+
"linux": "Linux",
46+
}
47+
48+
HEADER_TEMPLATE = """\
49+
//===-- {library} header <{header}> --===//
50+
//
51+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
52+
// See https://llvm.org/LICENSE.txt for license information.
53+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
54+
//
55+
//===---------------------------------------------------------------------===//
56+
57+
#ifndef {guard}
58+
#define {guard}
59+
60+
%%public_api()
61+
62+
#endif // {guard}
63+
"""
64+
3365

3466
class HeaderFile:
3567
def __init__(self, name):
@@ -40,6 +72,7 @@ def __init__(self, name):
4072
self.enumerations = []
4173
self.objects = []
4274
self.functions = []
75+
self.standards = []
4376

4477
def add_macro(self, macro):
4578
self.macros.append(macro)
@@ -63,6 +96,13 @@ def all_types(self):
6396
set(self.types),
6497
)
6598

99+
def all_standards(self):
100+
# FIXME: Only functions have the "standard" field, but all the entity
101+
# types should have one too.
102+
return set(self.standards).union(
103+
*(filter(None, (f.standards for f in self.functions)))
104+
)
105+
66106
def includes(self):
67107
return {
68108
PurePosixPath("llvm-libc-macros") / macro.header
@@ -75,14 +115,62 @@ def includes(self):
75115
for typ in self.all_types()
76116
}
77117

118+
def header_guard(self):
119+
return "LLVM_LIBC_" + "_".join(
120+
word.upper() for word in NONIDENTIFIER.split(self.name) if word
121+
)
122+
123+
def library_description(self):
124+
# If the header itself is in standard C, just call it that.
125+
if "stdc" in self.standards:
126+
return LIBRARY_DESCRIPTIONS["stdc"]
127+
# If the header itself is in POSIX, just call it that.
128+
if "posix" in self.standards:
129+
return LIBRARY_DESCRIPTIONS["posix"]
130+
# Otherwise, consider the standards for each symbol as well.
131+
standards = self.all_standards()
132+
# Otherwise, it's described by all those that apply, but ignoring
133+
# "stdc" and "posix" since this is not a "stdc" or "posix" header.
134+
return " / ".join(
135+
sorted(
136+
LIBRARY_DESCRIPTIONS[standard]
137+
for standard in standards
138+
if standard not in {"stdc", "posix"}
139+
)
140+
)
141+
142+
def template(self, dir, files_read):
143+
if self.template_file is not None:
144+
# There's a custom template file, so just read it in and record
145+
# that it was read as an input file.
146+
template_path = dir / self.template_file
147+
files_read.add(template_path)
148+
return template_path.read_text()
149+
150+
# Generate the default template.
151+
return HEADER_TEMPLATE.format(
152+
library=self.library_description(),
153+
header=self.name,
154+
guard=self.header_guard(),
155+
)
156+
78157
def public_api(self):
79158
# Python 3.12 has .relative_to(dir, walk_up=True) for this.
80159
path_prefix = PurePosixPath("../" * (len(PurePosixPath(self.name).parents) - 1))
81160

82161
def relpath(file):
83162
return path_prefix / file
84163

85-
content = [
164+
content = []
165+
166+
if self.template_file is None:
167+
# This always goes before all the other includes, which are sorted.
168+
# It's implicitly emitted here when using the default template so
169+
# it can get the right relative path. Custom template files should
170+
# all have it explicitly with their right particular relative path.
171+
content.append('#include "{file!s}"'.format(file=relpath(COMMON_HEADER)))
172+
173+
content += [
86174
f"#include {file}"
87175
for file in sorted(
88176
file if isinstance(file, str) else f'"{relpath(file)!s}"'

libc/utils/hdrgen/main.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,10 @@ def write_depfile():
6565

6666
header = load_yaml_file(yaml_file, HeaderFile, args.entry_point)
6767

68-
if not header.template_file:
69-
print(f"{yaml_file}: Missing header_template", sys.stderr)
70-
return 2
71-
7268
# The header_template path is relative to the containing YAML file.
73-
template_path = yaml_file.parent / header.template_file
69+
template = header.template(yaml_file.parent, files_read)
7470

75-
files_read.add(template_path)
76-
with open(template_path) as template:
77-
contents = fill_public_api(header.public_api(), template.read())
71+
contents = fill_public_api(header.public_api(), template)
7872

7973
write_depfile()
8074

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
1-
//===-- C standard library header subdir/test.h --------------------------===//
1+
//===-- BSD / GNU header <subdir/test.h> --===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
55
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66
//
7-
//===--------------------------------------------------------------------===//
7+
//===---------------------------------------------------------------------===//
88

99
#ifndef LLVM_LIBC_SUBDIR_TEST_H
1010
#define LLVM_LIBC_SUBDIR_TEST_H
1111

1212
#include "../__llvm-libc-common.h"
13-
1413
#include "../llvm-libc-types/type_a.h"
1514
#include "../llvm-libc-types/type_b.h"
1615

1716
__BEGIN_C_DECLS
1817

1918
type_a func(type_b) __NOEXCEPT;
2019

20+
void gnufunc(type_a) __NOEXCEPT;
21+
2122
__END_C_DECLS
2223

2324
#endif // LLVM_LIBC_SUBDIR_TEST_H

libc/utils/hdrgen/tests/input/subdir/test.h.def

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
header: subdir/test.h
2-
header_template: test.h.def
2+
standards:
3+
- bsd
34
functions:
45
- name: func
56
return_type: type_a
67
arguments:
78
- type: type_b
9+
- name: gnufunc
10+
return_type: void
11+
arguments:
12+
- type: type_a
813
standards:
9-
- stdc
14+
- gnu

libc/utils/hdrgen/yaml_to_classes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
3636
header_name = yaml_data.get("header")
3737
header = header_class(header_name)
3838
header.template_file = yaml_data.get("header_template")
39+
header.standards = yaml_data.get("standards", [])
3940

4041
for macro_data in yaml_data.get("macros", []):
4142
header.add_macro(

0 commit comments

Comments
 (0)