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