[libc-commits] [libc] [libc] added newhdrgen python script and class file (PR #96671)

via libc-commits libc-commits at lists.llvm.org
Tue Jun 25 12:49:42 PDT 2024


https://github.com/aaryanshukla updated https://github.com/llvm/llvm-project/pull/96671

>From 295a1b9fb97c80595e9dca9f1dfd656ba877ee67 Mon Sep 17 00:00:00 2001
From: Aaryan Shukla <aaryanshukla at google.com>
Date: Tue, 25 Jun 2024 17:52:52 +0000
Subject: [PATCH 1/2] [libc] added newhdrgen python script and class file

python script uses yaml and classes to generate c headers
header.py is only the main class file, the rest will be in another pr
more files to be added in multiple prs
---
 libc/newhdrgen/header.py          |  59 ++++++++++++++++
 libc/newhdrgen/yaml_to_classes.py | 113 ++++++++++++++++++++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 libc/newhdrgen/header.py
 create mode 100644 libc/newhdrgen/yaml_to_classes.py

diff --git a/libc/newhdrgen/header.py b/libc/newhdrgen/header.py
new file mode 100644
index 0000000000000..311345f10d1a2
--- /dev/null
+++ b/libc/newhdrgen/header.py
@@ -0,0 +1,59 @@
+# class_implementation/classes/header.py
+class HeaderFile:
+    def __init__(self, name):
+        self.name = name
+        self.macros = []
+        self.types = []
+        self.enumerations = []
+        self.objects = []
+        self.functions = []
+        self.includes = []
+
+    def add_macro(self, macro):
+        self.macros.append(macro)
+
+    def add_type(self, type_):
+        self.types.append(type_)
+
+    def add_enumeration(self, enumeration):
+        self.enumerations.append(enumeration)
+
+    def add_object(self, object):
+        self.objects.append(object)
+
+    def add_function(self, function):
+        self.functions.append(function)
+
+    def add_include(self, include):
+        self.includes.append(include)
+
+    def __str__(self):
+        # header_guard = self.name.replace(".", "_").upper()
+        content = [""]
+
+        for include in self.includes:
+            content.append(str(include))
+
+        for macro in self.macros:
+            content.append(str(macro))
+
+        for object in self.objects:
+            content.append(str(object))
+
+        for type_ in self.types:
+            content.append(str(type_))
+
+        if self.enumerations:
+            content.append("enum {")
+            for enum in self.enumerations:
+                content.append(f"\t{str(enum)},")
+            content.append("};")
+
+        # ToDo: replace line below with common.h functionality
+        content.append("__BEGIN_C_DECLS\n")
+        if self.functions:
+            for function in self.functions:
+                content.append(str(function))
+                content.append("")
+        content.append("__END_C_DECLS\n")
+        return "\n".join(content)
diff --git a/libc/newhdrgen/yaml_to_classes.py b/libc/newhdrgen/yaml_to_classes.py
new file mode 100644
index 0000000000000..e77e3b8fd8598
--- /dev/null
+++ b/libc/newhdrgen/yaml_to_classes.py
@@ -0,0 +1,113 @@
+import yaml
+import os
+import re
+
+from header import HeaderFile
+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.include import Include
+from class_implementation.classes.enums import Enumeration
+from class_implementation.classes.object import Object
+
+
+def yaml_to_classes(yaml_data):
+    header_name = yaml_data.get("header", "unknown.h")
+    # standard = yaml_data.get('standard', None)
+    header = HeaderFile(header_name)
+
+    for macro_data in yaml_data.get("macros", []):
+        header.add_macro(Macro(macro_data["macro_name"], macro_data["macro_value"]))
+
+    for type_data in yaml_data.get("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))
+        )
+
+    for object_data in yaml_data.get("objects", []):
+        header.add_object(
+            Object(object_data["object_name"], object_data["object_type"])
+        )
+
+    for function_data in yaml_data.get("functions", []):
+        arguments = [arg["type"] for arg in function_data["arguments"]]
+        header.add_function(
+            Function(
+                function_data["return_type"],
+                function_data["name"],
+                arguments,
+                function_data.get("guard"),
+                function_data.get("attributes", []),
+            )
+        )
+
+    for include_data in yaml_data.get("includes", []):
+        header.add_include(Include(include_data))
+
+    return header
+
+
+def load_yaml_file(yaml_file):
+    with open(yaml_file, "r") as f:
+        yaml_data = yaml.safe_load(f)
+    return yaml_to_classes(yaml_data)
+
+
+# will be used for specific functions a user wants to generate headers for
+"""
+def filter_functions(header, function_names):
+    filtered_functions = []
+    function_name_set = set(function_names)
+    for func in header.functions:
+        if func.name in function_name_set:
+            filtered_functions.append(func)
+    return filtered_functions
+"""
+
+
+def fill_public_api(header_str, h_def_content):
+    # using regular expression to identify the public_api string
+    return re.sub(r"%%public_api\(\)", header_str, h_def_content)
+
+
+def main(yaml_file, h_def_file, output_dir):
+    header = load_yaml_file(yaml_file)
+
+    with open(h_def_file, "r") as f:
+        h_def_content = f.read()
+
+    header_str = str(header)
+    final_header_content = fill_public_api(header_str, h_def_content)
+
+    output_file_name = os.path.basename(h_def_file).replace(".def", "")
+    output_file_path = os.path.join(output_dir, output_file_name)
+
+    with open(output_file_path, "w") as f:
+        f.write(final_header_content)
+
+    print(f"Generated header file: {output_file_path}")
+
+
+if __name__ == "__main__":
+    import argparse
+
+    parser = argparse.ArgumentParser(
+        description="Generate header files from YAML and .h.def templates"
+    )
+    parser.add_argument(
+        "yaml_file", help="Path to the YAML file containing header specification"
+    )
+    parser.add_argument("h_def_file", help="Path to the .h.def template file")
+    parser.add_argument(
+        "--output_dir",
+        default=".",
+        help="Directory to output the generated header file",
+    )
+    args = parser.parse_args()
+
+    main(args.yaml_file, args.h_def_file, args.output_dir)
+
+# Example Command Line Arg: python3 yaml_to_classes.py yaml/stdc_stdbit.yaml h_def/stdbit.h.def --output_dir output

>From 4fe3cc2fb2022bd9e18b1f7225a4ae1536854f02 Mon Sep 17 00:00:00 2001
From: Aaryan Shukla <aaryanshukla at google.com>
Date: Tue, 25 Jun 2024 19:49:04 +0000
Subject: [PATCH 2/2] added comments and license header

---
 libc/newhdrgen/yaml_to_classes.py | 63 ++++++++++++++++++++++---------
 1 file changed, 46 insertions(+), 17 deletions(-)

diff --git a/libc/newhdrgen/yaml_to_classes.py b/libc/newhdrgen/yaml_to_classes.py
index e77e3b8fd8598..2074c63013d00 100644
--- a/libc/newhdrgen/yaml_to_classes.py
+++ b/libc/newhdrgen/yaml_to_classes.py
@@ -1,6 +1,16 @@
+#!/usr/bin/env python
+#
+# ====- 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 os
 import re
+import argparse
 
 from header import HeaderFile
 from class_implementation.classes.macro import Macro
@@ -12,8 +22,16 @@
 
 
 def yaml_to_classes(yaml_data):
+    """
+    Convert YAML data to header classes.
+
+    Args:
+        yaml_data: The YAML data containing header specifications.
+
+    Returns:
+        HeaderFile: An instance of HeaderFile populated with the data.
+    """
     header_name = yaml_data.get("header", "unknown.h")
-    # standard = yaml_data.get('standard', None)
     header = HeaderFile(header_name)
 
     for macro_data in yaml_data.get("macros", []):
@@ -51,29 +69,44 @@ def yaml_to_classes(yaml_data):
 
 
 def load_yaml_file(yaml_file):
+    """
+    Load YAML file and convert it to header classes.
+
+    Args:
+        yaml_file: The path to the YAML file.
+
+    Returns:
+        HeaderFile: An instance of HeaderFile populated with the data from the YAML file.
+    """
     with open(yaml_file, "r") as f:
         yaml_data = yaml.safe_load(f)
     return yaml_to_classes(yaml_data)
 
 
-# will be used for specific functions a user wants to generate headers for
-"""
-def filter_functions(header, function_names):
-    filtered_functions = []
-    function_name_set = set(function_names)
-    for func in header.functions:
-        if func.name in function_name_set:
-            filtered_functions.append(func)
-    return filtered_functions
-"""
+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.
 
-def fill_public_api(header_str, h_def_content):
-    # using regular expression to identify the public_api string
+    Returns:
+        The final header content with the public API filled in.
+    """
     return re.sub(r"%%public_api\(\)", header_str, h_def_content)
 
 
 def main(yaml_file, h_def_file, output_dir):
+    """
+    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.
+    """
+    
     header = load_yaml_file(yaml_file)
 
     with open(h_def_file, "r") as f:
@@ -92,8 +125,6 @@ def main(yaml_file, h_def_file, output_dir):
 
 
 if __name__ == "__main__":
-    import argparse
-
     parser = argparse.ArgumentParser(
         description="Generate header files from YAML and .h.def templates"
     )
@@ -109,5 +140,3 @@ def main(yaml_file, h_def_file, output_dir):
     args = parser.parse_args()
 
     main(args.yaml_file, args.h_def_file, args.output_dir)
-
-# Example Command Line Arg: python3 yaml_to_classes.py yaml/stdc_stdbit.yaml h_def/stdbit.h.def --output_dir output



More information about the libc-commits mailing list