1#!/usr/bin/python3 2import os 3import re 4import shutil 5import xml.etree.ElementTree as ET 6from collections import defaultdict 7 8SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 9REDFISH_SCHEMA_DIR = os.path.realpath( 10 os.path.join(SCRIPT_DIR, "..", "redfish-core", "schema") 11) 12 13OUTFOLDER = os.path.realpath( 14 os.path.join( 15 SCRIPT_DIR, "..", "redfish-core", "include", "generated", "enums" 16 ) 17) 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)) 44 return EntityTypes 45 46 47def parse_file(filename): 48 print(f"Parsing {filename}") 49 tree = ET.parse(filename) 50 root = tree.getroot() 51 results = [] 52 data_services = root.findall(EDMX + "DataServices") 53 for ds in data_services: 54 for element in ds: 55 if element.tag == EDM + "Schema": 56 results.extend(parse_schema(element, filename)) 57 58 return results 59 60 61def camel_to_snake(name): 62 # snake casing PCIeDevice and PCIeFunction results in mediocre results 63 # given that the standard didn't camel case those in a way that the 64 # algorithm expects, so change the casing explicitly to generate sane 65 # snake case results. 66 name = name.replace("PCIe", "Pcie") 67 name = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name) 68 return re.sub("([a-z0-9])([A-Z])", r"\1_\2", name).lower() 69 70 71def write_enum_list(redfish_defs_file, enum_list, snake_case_namespace): 72 redfish_defs_file.write( 73 "#pragma once\n" 74 "#include <nlohmann/json.hpp>\n\n" 75 "namespace {}\n" 76 "{{\n" 77 "// clang-format off\n\n".format(snake_case_namespace) 78 ) 79 80 for element in enum_list: 81 redfish_defs_file.write("enum class {}{{\n".format(element.name)) 82 values = element.values 83 if "Invalid" not in values: 84 values.insert(0, "Invalid") 85 86 for value in values: 87 redfish_defs_file.write(" {},\n".format(value)) 88 89 redfish_defs_file.write("};\n\n") 90 91 for element in enum_list: 92 values = element.values 93 if "Invalid" not in values: 94 values.insert(0, "Invalid") 95 # nlohmann::json apparently uses c style arrays in their enum 96 # implementation, and clang-tidy isn't smart enough to figure out that 97 # the C arrays are in their code not bmcwebs, so we have to explicitly 98 # ignore the error. 99 redfish_defs_file.write( 100 "NLOHMANN_JSON_SERIALIZE_ENUM({}, {{\n".format(element.name) 101 ) 102 for value in values: 103 redfish_defs_file.write( 104 ' {{{}::{}, "{}"}},\n'.format(element.name, value, value) 105 ) 106 107 redfish_defs_file.write("});\n\n") 108 109 print(element.name) 110 111 redfish_defs_file.write("}\n// clang-format on\n") 112 113 114def generate_enums(flat_list): 115 # clear out the old results if they exist 116 if os.path.exists(OUTFOLDER): 117 shutil.rmtree(OUTFOLDER) 118 os.makedirs(OUTFOLDER) 119 120 enum_by_namespace = defaultdict(list) 121 122 for element in flat_list: 123 if isinstance(element, Enum): 124 namespace_split = element.namespace.split(".")[0] 125 enum_by_namespace[namespace_split].append(element) 126 127 for namespace, enum_list in enum_by_namespace.items(): 128 snake_case_namespace = camel_to_snake(namespace) 129 outfile = os.path.join( 130 OUTFOLDER, "{}.hpp".format(snake_case_namespace) 131 ) 132 133 with open(outfile, "w") as redfish_defs: 134 write_enum_list(redfish_defs, enum_list, snake_case_namespace) 135 136 137def main(): 138 print("Reading from {}".format(REDFISH_SCHEMA_DIR)) 139 140 filepaths = [] 141 for root, dirs, files in os.walk(REDFISH_SCHEMA_DIR): 142 for csdl_file in files: 143 if csdl_file.endswith(".xml"): 144 filepaths.append(os.path.join(root, csdl_file)) 145 print(filepaths) 146 enum_list = [] 147 148 for filepath in filepaths: 149 out = parse_file(filepath) 150 enum_list.extend(out) 151 152 print("Parsing done") 153 154 generate_enums(enum_list) 155 156 157if __name__ == "__main__": 158 main() 159