| #!/usr/bin/env python3 |
| # |
| # ===- Generate headers for libc functions -------------------*- python -*--==# |
| # |
| # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| # See https://llvm.org/LICENSE.txt for license information. |
| # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| # |
| # ==-------------------------------------------------------------------------==# |
| |
| import yaml |
| import argparse |
| from pathlib import Path |
| from header import HeaderFile |
| from gpu_headers import GpuHeaderFile as GpuHeader |
| from class_implementation.classes.macro import Macro |
| from class_implementation.classes.type import Type |
| from class_implementation.classes.function import Function |
| from class_implementation.classes.enumeration import Enumeration |
| from class_implementation.classes.object import Object |
| |
| |
| def yaml_to_classes(yaml_data, header_class, entry_points=None): |
| """ |
| Convert YAML data to header classes. |
| |
| Args: |
| yaml_data: The YAML data containing header specifications. |
| header_class: The class to use for creating the header. |
| entry_points: A list of specific function names to include in the header. |
| |
| Returns: |
| HeaderFile: An instance of HeaderFile populated with the data. |
| """ |
| header_name = yaml_data.get("header") |
| header = header_class(header_name) |
| |
| for macro_data in yaml_data.get("macros", []): |
| header.add_macro(Macro(macro_data["macro_name"], macro_data["macro_value"])) |
| |
| types = yaml_data.get("types", []) |
| sorted_types = sorted(types, key=lambda x: x["type_name"]) |
| for type_data in sorted_types: |
| header.add_type(Type(type_data["type_name"])) |
| |
| for enum_data in yaml_data.get("enums", []): |
| header.add_enumeration( |
| Enumeration(enum_data["name"], enum_data.get("value", None)) |
| ) |
| |
| functions = yaml_data.get("functions", []) |
| if entry_points: |
| entry_points_set = set(entry_points) |
| functions = [f for f in functions if f["name"] in entry_points_set] |
| sorted_functions = sorted(functions, key=lambda x: x["name"]) |
| guards = [] |
| guarded_function_dict = {} |
| for function_data in sorted_functions: |
| guard = function_data.get("guard", None) |
| if guard is None: |
| arguments = [arg["type"] for arg in function_data["arguments"]] |
| attributes = function_data.get("attributes", None) |
| standards = function_data.get("standards", None) |
| header.add_function( |
| Function( |
| function_data["return_type"], |
| function_data["name"], |
| arguments, |
| standards, |
| guard, |
| attributes, |
| ) |
| ) |
| else: |
| if guard not in guards: |
| guards.append(guard) |
| guarded_function_dict[guard] = [] |
| guarded_function_dict[guard].append(function_data) |
| else: |
| guarded_function_dict[guard].append(function_data) |
| sorted_guards = sorted(guards) |
| for guard in sorted_guards: |
| for function_data in guarded_function_dict[guard]: |
| arguments = [arg["type"] for arg in function_data["arguments"]] |
| attributes = function_data.get("attributes", None) |
| standards = function_data.get("standards", None) |
| header.add_function( |
| Function( |
| function_data["return_type"], |
| function_data["name"], |
| arguments, |
| standards, |
| guard, |
| attributes, |
| ) |
| ) |
| |
| objects = yaml_data.get("objects", []) |
| sorted_objects = sorted(objects, key=lambda x: x["object_name"]) |
| for object_data in sorted_objects: |
| header.add_object( |
| Object(object_data["object_name"], object_data["object_type"]) |
| ) |
| |
| return header |
| |
| |
| def load_yaml_file(yaml_file, header_class, entry_points): |
| """ |
| Load YAML file and convert it to header classes. |
| |
| Args: |
| yaml_file: Path to the YAML file. |
| header_class: The class to use for creating the header (HeaderFile or GpuHeader). |
| entry_points: A list of specific function names to include in the header. |
| |
| Returns: |
| HeaderFile: An instance of HeaderFile populated with the data. |
| """ |
| with open(yaml_file, "r") as f: |
| yaml_data = yaml.safe_load(f) |
| return yaml_to_classes(yaml_data, header_class, entry_points) |
| |
| |
| def fill_public_api(header_str, h_def_content): |
| """ |
| Replace the %%public_api() placeholder in the .h.def content with the generated header content. |
| |
| Args: |
| header_str: The generated header string. |
| h_def_content: The content of the .h.def file. |
| |
| Returns: |
| The final header content with the public API filled in. |
| """ |
| header_str = header_str.strip() |
| return h_def_content.replace("%%public_api()", header_str, 1) |
| |
| |
| def parse_function_details(details): |
| """ |
| Parse function details from a list of strings and return a Function object. |
| |
| Args: |
| details: A list containing function details |
| |
| Returns: |
| Function: An instance of Function initialized with the details. |
| """ |
| return_type, name, arguments, standards, guard, attributes = details |
| standards = standards.split(",") if standards != "null" else [] |
| arguments = [arg.strip() for arg in arguments.split(",")] |
| attributes = attributes.split(",") if attributes != "null" else [] |
| |
| return Function( |
| return_type=return_type, |
| name=name, |
| arguments=arguments, |
| standards=standards, |
| guard=guard if guard != "null" else None, |
| attributes=attributes if attributes else [], |
| ) |
| |
| |
| def add_function_to_yaml(yaml_file, function_details): |
| """ |
| Add a function to the YAML file. |
| |
| Args: |
| yaml_file: The path to the YAML file. |
| function_details: A list containing function details (return_type, name, arguments, standards, guard, attributes). |
| """ |
| new_function = parse_function_details(function_details) |
| |
| with open(yaml_file, "r") as f: |
| yaml_data = yaml.safe_load(f) |
| if "functions" not in yaml_data: |
| yaml_data["functions"] = [] |
| |
| function_dict = { |
| "name": new_function.name, |
| "standards": new_function.standards, |
| "return_type": new_function.return_type, |
| "arguments": [{"type": arg} for arg in new_function.arguments], |
| } |
| |
| if new_function.guard: |
| function_dict["guard"] = new_function.guard |
| |
| if new_function.attributes: |
| function_dict["attributes"] = new_function.attributes |
| |
| insert_index = 0 |
| for i, func in enumerate(yaml_data["functions"]): |
| if func["name"] > new_function.name: |
| insert_index = i |
| break |
| else: |
| insert_index = len(yaml_data["functions"]) |
| |
| yaml_data["functions"].insert(insert_index, function_dict) |
| |
| class IndentYamlListDumper(yaml.Dumper): |
| def increase_indent(self, flow=False, indentless=False): |
| return super(IndentYamlListDumper, self).increase_indent(flow, False) |
| |
| with open(yaml_file, "w") as f: |
| yaml.dump( |
| yaml_data, |
| f, |
| Dumper=IndentYamlListDumper, |
| default_flow_style=False, |
| sort_keys=False, |
| ) |
| |
| print(f"Added function {new_function.name} to {yaml_file}") |
| |
| |
| def main( |
| yaml_file, |
| output_dir=None, |
| h_def_file=None, |
| add_function=None, |
| entry_points=None, |
| export_decls=False, |
| ): |
| """ |
| Main function to generate header files from YAML and .h.def templates. |
| |
| Args: |
| yaml_file: Path to the YAML file containing header specification. |
| h_def_file: Path to the .h.def template file. |
| output_dir: Directory to output the generated header file. |
| add_function: Details of the function to be added to the YAML file (if any). |
| entry_points: A list of specific function names to include in the header. |
| export_decls: Flag to use GpuHeader for exporting declarations. |
| """ |
| if add_function: |
| add_function_to_yaml(yaml_file, add_function) |
| |
| header_class = GpuHeader if export_decls else HeaderFile |
| header = load_yaml_file(yaml_file, header_class, entry_points) |
| |
| header_str = str(header) |
| |
| if output_dir: |
| output_file_path = Path(output_dir) |
| if output_file_path.is_dir(): |
| output_file_path /= f"{Path(yaml_file).stem}.h" |
| else: |
| output_file_path = Path(f"{Path(yaml_file).stem}.h") |
| |
| if not export_decls and h_def_file: |
| with open(h_def_file, "r") as f: |
| h_def_content = f.read() |
| final_header_content = fill_public_api(header_str, h_def_content) |
| with open(output_file_path, "w") as f: |
| f.write(final_header_content) |
| else: |
| with open(output_file_path, "w") as f: |
| f.write(header_str) |
| |
| |
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser(description="Generate header files from YAML") |
| parser.add_argument( |
| "yaml_file", help="Path to the YAML file containing header specification" |
| ) |
| parser.add_argument( |
| "--output_dir", |
| help="Directory to output the generated header file", |
| ) |
| parser.add_argument( |
| "--h_def_file", |
| help="Path to the .h.def template file (required if not using --export_decls)", |
| ) |
| parser.add_argument( |
| "--add_function", |
| nargs=6, |
| metavar=( |
| "RETURN_TYPE", |
| "NAME", |
| "ARGUMENTS", |
| "STANDARDS", |
| "GUARD", |
| "ATTRIBUTES", |
| ), |
| help="Add a function to the YAML file", |
| ) |
| parser.add_argument( |
| "--e", action="append", help="Entry point to include", dest="entry_points" |
| ) |
| parser.add_argument( |
| "--export-decls", |
| action="store_true", |
| help="Flag to use GpuHeader for exporting declarations", |
| ) |
| args = parser.parse_args() |
| |
| main( |
| args.yaml_file, |
| args.output_dir, |
| args.h_def_file, |
| args.add_function, |
| args.entry_points, |
| args.export_decls, |
| ) |