[libc++][Android] Allow testing libc++ with clang-r536225 (#116149)
[llvm-project.git] / libc / newhdrgen / yaml_to_classes.py
blob0e8ca2d8a82b0cb5e6d583f6bd931d36dd809f9e
1 #!/usr/bin/env python3
3 # ===- Generate headers for libc functions -------------------*- python -*--==#
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
9 # ==-------------------------------------------------------------------------==#
11 import yaml
12 import argparse
13 from pathlib import Path
14 from header import HeaderFile
15 from gpu_headers import GpuHeaderFile as GpuHeader
16 from class_implementation.classes.macro import Macro
17 from class_implementation.classes.type import Type
18 from class_implementation.classes.function import Function
19 from class_implementation.classes.enumeration import Enumeration
20 from class_implementation.classes.object import Object
23 def yaml_to_classes(yaml_data, header_class, entry_points=None):
24 """
25 Convert YAML data to header classes.
27 Args:
28 yaml_data: The YAML data containing header specifications.
29 header_class: The class to use for creating the header.
30 entry_points: A list of specific function names to include in the header.
32 Returns:
33 HeaderFile: An instance of HeaderFile populated with the data.
34 """
35 header_name = yaml_data.get("header")
36 header = header_class(header_name)
38 for macro_data in yaml_data.get("macros", []):
39 header.add_macro(Macro(macro_data["macro_name"], macro_data["macro_value"]))
41 types = yaml_data.get("types", [])
42 sorted_types = sorted(types, key=lambda x: x["type_name"])
43 for type_data in sorted_types:
44 header.add_type(Type(type_data["type_name"]))
46 for enum_data in yaml_data.get("enums", []):
47 header.add_enumeration(
48 Enumeration(enum_data["name"], enum_data.get("value", None))
51 functions = yaml_data.get("functions", [])
52 if entry_points:
53 entry_points_set = set(entry_points)
54 functions = [f for f in functions if f["name"] in entry_points_set]
55 sorted_functions = sorted(functions, key=lambda x: x["name"])
56 guards = []
57 guarded_function_dict = {}
58 for function_data in sorted_functions:
59 guard = function_data.get("guard", None)
60 if guard is None:
61 arguments = [arg["type"] for arg in function_data["arguments"]]
62 attributes = function_data.get("attributes", None)
63 standards = function_data.get("standards", None)
64 header.add_function(
65 Function(
66 function_data["return_type"],
67 function_data["name"],
68 arguments,
69 standards,
70 guard,
71 attributes,
74 else:
75 if guard not in guards:
76 guards.append(guard)
77 guarded_function_dict[guard] = []
78 guarded_function_dict[guard].append(function_data)
79 else:
80 guarded_function_dict[guard].append(function_data)
81 sorted_guards = sorted(guards)
82 for guard in sorted_guards:
83 for function_data in guarded_function_dict[guard]:
84 arguments = [arg["type"] for arg in function_data["arguments"]]
85 attributes = function_data.get("attributes", None)
86 standards = function_data.get("standards", None)
87 header.add_function(
88 Function(
89 function_data["return_type"],
90 function_data["name"],
91 arguments,
92 standards,
93 guard,
94 attributes,
98 objects = yaml_data.get("objects", [])
99 sorted_objects = sorted(objects, key=lambda x: x["object_name"])
100 for object_data in sorted_objects:
101 header.add_object(
102 Object(object_data["object_name"], object_data["object_type"])
105 return header
108 def load_yaml_file(yaml_file, header_class, entry_points):
110 Load YAML file and convert it to header classes.
112 Args:
113 yaml_file: Path to the YAML file.
114 header_class: The class to use for creating the header (HeaderFile or GpuHeader).
115 entry_points: A list of specific function names to include in the header.
117 Returns:
118 HeaderFile: An instance of HeaderFile populated with the data.
120 with open(yaml_file, "r") as f:
121 yaml_data = yaml.safe_load(f)
122 return yaml_to_classes(yaml_data, header_class, entry_points)
125 def fill_public_api(header_str, h_def_content):
127 Replace the %%public_api() placeholder in the .h.def content with the generated header content.
129 Args:
130 header_str: The generated header string.
131 h_def_content: The content of the .h.def file.
133 Returns:
134 The final header content with the public API filled in.
136 header_str = header_str.strip()
137 return h_def_content.replace("%%public_api()", header_str, 1)
140 def parse_function_details(details):
142 Parse function details from a list of strings and return a Function object.
144 Args:
145 details: A list containing function details
147 Returns:
148 Function: An instance of Function initialized with the details.
150 return_type, name, arguments, standards, guard, attributes = details
151 standards = standards.split(",") if standards != "null" else []
152 arguments = [arg.strip() for arg in arguments.split(",")]
153 attributes = attributes.split(",") if attributes != "null" else []
155 return Function(
156 return_type=return_type,
157 name=name,
158 arguments=arguments,
159 standards=standards,
160 guard=guard if guard != "null" else None,
161 attributes=attributes if attributes else [],
165 def add_function_to_yaml(yaml_file, function_details):
167 Add a function to the YAML file.
169 Args:
170 yaml_file: The path to the YAML file.
171 function_details: A list containing function details (return_type, name, arguments, standards, guard, attributes).
173 new_function = parse_function_details(function_details)
175 with open(yaml_file, "r") as f:
176 yaml_data = yaml.safe_load(f)
177 if "functions" not in yaml_data:
178 yaml_data["functions"] = []
180 function_dict = {
181 "name": new_function.name,
182 "standards": new_function.standards,
183 "return_type": new_function.return_type,
184 "arguments": [{"type": arg} for arg in new_function.arguments],
187 if new_function.guard:
188 function_dict["guard"] = new_function.guard
190 if new_function.attributes:
191 function_dict["attributes"] = new_function.attributes
193 insert_index = 0
194 for i, func in enumerate(yaml_data["functions"]):
195 if func["name"] > new_function.name:
196 insert_index = i
197 break
198 else:
199 insert_index = len(yaml_data["functions"])
201 yaml_data["functions"].insert(insert_index, function_dict)
203 class IndentYamlListDumper(yaml.Dumper):
204 def increase_indent(self, flow=False, indentless=False):
205 return super(IndentYamlListDumper, self).increase_indent(flow, False)
207 with open(yaml_file, "w") as f:
208 yaml.dump(
209 yaml_data,
211 Dumper=IndentYamlListDumper,
212 default_flow_style=False,
213 sort_keys=False,
216 print(f"Added function {new_function.name} to {yaml_file}")
219 def main():
220 parser = argparse.ArgumentParser(description="Generate header files from YAML")
221 parser.add_argument(
222 "yaml_file", help="Path to the YAML file containing header specification"
224 parser.add_argument(
225 "--output_dir",
226 help="Directory to output the generated header file",
228 parser.add_argument(
229 "--h_def_file",
230 help="Path to the .h.def template file (required if not using --export_decls)",
232 parser.add_argument(
233 "--add_function",
234 nargs=6,
235 metavar=(
236 "RETURN_TYPE",
237 "NAME",
238 "ARGUMENTS",
239 "STANDARDS",
240 "GUARD",
241 "ATTRIBUTES",
243 help="Add a function to the YAML file",
245 parser.add_argument(
246 "--e", action="append", help="Entry point to include", dest="entry_points"
248 parser.add_argument(
249 "--export-decls",
250 action="store_true",
251 help="Flag to use GpuHeader for exporting declarations",
253 args = parser.parse_args()
255 if args.add_function:
256 add_function_to_yaml(args.yaml_file, args.add_function)
258 header_class = GpuHeader if args.export_decls else HeaderFile
259 header = load_yaml_file(args.yaml_file, header_class, args.entry_points)
261 header_str = str(header)
263 if args.output_dir:
264 output_file_path = Path(args.output_dir)
265 if output_file_path.is_dir():
266 output_file_path /= f"{Path(args.yaml_file).stem}.h"
267 else:
268 output_file_path = Path(f"{Path(args.yaml_file).stem}.h")
270 if not args.export_decls and args.h_def_file:
271 with open(args.h_def_file, "r") as f:
272 h_def_content = f.read()
273 final_header_content = fill_public_api(header_str, h_def_content)
274 with open(output_file_path, "w") as f:
275 f.write(final_header_content)
276 else:
277 with open(output_file_path, "w") as f:
278 f.write(header_str)
281 if __name__ == "__main__":
282 main()