Skip to content

Commit 22079e3

Browse files
authored
[libc][hdrgen] Add extra_standards and license_text (llvm#165459)
This adds a few new features to hdrgen, all meant to facilitate using it with inputs and outputs that are outside the llvm-libc source tree. The new `extra_standards` field is a dictionary to augment the set of names that can be used in `standards` lists. The keys are the identifiers used in YAML ("stdc") and the values are the pretty names generated in the header comments ("Standard C"). This lets a libc project that's leveraging the llvm-libc sources along with its own code define new APIs outside the formal and de facto standards that llvm-libc draws its supported APIs from. The new `license_text` field is a list of lines of license text that replaces the standard LLVM license text used at the top of each generated header. This lets other projects use hdrgen with their own inputs to produce generated headers that are not tied to the LLVM project. Finally, for any function attributes that are not in a canonical list known to be provided by __llvm-libc-common.h, an include will be generated for "llvm-libc-macros/{attribute name}.h", expecting that file to define the "attribute" name as a macro. All this can be used immediately by builds that drive hdrgen and build libc code outside the LLVM CMake build. Future changes could add CMake plumbing to facilitate augmenting the LLVM CMake build of libc with outside sources via overlays and cache files.
1 parent 24c75a2 commit 22079e3

File tree

8 files changed

+106
-16
lines changed

8 files changed

+106
-16
lines changed

libc/utils/hdrgen/hdrgen/header.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@
3535

3636
COMMON_HEADER = PurePosixPath("__llvm-libc-common.h")
3737

38+
# These "attributes" are known macros defined in COMMON_HEADER.
39+
# Others are found in "llvm-libc-macros/{name}.h".
40+
COMMON_ATTRIBUTES = {
41+
"_Noreturn",
42+
"_Returns_twice",
43+
}
44+
3845
# All the canonical identifiers are in lowercase for easy maintenance.
3946
# This maps them to the pretty descriptions to generate in header comments.
4047
LIBRARY_DESCRIPTIONS = {
@@ -50,9 +57,7 @@
5057
HEADER_TEMPLATE = """\
5158
//===-- {library} header <{header}> --===//
5259
//
53-
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
54-
// See https://llvm.org/LICENSE.txt for license information.
55-
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60+
{license_lines}
5661
//
5762
//===---------------------------------------------------------------------===//
5863
@@ -64,6 +69,12 @@
6469
#endif // {guard}
6570
"""
6671

72+
LLVM_LICENSE_TEXT = [
73+
"Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.",
74+
"See https://llvm.org/LICENSE.txt for license information.",
75+
"SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception",
76+
]
77+
6778

6879
class HeaderFile:
6980
def __init__(self, name):
@@ -74,8 +85,10 @@ def __init__(self, name):
7485
self.enumerations = []
7586
self.objects = []
7687
self.functions = []
88+
self.extra_standards = {}
7789
self.standards = []
7890
self.merge_yaml_files = []
91+
self.license_text = []
7992

8093
def add_macro(self, macro):
8194
self.macros.append(macro)
@@ -98,6 +111,11 @@ def merge(self, other):
98111
self.enumerations = sorted(set(self.enumerations) | set(other.enumerations))
99112
self.objects = sorted(set(self.objects) | set(other.objects))
100113
self.functions = sorted(set(self.functions) | set(other.functions))
114+
self.extra_standards |= other.extra_standards
115+
if self.license_text:
116+
assert not other.license_text, "only one `license_text` allowed"
117+
else:
118+
self.license_text = other.license_text
101119

102120
def all_types(self):
103121
return reduce(
@@ -106,6 +124,13 @@ def all_types(self):
106124
set(self.types),
107125
)
108126

127+
def all_attributes(self):
128+
return reduce(
129+
lambda a, b: a | b,
130+
[set(f.attributes) for f in self.functions],
131+
set(),
132+
)
133+
109134
def all_standards(self):
110135
# FIXME: Only functions have the "standard" field, but all the entity
111136
# types should have one too.
@@ -114,41 +139,54 @@ def all_standards(self):
114139
)
115140

116141
def includes(self):
117-
return {
118-
PurePosixPath("llvm-libc-macros") / macro.header
119-
for macro in self.macros
120-
if macro.header is not None
121-
} | {
122-
COMPILER_HEADER_TYPES.get(
123-
typ.type_name, PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h"
124-
)
125-
for typ in self.all_types()
126-
}
142+
return (
143+
{
144+
PurePosixPath("llvm-libc-macros") / macro.header
145+
for macro in self.macros
146+
if macro.header is not None
147+
}
148+
| {
149+
COMPILER_HEADER_TYPES.get(
150+
typ.type_name,
151+
PurePosixPath("llvm-libc-types") / f"{typ.type_name}.h",
152+
)
153+
for typ in self.all_types()
154+
}
155+
| {
156+
PurePosixPath("llvm-libc-macros") / f"{attr}.h"
157+
for attr in self.all_attributes() - COMMON_ATTRIBUTES
158+
}
159+
)
127160

128161
def header_guard(self):
129162
return "_LLVM_LIBC_" + "_".join(
130163
word.upper() for word in NONIDENTIFIER.split(self.name) if word
131164
)
132165

133166
def library_description(self):
167+
descriptions = LIBRARY_DESCRIPTIONS | self.extra_standards
134168
# If the header itself is in standard C, just call it that.
135169
if "stdc" in self.standards:
136-
return LIBRARY_DESCRIPTIONS["stdc"]
170+
return descriptions["stdc"]
137171
# If the header itself is in POSIX, just call it that.
138172
if "posix" in self.standards:
139-
return LIBRARY_DESCRIPTIONS["posix"]
173+
return descriptions["posix"]
140174
# Otherwise, consider the standards for each symbol as well.
141175
standards = self.all_standards()
142176
# Otherwise, it's described by all those that apply, but ignoring
143177
# "stdc" and "posix" since this is not a "stdc" or "posix" header.
144178
return " / ".join(
145179
sorted(
146-
LIBRARY_DESCRIPTIONS[standard]
180+
descriptions[standard]
147181
for standard in standards
148182
if standard not in {"stdc", "posix"}
149183
)
150184
)
151185

186+
def license_lines(self):
187+
lines = self.license_text or LLVM_LICENSE_TEXT
188+
return "\n".join([f"// {line}" for line in lines])
189+
152190
def template(self, dir, files_read):
153191
if self.template_file is not None:
154192
# There's a custom template file, so just read it in and record
@@ -162,6 +200,7 @@ def template(self, dir, files_read):
162200
library=self.library_description(),
163201
header=self.name,
164202
guard=self.header_guard(),
203+
license_lines=self.license_lines(),
165204
)
166205

167206
def public_api(self):

libc/utils/hdrgen/hdrgen/yaml_to_classes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def yaml_to_classes(yaml_data, header_class, entry_points=None):
3737
header = header_class(header_name)
3838
header.template_file = yaml_data.get("header_template")
3939
header.standards = yaml_data.get("standards", [])
40+
header.extra_standards = yaml_data.get("extra_standards", {})
41+
header.license_text = yaml_data.get("license_text", [])
4042
header.merge_yaml_files = yaml_data.get("merge_yaml_files", [])
4143

4244
for macro_data in yaml_data.get("macros", []):
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Wile E. Coyote header <custom.h> --===//
2+
//
3+
// Caveat emptor.
4+
// I never studied law.
5+
//
6+
//===---------------------------------------------------------------------===//
7+
8+
#ifndef _LLVM_LIBC_CUSTOM_H
9+
#define _LLVM_LIBC_CUSTOM_H
10+
11+
#include "__llvm-libc-common.h"
12+
#include "llvm-libc-types/meep.h"
13+
#include "llvm-libc-types/road.h"
14+
15+
__BEGIN_C_DECLS
16+
17+
road runner(meep, meep) __NOEXCEPT;
18+
19+
__END_C_DECLS
20+
21+
#endif // _LLVM_LIBC_CUSTOM_H

libc/utils/hdrgen/tests/expected_output/test_header.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "__llvm-libc-common.h"
1313
#include "llvm-libc-macros/float16-macros.h"
1414

15+
#include "llvm-libc-macros/CONST_FUNC_A.h"
1516
#include "llvm-libc-macros/test_more-macros.h"
1617
#include "llvm-libc-macros/test_small-macros.h"
1718
#include "llvm-libc-types/float128.h"

libc/utils/hdrgen/tests/expected_output/test_small.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"standards": [],
55
"includes": [
66
"__llvm-libc-common.h",
7+
"llvm-libc-macros/CONST_FUNC_A.h",
78
"llvm-libc-macros/test_more-macros.h",
89
"llvm-libc-macros/test_small-macros.h",
910
"llvm-libc-types/float128.h",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
license_text:
2+
- Caveat emptor.
3+
- I never studied law.
4+
5+
extra_standards:
6+
acme: Wile E. Coyote
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
merge_yaml_files:
2+
- custom-common.yaml
3+
4+
header: custom.h
5+
standards:
6+
- acme
7+
8+
functions:
9+
- name: runner
10+
return_type: road
11+
arguments:
12+
- type: meep
13+
- type: meep

libc/utils/hdrgen/tests/test_integration.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ def test_generate_subdir_header(self):
5959
self.run_script(yaml_file, output_file)
6060
self.compare_files(output_file, expected_output_file)
6161

62+
def test_custom_license_and_standards(self):
63+
yaml_file = self.source_dir / "input" / "custom.yaml"
64+
expected_output_file = self.source_dir / "expected_output" / "custom.h"
65+
output_file = self.output_dir / "custom.h"
66+
self.run_script(yaml_file, output_file)
67+
self.compare_files(output_file, expected_output_file)
68+
6269
def test_generate_json(self):
6370
yaml_file = self.source_dir / "input/test_small.yaml"
6471
expected_output_file = self.source_dir / "expected_output/test_small.json"

0 commit comments

Comments
 (0)