19abcca5eSRoland McGrath#!/usr/bin/env python3 29abcca5eSRoland McGrath# 39abcca5eSRoland McGrath# ===- Generate headers for libc functions -------------------*- python -*--==# 49abcca5eSRoland McGrath# 59abcca5eSRoland McGrath# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 69abcca5eSRoland McGrath# See https://llvm.org/LICENSE.txt for license information. 79abcca5eSRoland McGrath# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 89abcca5eSRoland McGrath# 99abcca5eSRoland McGrath# ==-------------------------------------------------------------------------==# 109abcca5eSRoland McGrath 119abcca5eSRoland McGrathimport yaml 129abcca5eSRoland McGrathimport argparse 139abcca5eSRoland McGrathfrom pathlib import Path 1407e13b76SRoland McGrath 1507e13b76SRoland McGrathfrom enumeration import Enumeration 1607e13b76SRoland McGrathfrom function import Function 179abcca5eSRoland McGrathfrom gpu_headers import GpuHeaderFile as GpuHeader 1807e13b76SRoland McGrathfrom header import HeaderFile 1907e13b76SRoland McGrathfrom macro import Macro 2007e13b76SRoland McGrathfrom object import Object 2107e13b76SRoland McGrathfrom type import Type 229abcca5eSRoland McGrath 239abcca5eSRoland McGrath 249abcca5eSRoland McGrathdef yaml_to_classes(yaml_data, header_class, entry_points=None): 259abcca5eSRoland McGrath """ 269abcca5eSRoland McGrath Convert YAML data to header classes. 279abcca5eSRoland McGrath 289abcca5eSRoland McGrath Args: 299abcca5eSRoland McGrath yaml_data: The YAML data containing header specifications. 309abcca5eSRoland McGrath header_class: The class to use for creating the header. 319abcca5eSRoland McGrath entry_points: A list of specific function names to include in the header. 329abcca5eSRoland McGrath 339abcca5eSRoland McGrath Returns: 349abcca5eSRoland McGrath HeaderFile: An instance of HeaderFile populated with the data. 359abcca5eSRoland McGrath """ 369abcca5eSRoland McGrath header_name = yaml_data.get("header") 379abcca5eSRoland McGrath header = header_class(header_name) 38*6ad0dcf6SRoland McGrath header.template_file = yaml_data.get("header_template") 399abcca5eSRoland McGrath 409abcca5eSRoland McGrath for macro_data in yaml_data.get("macros", []): 419abcca5eSRoland McGrath header.add_macro(Macro(macro_data["macro_name"], macro_data["macro_value"])) 429abcca5eSRoland McGrath 439abcca5eSRoland McGrath types = yaml_data.get("types", []) 449abcca5eSRoland McGrath sorted_types = sorted(types, key=lambda x: x["type_name"]) 459abcca5eSRoland McGrath for type_data in sorted_types: 469abcca5eSRoland McGrath header.add_type(Type(type_data["type_name"])) 479abcca5eSRoland McGrath 489abcca5eSRoland McGrath for enum_data in yaml_data.get("enums", []): 499abcca5eSRoland McGrath header.add_enumeration( 509abcca5eSRoland McGrath Enumeration(enum_data["name"], enum_data.get("value", None)) 519abcca5eSRoland McGrath ) 529abcca5eSRoland McGrath 539abcca5eSRoland McGrath functions = yaml_data.get("functions", []) 549abcca5eSRoland McGrath if entry_points: 559abcca5eSRoland McGrath entry_points_set = set(entry_points) 569abcca5eSRoland McGrath functions = [f for f in functions if f["name"] in entry_points_set] 579abcca5eSRoland McGrath sorted_functions = sorted(functions, key=lambda x: x["name"]) 589abcca5eSRoland McGrath guards = [] 599abcca5eSRoland McGrath guarded_function_dict = {} 609abcca5eSRoland McGrath for function_data in sorted_functions: 619abcca5eSRoland McGrath guard = function_data.get("guard", None) 629abcca5eSRoland McGrath if guard is None: 639abcca5eSRoland McGrath arguments = [arg["type"] for arg in function_data["arguments"]] 649abcca5eSRoland McGrath attributes = function_data.get("attributes", None) 659abcca5eSRoland McGrath standards = function_data.get("standards", None) 669abcca5eSRoland McGrath header.add_function( 679abcca5eSRoland McGrath Function( 689abcca5eSRoland McGrath function_data["return_type"], 699abcca5eSRoland McGrath function_data["name"], 709abcca5eSRoland McGrath arguments, 719abcca5eSRoland McGrath standards, 729abcca5eSRoland McGrath guard, 739abcca5eSRoland McGrath attributes, 749abcca5eSRoland McGrath ) 759abcca5eSRoland McGrath ) 769abcca5eSRoland McGrath else: 779abcca5eSRoland McGrath if guard not in guards: 789abcca5eSRoland McGrath guards.append(guard) 799abcca5eSRoland McGrath guarded_function_dict[guard] = [] 809abcca5eSRoland McGrath guarded_function_dict[guard].append(function_data) 819abcca5eSRoland McGrath else: 829abcca5eSRoland McGrath guarded_function_dict[guard].append(function_data) 839abcca5eSRoland McGrath sorted_guards = sorted(guards) 849abcca5eSRoland McGrath for guard in sorted_guards: 859abcca5eSRoland McGrath for function_data in guarded_function_dict[guard]: 869abcca5eSRoland McGrath arguments = [arg["type"] for arg in function_data["arguments"]] 879abcca5eSRoland McGrath attributes = function_data.get("attributes", None) 889abcca5eSRoland McGrath standards = function_data.get("standards", None) 899abcca5eSRoland McGrath header.add_function( 909abcca5eSRoland McGrath Function( 919abcca5eSRoland McGrath function_data["return_type"], 929abcca5eSRoland McGrath function_data["name"], 939abcca5eSRoland McGrath arguments, 949abcca5eSRoland McGrath standards, 959abcca5eSRoland McGrath guard, 969abcca5eSRoland McGrath attributes, 979abcca5eSRoland McGrath ) 989abcca5eSRoland McGrath ) 999abcca5eSRoland McGrath 1009abcca5eSRoland McGrath objects = yaml_data.get("objects", []) 1019abcca5eSRoland McGrath sorted_objects = sorted(objects, key=lambda x: x["object_name"]) 1029abcca5eSRoland McGrath for object_data in sorted_objects: 1039abcca5eSRoland McGrath header.add_object( 1049abcca5eSRoland McGrath Object(object_data["object_name"], object_data["object_type"]) 1059abcca5eSRoland McGrath ) 1069abcca5eSRoland McGrath 1079abcca5eSRoland McGrath return header 1089abcca5eSRoland McGrath 1099abcca5eSRoland McGrath 1109abcca5eSRoland McGrathdef load_yaml_file(yaml_file, header_class, entry_points): 1119abcca5eSRoland McGrath """ 1129abcca5eSRoland McGrath Load YAML file and convert it to header classes. 1139abcca5eSRoland McGrath 1149abcca5eSRoland McGrath Args: 1159abcca5eSRoland McGrath yaml_file: Path to the YAML file. 1169abcca5eSRoland McGrath header_class: The class to use for creating the header (HeaderFile or GpuHeader). 1179abcca5eSRoland McGrath entry_points: A list of specific function names to include in the header. 1189abcca5eSRoland McGrath 1199abcca5eSRoland McGrath Returns: 1209abcca5eSRoland McGrath HeaderFile: An instance of HeaderFile populated with the data. 1219abcca5eSRoland McGrath """ 1229abcca5eSRoland McGrath with open(yaml_file, "r") as f: 1239abcca5eSRoland McGrath yaml_data = yaml.safe_load(f) 1249abcca5eSRoland McGrath return yaml_to_classes(yaml_data, header_class, entry_points) 1259abcca5eSRoland McGrath 1269abcca5eSRoland McGrath 1279abcca5eSRoland McGrathdef fill_public_api(header_str, h_def_content): 1289abcca5eSRoland McGrath """ 1299abcca5eSRoland McGrath Replace the %%public_api() placeholder in the .h.def content with the generated header content. 1309abcca5eSRoland McGrath 1319abcca5eSRoland McGrath Args: 1329abcca5eSRoland McGrath header_str: The generated header string. 1339abcca5eSRoland McGrath h_def_content: The content of the .h.def file. 1349abcca5eSRoland McGrath 1359abcca5eSRoland McGrath Returns: 1369abcca5eSRoland McGrath The final header content with the public API filled in. 1379abcca5eSRoland McGrath """ 1389abcca5eSRoland McGrath header_str = header_str.strip() 1399abcca5eSRoland McGrath return h_def_content.replace("%%public_api()", header_str, 1) 1409abcca5eSRoland McGrath 1419abcca5eSRoland McGrath 1429abcca5eSRoland McGrathdef parse_function_details(details): 1439abcca5eSRoland McGrath """ 1449abcca5eSRoland McGrath Parse function details from a list of strings and return a Function object. 1459abcca5eSRoland McGrath 1469abcca5eSRoland McGrath Args: 1479abcca5eSRoland McGrath details: A list containing function details 1489abcca5eSRoland McGrath 1499abcca5eSRoland McGrath Returns: 1509abcca5eSRoland McGrath Function: An instance of Function initialized with the details. 1519abcca5eSRoland McGrath """ 1529abcca5eSRoland McGrath return_type, name, arguments, standards, guard, attributes = details 1539abcca5eSRoland McGrath standards = standards.split(",") if standards != "null" else [] 1549abcca5eSRoland McGrath arguments = [arg.strip() for arg in arguments.split(",")] 1559abcca5eSRoland McGrath attributes = attributes.split(",") if attributes != "null" else [] 1569abcca5eSRoland McGrath 1579abcca5eSRoland McGrath return Function( 1589abcca5eSRoland McGrath return_type=return_type, 1599abcca5eSRoland McGrath name=name, 1609abcca5eSRoland McGrath arguments=arguments, 1619abcca5eSRoland McGrath standards=standards, 1629abcca5eSRoland McGrath guard=guard if guard != "null" else None, 1639abcca5eSRoland McGrath attributes=attributes if attributes else [], 1649abcca5eSRoland McGrath ) 1659abcca5eSRoland McGrath 1669abcca5eSRoland McGrath 1679abcca5eSRoland McGrathdef add_function_to_yaml(yaml_file, function_details): 1689abcca5eSRoland McGrath """ 1699abcca5eSRoland McGrath Add a function to the YAML file. 1709abcca5eSRoland McGrath 1719abcca5eSRoland McGrath Args: 1729abcca5eSRoland McGrath yaml_file: The path to the YAML file. 1739abcca5eSRoland McGrath function_details: A list containing function details (return_type, name, arguments, standards, guard, attributes). 1749abcca5eSRoland McGrath """ 1759abcca5eSRoland McGrath new_function = parse_function_details(function_details) 1769abcca5eSRoland McGrath 1779abcca5eSRoland McGrath with open(yaml_file, "r") as f: 1789abcca5eSRoland McGrath yaml_data = yaml.safe_load(f) 1799abcca5eSRoland McGrath if "functions" not in yaml_data: 1809abcca5eSRoland McGrath yaml_data["functions"] = [] 1819abcca5eSRoland McGrath 1829abcca5eSRoland McGrath function_dict = { 1839abcca5eSRoland McGrath "name": new_function.name, 1849abcca5eSRoland McGrath "standards": new_function.standards, 1859abcca5eSRoland McGrath "return_type": new_function.return_type, 1869abcca5eSRoland McGrath "arguments": [{"type": arg} for arg in new_function.arguments], 1879abcca5eSRoland McGrath } 1889abcca5eSRoland McGrath 1899abcca5eSRoland McGrath if new_function.guard: 1909abcca5eSRoland McGrath function_dict["guard"] = new_function.guard 1919abcca5eSRoland McGrath 1929abcca5eSRoland McGrath if new_function.attributes: 1939abcca5eSRoland McGrath function_dict["attributes"] = new_function.attributes 1949abcca5eSRoland McGrath 1959abcca5eSRoland McGrath insert_index = 0 1969abcca5eSRoland McGrath for i, func in enumerate(yaml_data["functions"]): 1979abcca5eSRoland McGrath if func["name"] > new_function.name: 1989abcca5eSRoland McGrath insert_index = i 1999abcca5eSRoland McGrath break 2009abcca5eSRoland McGrath else: 2019abcca5eSRoland McGrath insert_index = len(yaml_data["functions"]) 2029abcca5eSRoland McGrath 2039abcca5eSRoland McGrath yaml_data["functions"].insert(insert_index, function_dict) 2049abcca5eSRoland McGrath 2059abcca5eSRoland McGrath class IndentYamlListDumper(yaml.Dumper): 2069abcca5eSRoland McGrath def increase_indent(self, flow=False, indentless=False): 2079abcca5eSRoland McGrath return super(IndentYamlListDumper, self).increase_indent(flow, False) 2089abcca5eSRoland McGrath 2099abcca5eSRoland McGrath with open(yaml_file, "w") as f: 2109abcca5eSRoland McGrath yaml.dump( 2119abcca5eSRoland McGrath yaml_data, 2129abcca5eSRoland McGrath f, 2139abcca5eSRoland McGrath Dumper=IndentYamlListDumper, 2149abcca5eSRoland McGrath default_flow_style=False, 2159abcca5eSRoland McGrath sort_keys=False, 2169abcca5eSRoland McGrath ) 2179abcca5eSRoland McGrath 2189abcca5eSRoland McGrath print(f"Added function {new_function.name} to {yaml_file}") 2199abcca5eSRoland McGrath 2209abcca5eSRoland McGrath 2219abcca5eSRoland McGrathdef main(): 2229abcca5eSRoland McGrath parser = argparse.ArgumentParser(description="Generate header files from YAML") 2239abcca5eSRoland McGrath parser.add_argument( 2249abcca5eSRoland McGrath "yaml_file", help="Path to the YAML file containing header specification" 2259abcca5eSRoland McGrath ) 2269abcca5eSRoland McGrath parser.add_argument( 2279abcca5eSRoland McGrath "--output_dir", 2289abcca5eSRoland McGrath help="Directory to output the generated header file", 2299abcca5eSRoland McGrath ) 2309abcca5eSRoland McGrath parser.add_argument( 2319abcca5eSRoland McGrath "--add_function", 2329abcca5eSRoland McGrath nargs=6, 2339abcca5eSRoland McGrath metavar=( 2349abcca5eSRoland McGrath "RETURN_TYPE", 2359abcca5eSRoland McGrath "NAME", 2369abcca5eSRoland McGrath "ARGUMENTS", 2379abcca5eSRoland McGrath "STANDARDS", 2389abcca5eSRoland McGrath "GUARD", 2399abcca5eSRoland McGrath "ATTRIBUTES", 2409abcca5eSRoland McGrath ), 2419abcca5eSRoland McGrath help="Add a function to the YAML file", 2429abcca5eSRoland McGrath ) 2439abcca5eSRoland McGrath parser.add_argument( 244*6ad0dcf6SRoland McGrath "--entry-point", 245*6ad0dcf6SRoland McGrath action="append", 246*6ad0dcf6SRoland McGrath help="Entry point to include", 247*6ad0dcf6SRoland McGrath dest="entry_points", 2489abcca5eSRoland McGrath ) 2499abcca5eSRoland McGrath parser.add_argument( 2509abcca5eSRoland McGrath "--export-decls", 2519abcca5eSRoland McGrath action="store_true", 2529abcca5eSRoland McGrath help="Flag to use GpuHeader for exporting declarations", 2539abcca5eSRoland McGrath ) 2549abcca5eSRoland McGrath args = parser.parse_args() 2559abcca5eSRoland McGrath 2569abcca5eSRoland McGrath if args.add_function: 2579abcca5eSRoland McGrath add_function_to_yaml(args.yaml_file, args.add_function) 2589abcca5eSRoland McGrath 2599abcca5eSRoland McGrath header_class = GpuHeader if args.export_decls else HeaderFile 2609abcca5eSRoland McGrath header = load_yaml_file(args.yaml_file, header_class, args.entry_points) 2619abcca5eSRoland McGrath 2629abcca5eSRoland McGrath header_str = str(header) 2639abcca5eSRoland McGrath 2649abcca5eSRoland McGrath if args.output_dir: 2659abcca5eSRoland McGrath output_file_path = Path(args.output_dir) 2669abcca5eSRoland McGrath if output_file_path.is_dir(): 2679abcca5eSRoland McGrath output_file_path /= f"{Path(args.yaml_file).stem}.h" 2689abcca5eSRoland McGrath else: 2699abcca5eSRoland McGrath output_file_path = Path(f"{Path(args.yaml_file).stem}.h") 2709abcca5eSRoland McGrath 271*6ad0dcf6SRoland McGrath if args.export_decls: 2729abcca5eSRoland McGrath with open(output_file_path, "w") as f: 2739abcca5eSRoland McGrath f.write(header_str) 2749abcca5eSRoland McGrath 2759abcca5eSRoland McGrath 2769abcca5eSRoland McGrathif __name__ == "__main__": 2779abcca5eSRoland McGrath main() 278