blob: 4f79f302b9391eeba92378d676a524d27656773f [file] [log] [blame]
Ed Tanous0ec8b832022-03-14 14:56:47 -07001#!/usr/bin/python3
2import os
Patrick Williamsfd06b302022-12-12 10:39:42 -06003import re
4import shutil
Ed Tanous0ec8b832022-03-14 14:56:47 -07005import xml.etree.ElementTree as ET
6from collections import defaultdict
Ed Tanous0ec8b832022-03-14 14:56:47 -07007
8SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
9REDFISH_SCHEMA_DIR = os.path.realpath(
Ed Tanous720c9892024-05-11 07:28:09 -070010 os.path.join(SCRIPT_DIR, "..", "redfish-core", "schema")
Ed Tanous0ec8b832022-03-14 14:56:47 -070011)
12
Patrick Williamsfd06b302022-12-12 10:39:42 -060013OUTFOLDER = os.path.realpath(
14 os.path.join(
15 SCRIPT_DIR, "..", "redfish-core", "include", "generated", "enums"
16 )
Ed Tanous0ec8b832022-03-14 14:56:47 -070017)
18
19# Odata string types
20EDMX = "{http://docs.oasis-open.org/odata/ns/edmx}"
21EDM = "{http://docs.oasis-open.org/odata/ns/edm}"
22
23
24class Enum:
25 def __init__(self, name, values, namespace, from_file):
26 self.name = name
27 self.values = values
28 self.namespace = namespace
29 self.from_file = from_file
30
31
32def parse_schema(element, filename):
33 EntityTypes = []
34 namespace = element.attrib["Namespace"]
35 for schema_element in element:
36 name = schema_element.attrib.get("Name", None)
37 if name is None:
38 continue
39 if schema_element.tag == EDM + "EnumType":
40 enums = []
41 for member in schema_element.findall(EDM + "Member"):
42 enums.append(member.attrib["Name"])
43 EntityTypes.append(Enum(name, enums, namespace, filename))
Chandramohan Harkuded1a3caa2025-04-17 13:25:28 +053044 if schema_element.tag == EDM + "TypeDefinition":
45 enums = []
46 for annotation in schema_element:
47 for collection in annotation:
48 for record in collection.findall(EDM + "Record"):
49 for member in record.findall(EDM + "PropertyValue"):
50 enums.append(member.attrib["String"])
51 EntityTypes.append(Enum(name, enums, namespace, filename))
Ed Tanous0ec8b832022-03-14 14:56:47 -070052 return EntityTypes
53
54
55def parse_file(filename):
Ed Tanous720c9892024-05-11 07:28:09 -070056 print(f"Parsing {filename}")
Ed Tanous0ec8b832022-03-14 14:56:47 -070057 tree = ET.parse(filename)
58 root = tree.getroot()
59 results = []
60 data_services = root.findall(EDMX + "DataServices")
61 for ds in data_services:
62 for element in ds:
63 if element.tag == EDM + "Schema":
64 results.extend(parse_schema(element, filename))
65
66 return results
67
68
69def camel_to_snake(name):
70 # snake casing PCIeDevice and PCIeFunction results in mediocre results
71 # given that the standard didn't camel case those in a way that the
72 # algorithm expects, so change the casing explicitly to generate sane
73 # snake case results.
74 name = name.replace("PCIe", "Pcie")
75 name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name)
76 return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower()
77
78
79def write_enum_list(redfish_defs_file, enum_list, snake_case_namespace):
80 redfish_defs_file.write(
Ed Tanous40e9b922024-09-10 13:50:16 -070081 "// SPDX-License-Identifier: Apache-2.0\n"
82 "// SPDX-FileCopyrightText: Copyright OpenBMC Authors\n"
Ed Tanous0ec8b832022-03-14 14:56:47 -070083 "#pragma once\n"
84 "#include <nlohmann/json.hpp>\n\n"
85 "namespace {}\n"
86 "{{\n"
87 "// clang-format off\n\n".format(snake_case_namespace)
88 )
89
90 for element in enum_list:
91 redfish_defs_file.write("enum class {}{{\n".format(element.name))
92 values = element.values
93 if "Invalid" not in values:
94 values.insert(0, "Invalid")
95
96 for value in values:
Chandramohan Harkuded1a3caa2025-04-17 13:25:28 +053097 # If the value is numeric, prefix it with the enum name
98 if value.isdigit():
99 enum_value = f"{element.name}{value}"
100 else:
101 enum_value = re.sub(r"[^0-9_a-zA-Z]", "", value)
Ed Tanous0ec8b832022-03-14 14:56:47 -0700102
Chandramohan Harkuded1a3caa2025-04-17 13:25:28 +0530103 redfish_defs_file.write(" {},\n".format(enum_value))
Ed Tanous0ec8b832022-03-14 14:56:47 -0700104 redfish_defs_file.write("};\n\n")
105
106 for element in enum_list:
107 values = element.values
108 if "Invalid" not in values:
109 values.insert(0, "Invalid")
Ed Tanous8ece0e42024-01-02 13:16:50 -0800110 # nlohmann::json apparently uses c style arrays in their enum
Ed Tanous0ec8b832022-03-14 14:56:47 -0700111 # implementation, and clang-tidy isn't smart enough to figure out that
112 # the C arrays are in their code not bmcwebs, so we have to explicitly
113 # ignore the error.
114 redfish_defs_file.write(
Patrick Williamsfd06b302022-12-12 10:39:42 -0600115 "NLOHMANN_JSON_SERIALIZE_ENUM({}, {{\n".format(element.name)
Ed Tanous0ec8b832022-03-14 14:56:47 -0700116 )
117 for value in values:
Chandramohan Harkuded1a3caa2025-04-17 13:25:28 +0530118 # If the value is numeric, prefix it with the enum name
119 if value.isdigit():
120 enum_value = f"{element.name}{value}"
121 else:
122 enum_value = re.sub(r"[^0-9_a-zA-Z]", "", value)
Ed Tanous0ec8b832022-03-14 14:56:47 -0700123 redfish_defs_file.write(
Chandramohan Harkuded1a3caa2025-04-17 13:25:28 +0530124 ' {{{}::{}, "{}"}},\n'.format(
125 element.name, enum_value, value
126 )
Ed Tanous0ec8b832022-03-14 14:56:47 -0700127 )
128
129 redfish_defs_file.write("});\n\n")
130
131 print(element.name)
132
Patrick Williamsfd06b302022-12-12 10:39:42 -0600133 redfish_defs_file.write("}\n// clang-format on\n")
Ed Tanous0ec8b832022-03-14 14:56:47 -0700134
135
136def generate_enums(flat_list):
137 # clear out the old results if they exist
138 if os.path.exists(OUTFOLDER):
139 shutil.rmtree(OUTFOLDER)
140 os.makedirs(OUTFOLDER)
141
142 enum_by_namespace = defaultdict(list)
143
144 for element in flat_list:
145 if isinstance(element, Enum):
146 namespace_split = element.namespace.split(".")[0]
147 enum_by_namespace[namespace_split].append(element)
148
149 for namespace, enum_list in enum_by_namespace.items():
150 snake_case_namespace = camel_to_snake(namespace)
151 outfile = os.path.join(
152 OUTFOLDER, "{}.hpp".format(snake_case_namespace)
153 )
154
155 with open(outfile, "w") as redfish_defs:
156 write_enum_list(redfish_defs, enum_list, snake_case_namespace)
157
158
159def main():
160 print("Reading from {}".format(REDFISH_SCHEMA_DIR))
Ed Tanous0ec8b832022-03-14 14:56:47 -0700161
Ed Tanous720c9892024-05-11 07:28:09 -0700162 filepaths = []
163 for root, dirs, files in os.walk(REDFISH_SCHEMA_DIR):
164 for csdl_file in files:
Ed Tanousa529a6a2024-05-29 16:51:37 -0700165 filepath = os.path.join(root, csdl_file)
166 if os.path.islink(filepath):
167 continue
Ed Tanous720c9892024-05-11 07:28:09 -0700168 if csdl_file.endswith(".xml"):
Ed Tanousa529a6a2024-05-29 16:51:37 -0700169 filepaths.append(filepath)
Ed Tanous720c9892024-05-11 07:28:09 -0700170 print(filepaths)
Ed Tanous0ec8b832022-03-14 14:56:47 -0700171 enum_list = []
Ed Tanousa529a6a2024-05-29 16:51:37 -0700172 filepaths.sort()
Ed Tanous0ec8b832022-03-14 14:56:47 -0700173 for filepath in filepaths:
174 out = parse_file(filepath)
175 enum_list.extend(out)
176
177 print("Parsing done")
178
179 generate_enums(enum_list)
180
181
182if __name__ == "__main__":
183 main()