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