Skip to content

Commit 829e2a5

Browse files
authored
[libc][hdrgen] Allow to treat hdrgen Python code as a Python module. (#128955)
Move the hdrgen code under a subdirectory to treat it as a Python module. This mimics the structure used by llvm/utils/lit and llvm/utils/mlgo-utils and simplifies integration of hdrgen to the build system which rely on Python modules. In addition to that, it clarifies which imports are coming from the hdrgen-specific helpers (e.g. "from type import ..." becomes "from hdrgen.type import ...". Leave the entrypoints (top-level main.py and yaml_to_classes.py) as-is: they can keep being referred by the CMake build system w/o any changes.
1 parent 524711c commit 829e2a5

File tree

14 files changed

+435
-405
lines changed

14 files changed

+435
-405
lines changed

libc/docs/dev/header_generation.rst

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ To add through the command line:
6363
examine.
6464

6565
If you want to sort the functions alphabetically you can check out
66-
libc/utils/hdrgen/yaml_functions_sorted.py.
66+
``libc/utils/hdrgen/hdrgen/yaml_functions_sorted.py``.
6767

6868

6969
Testing
@@ -90,7 +90,7 @@ Common Errors
9090

9191
.. code-block:: none
9292
93-
"/llvm-project/libc/utils/hdrgen/yaml_to_classes.py", line 67, in yaml_to_classes function_data["return_type"]
93+
"/llvm-project/libc/utils/hdrgen/hdrgen/yaml_to_classes.py", line 67, in yaml_to_classes function_data["return_type"]
9494
9595
If you receive this error or any error pertaining to
9696
``function_data[function_specific_component]`` while building the headers
@@ -107,9 +107,9 @@ Common Errors
107107
108108
CMake Error at:
109109
/llvm-project/libc/cmake/modules/LLVMLibCHeaderRules.cmake:86 (message):
110-
'add_gen_hdr2' rule requires GEN_HDR to be specified.
110+
'add_gen_hdr' rule requires GEN_HDR to be specified.
111111
Call Stack (most recent call first):
112-
/llvm-project/libc/include/CMakeLists.txt:22 (add_gen_header2)
112+
/llvm-project/libc/include/CMakeLists.txt:22 (add_gen_header)
113113
/llvm-project/libc/include/CMakeLists.txt:62 (add_header_macro)
114114
115115
If you receive this error, there is a missing YAML file, h_def file, or
@@ -119,7 +119,6 @@ Common Errors
119119

120120
| ``[header_name]``
121121
| ``[../libc/include/[yaml_file.yaml]``
122-
| ``[header_name.h.def]``
123122
| ``[header_name.h]``
124123
| ``DEPENDS``
125124
| ``{Necessary Depend Files}``
@@ -148,13 +147,13 @@ Common Errors
148147

149148
.. code-block:: none
150149
151-
File "/llvm-project/libc/utils/hdrgen/header.py", line 60, in __str__ for
150+
File "/llvm-project/libc/utils/hdrgen/hdrgen/header.py", line 60, in __str__ for
152151
function in self.functions: AttributeError: 'HeaderFile' object has no
153152
attribute 'functions'
154153
155154
When running ``ninja libc`` in the build directory to generate headers you
156155
may receive the error above. Essentially this means that in
157-
``libc/utils/hdrgen/header.py`` there is a missing attribute named functions.
156+
``libc/utils/hdrgen/hdrgen/header.py`` there is a missing attribute named functions.
158157
Make sure all function components are defined within this file and there are
159158
no missing functions to add these components.
160159

libc/utils/hdrgen/hdrgen/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
2+
# See https://llvm.org/LICENSE.txt for license information.
3+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

libc/utils/hdrgen/function.py renamed to libc/utils/hdrgen/hdrgen/function.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import re
1010
from functools import total_ordering
11-
from type import Type
11+
from hdrgen.type import Type
1212

1313

1414
# These are the keywords that appear in C type syntax but are not part of the

libc/utils/hdrgen/gpu_headers.py renamed to libc/utils/hdrgen/hdrgen/gpu_headers.py

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

9-
from header import HeaderFile
9+
from hdrgen.header import HeaderFile
1010

1111

1212
class GpuHeaderFile(HeaderFile):
File renamed without changes.
File renamed without changes.

libc/utils/hdrgen/hdrgen/main.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env python3
2+
#
3+
# ===- Generate headers for libc functions ------------------*- python -*--==#
4+
#
5+
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
6+
# See https://llvm.org/LICENSE.txt for license information.
7+
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
8+
#
9+
# ==------------------------------------------------------------------------==#
10+
11+
import argparse
12+
import json
13+
import sys
14+
from pathlib import Path
15+
16+
from hdrgen.header import HeaderFile
17+
from hdrgen.yaml_to_classes import load_yaml_file, fill_public_api
18+
19+
20+
def main():
21+
parser = argparse.ArgumentParser(description="Generate header files from YAML")
22+
parser.add_argument(
23+
"yaml_file",
24+
help="Path to the YAML file containing header specification",
25+
metavar="FILE",
26+
type=Path,
27+
nargs="+",
28+
)
29+
parser.add_argument(
30+
"-o",
31+
"--output",
32+
help="Path to write generated header file",
33+
type=Path,
34+
required=True,
35+
)
36+
parser.add_argument(
37+
"--json",
38+
help="Write JSON instead of a header, can use multiple YAML files",
39+
action="store_true",
40+
)
41+
parser.add_argument(
42+
"--depfile",
43+
help="Path to write a depfile",
44+
type=Path,
45+
)
46+
parser.add_argument(
47+
"--write-if-changed",
48+
help="Write the output file only if its contents have changed",
49+
action="store_true",
50+
default=False,
51+
)
52+
parser.add_argument(
53+
"-e",
54+
"--entry-point",
55+
help="Entry point to include; may be given many times",
56+
metavar="SYMBOL",
57+
action="append",
58+
)
59+
args = parser.parse_args()
60+
61+
if not args.json and len(args.yaml_file) != 1:
62+
print("Only one YAML file at a time without --json", file=sys.stderr)
63+
parser.print_usage(sys.stderr)
64+
return 2
65+
66+
files_read = set()
67+
68+
def write_depfile():
69+
if not args.depfile:
70+
return
71+
deps = " ".join(str(f) for f in sorted(files_read))
72+
args.depfile.parent.mkdir(parents=True, exist_ok=True)
73+
with open(args.depfile, "w") as depfile:
74+
depfile.write(f"{args.output}: {deps}\n")
75+
76+
def load_yaml(path):
77+
files_read.add(path)
78+
return load_yaml_file(path, HeaderFile, args.entry_point)
79+
80+
def load_header(yaml_file):
81+
merge_from_files = dict()
82+
83+
def merge_from(paths):
84+
for path in paths:
85+
# Load each file exactly once, in case of redundant merges.
86+
if path in merge_from_files:
87+
continue
88+
header = load_yaml(path)
89+
merge_from_files[path] = header
90+
merge_from(path.parent / f for f in header.merge_yaml_files)
91+
92+
# Load the main file first.
93+
header = load_yaml(yaml_file)
94+
95+
# Now load all the merge_yaml_files, and transitive merge_yaml_files.
96+
merge_from(yaml_file.parent / f for f in header.merge_yaml_files)
97+
98+
# Merge in all those files' contents.
99+
for merge_from_path, merge_from_header in merge_from_files.items():
100+
if merge_from_header.name is not None:
101+
print(
102+
f"{merge_from_path!s}: Merge file cannot have header field",
103+
file=sys.stderr,
104+
)
105+
return 2
106+
header.merge(merge_from_header)
107+
108+
return header
109+
110+
if args.json:
111+
contents = json.dumps(
112+
[load_header(file).json_data() for file in args.yaml_file],
113+
indent=2,
114+
)
115+
else:
116+
[yaml_file] = args.yaml_file
117+
header = load_header(yaml_file)
118+
# The header_template path is relative to the containing YAML file.
119+
template = header.template(yaml_file.parent, files_read)
120+
contents = fill_public_api(header.public_api(), template)
121+
122+
write_depfile()
123+
124+
if (
125+
not args.write_if_changed
126+
or not args.output.exists()
127+
or args.output.read_text() != contents
128+
):
129+
args.output.parent.mkdir(parents=True, exist_ok=True)
130+
args.output.write_text(contents)
131+
132+
133+
if __name__ == "__main__":
134+
sys.exit(main())
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)