xref: /llvm-project/libc/utils/hdrgen/yaml_to_classes.py (revision 6ad0dcf67f5dccdf8506ce5f51d793062a1c6879)
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