10ec8b83dSEd Tanous#!/usr/bin/python3 20ec8b83dSEd Tanousimport os 3fd06b304SPatrick Williamsimport re 4fd06b304SPatrick Williamsimport shutil 50ec8b83dSEd Tanousimport xml.etree.ElementTree as ET 60ec8b83dSEd Tanousfrom collections import defaultdict 70ec8b83dSEd Tanous 80ec8b83dSEd TanousSCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 90ec8b83dSEd TanousREDFISH_SCHEMA_DIR = os.path.realpath( 10720c9898SEd Tanous os.path.join(SCRIPT_DIR, "..", "redfish-core", "schema") 110ec8b83dSEd Tanous) 120ec8b83dSEd Tanous 13fd06b304SPatrick WilliamsOUTFOLDER = os.path.realpath( 14fd06b304SPatrick Williams os.path.join( 15fd06b304SPatrick Williams SCRIPT_DIR, "..", "redfish-core", "include", "generated", "enums" 160ec8b83dSEd Tanous ) 170ec8b83dSEd Tanous) 180ec8b83dSEd Tanous 190ec8b83dSEd Tanous# Odata string types 200ec8b83dSEd TanousEDMX = "{http://docs.oasis-open.org/odata/ns/edmx}" 210ec8b83dSEd TanousEDM = "{http://docs.oasis-open.org/odata/ns/edm}" 220ec8b83dSEd Tanous 230ec8b83dSEd Tanous 240ec8b83dSEd Tanousclass Enum: 250ec8b83dSEd Tanous def __init__(self, name, values, namespace, from_file): 260ec8b83dSEd Tanous self.name = name 270ec8b83dSEd Tanous self.values = values 280ec8b83dSEd Tanous self.namespace = namespace 290ec8b83dSEd Tanous self.from_file = from_file 300ec8b83dSEd Tanous 310ec8b83dSEd Tanous 320ec8b83dSEd Tanousdef parse_schema(element, filename): 330ec8b83dSEd Tanous EntityTypes = [] 340ec8b83dSEd Tanous namespace = element.attrib["Namespace"] 350ec8b83dSEd Tanous for schema_element in element: 360ec8b83dSEd Tanous name = schema_element.attrib.get("Name", None) 370ec8b83dSEd Tanous if name is None: 380ec8b83dSEd Tanous continue 390ec8b83dSEd Tanous if schema_element.tag == EDM + "EnumType": 400ec8b83dSEd Tanous enums = [] 410ec8b83dSEd Tanous for member in schema_element.findall(EDM + "Member"): 420ec8b83dSEd Tanous enums.append(member.attrib["Name"]) 430ec8b83dSEd Tanous EntityTypes.append(Enum(name, enums, namespace, filename)) 440ec8b83dSEd Tanous return EntityTypes 450ec8b83dSEd Tanous 460ec8b83dSEd Tanous 470ec8b83dSEd Tanousdef parse_file(filename): 48720c9898SEd Tanous print(f"Parsing {filename}") 490ec8b83dSEd Tanous tree = ET.parse(filename) 500ec8b83dSEd Tanous root = tree.getroot() 510ec8b83dSEd Tanous results = [] 520ec8b83dSEd Tanous data_services = root.findall(EDMX + "DataServices") 530ec8b83dSEd Tanous for ds in data_services: 540ec8b83dSEd Tanous for element in ds: 550ec8b83dSEd Tanous if element.tag == EDM + "Schema": 560ec8b83dSEd Tanous results.extend(parse_schema(element, filename)) 570ec8b83dSEd Tanous 580ec8b83dSEd Tanous return results 590ec8b83dSEd Tanous 600ec8b83dSEd Tanous 610ec8b83dSEd Tanousdef camel_to_snake(name): 620ec8b83dSEd Tanous # snake casing PCIeDevice and PCIeFunction results in mediocre results 630ec8b83dSEd Tanous # given that the standard didn't camel case those in a way that the 640ec8b83dSEd Tanous # algorithm expects, so change the casing explicitly to generate sane 650ec8b83dSEd Tanous # snake case results. 660ec8b83dSEd Tanous name = name.replace("PCIe", "Pcie") 670ec8b83dSEd Tanous name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) 680ec8b83dSEd Tanous return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() 690ec8b83dSEd Tanous 700ec8b83dSEd Tanous 710ec8b83dSEd Tanousdef write_enum_list(redfish_defs_file, enum_list, snake_case_namespace): 720ec8b83dSEd Tanous redfish_defs_file.write( 73*40e9b92eSEd Tanous "// SPDX-License-Identifier: Apache-2.0\n" 74*40e9b92eSEd Tanous "// SPDX-FileCopyrightText: Copyright OpenBMC Authors\n" 750ec8b83dSEd Tanous "#pragma once\n" 760ec8b83dSEd Tanous "#include <nlohmann/json.hpp>\n\n" 770ec8b83dSEd Tanous "namespace {}\n" 780ec8b83dSEd Tanous "{{\n" 790ec8b83dSEd Tanous "// clang-format off\n\n".format(snake_case_namespace) 800ec8b83dSEd Tanous ) 810ec8b83dSEd Tanous 820ec8b83dSEd Tanous for element in enum_list: 830ec8b83dSEd Tanous redfish_defs_file.write("enum class {}{{\n".format(element.name)) 840ec8b83dSEd Tanous values = element.values 850ec8b83dSEd Tanous if "Invalid" not in values: 860ec8b83dSEd Tanous values.insert(0, "Invalid") 870ec8b83dSEd Tanous 880ec8b83dSEd Tanous for value in values: 890ec8b83dSEd Tanous redfish_defs_file.write(" {},\n".format(value)) 900ec8b83dSEd Tanous 910ec8b83dSEd Tanous redfish_defs_file.write("};\n\n") 920ec8b83dSEd Tanous 930ec8b83dSEd Tanous for element in enum_list: 940ec8b83dSEd Tanous values = element.values 950ec8b83dSEd Tanous if "Invalid" not in values: 960ec8b83dSEd Tanous values.insert(0, "Invalid") 978ece0e45SEd Tanous # nlohmann::json apparently uses c style arrays in their enum 980ec8b83dSEd Tanous # implementation, and clang-tidy isn't smart enough to figure out that 990ec8b83dSEd Tanous # the C arrays are in their code not bmcwebs, so we have to explicitly 1000ec8b83dSEd Tanous # ignore the error. 1010ec8b83dSEd Tanous redfish_defs_file.write( 102fd06b304SPatrick Williams "NLOHMANN_JSON_SERIALIZE_ENUM({}, {{\n".format(element.name) 1030ec8b83dSEd Tanous ) 1040ec8b83dSEd Tanous for value in values: 1050ec8b83dSEd Tanous redfish_defs_file.write( 1060ec8b83dSEd Tanous ' {{{}::{}, "{}"}},\n'.format(element.name, value, value) 1070ec8b83dSEd Tanous ) 1080ec8b83dSEd Tanous 1090ec8b83dSEd Tanous redfish_defs_file.write("});\n\n") 1100ec8b83dSEd Tanous 1110ec8b83dSEd Tanous print(element.name) 1120ec8b83dSEd Tanous 113fd06b304SPatrick Williams redfish_defs_file.write("}\n// clang-format on\n") 1140ec8b83dSEd Tanous 1150ec8b83dSEd Tanous 1160ec8b83dSEd Tanousdef generate_enums(flat_list): 1170ec8b83dSEd Tanous # clear out the old results if they exist 1180ec8b83dSEd Tanous if os.path.exists(OUTFOLDER): 1190ec8b83dSEd Tanous shutil.rmtree(OUTFOLDER) 1200ec8b83dSEd Tanous os.makedirs(OUTFOLDER) 1210ec8b83dSEd Tanous 1220ec8b83dSEd Tanous enum_by_namespace = defaultdict(list) 1230ec8b83dSEd Tanous 1240ec8b83dSEd Tanous for element in flat_list: 1250ec8b83dSEd Tanous if isinstance(element, Enum): 1260ec8b83dSEd Tanous namespace_split = element.namespace.split(".")[0] 1270ec8b83dSEd Tanous enum_by_namespace[namespace_split].append(element) 1280ec8b83dSEd Tanous 1290ec8b83dSEd Tanous for namespace, enum_list in enum_by_namespace.items(): 1300ec8b83dSEd Tanous snake_case_namespace = camel_to_snake(namespace) 1310ec8b83dSEd Tanous outfile = os.path.join( 1320ec8b83dSEd Tanous OUTFOLDER, "{}.hpp".format(snake_case_namespace) 1330ec8b83dSEd Tanous ) 1340ec8b83dSEd Tanous 1350ec8b83dSEd Tanous with open(outfile, "w") as redfish_defs: 1360ec8b83dSEd Tanous write_enum_list(redfish_defs, enum_list, snake_case_namespace) 1370ec8b83dSEd Tanous 1380ec8b83dSEd Tanous 1390ec8b83dSEd Tanousdef main(): 1400ec8b83dSEd Tanous print("Reading from {}".format(REDFISH_SCHEMA_DIR)) 1410ec8b83dSEd Tanous 142720c9898SEd Tanous filepaths = [] 143720c9898SEd Tanous for root, dirs, files in os.walk(REDFISH_SCHEMA_DIR): 144720c9898SEd Tanous for csdl_file in files: 145a529a6aaSEd Tanous filepath = os.path.join(root, csdl_file) 146a529a6aaSEd Tanous if os.path.islink(filepath): 147a529a6aaSEd Tanous continue 148720c9898SEd Tanous if csdl_file.endswith(".xml"): 149a529a6aaSEd Tanous filepaths.append(filepath) 150720c9898SEd Tanous print(filepaths) 1510ec8b83dSEd Tanous enum_list = [] 152a529a6aaSEd Tanous filepaths.sort() 1530ec8b83dSEd Tanous for filepath in filepaths: 1540ec8b83dSEd Tanous out = parse_file(filepath) 1550ec8b83dSEd Tanous enum_list.extend(out) 1560ec8b83dSEd Tanous 1570ec8b83dSEd Tanous print("Parsing done") 1580ec8b83dSEd Tanous 1590ec8b83dSEd Tanous generate_enums(enum_list) 1600ec8b83dSEd Tanous 1610ec8b83dSEd Tanous 1620ec8b83dSEd Tanousif __name__ == "__main__": 1630ec8b83dSEd Tanous main() 164