#!/usr/bin/env python3 # # esx_vi_generator.py: generates most of the SOAP type mapping code # # Copyright (C) 2014 Red Hat, Inc. # Copyright (C) 2010-2012 Matthias Bolte # Copyright (C) 2013 Ata E Husain Bohra # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see # . # import sys import os import os.path OCCURRENCE__REQUIRED_ITEM = "r" OCCURRENCE__REQUIRED_LIST = "rl" OCCURRENCE__OPTIONAL_ITEM = "o" OCCURRENCE__OPTIONAL_LIST = "ol" OCCURRENCE__IGNORED = "i" valid_occurrences = [OCCURRENCE__REQUIRED_ITEM, OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_ITEM, OCCURRENCE__OPTIONAL_LIST, OCCURRENCE__IGNORED] autobind_names = set() separator = "/* " + ("* " * 37) + "*\n" def aligned(left, right, length=59): return left.ljust(length, ' ') + right class Member: def __init__(self, type, occurrence): self.type = type self.occurrence = occurrence def is_enum(self): return self.type in predefined_enums or self.type in enums_by_name def is_object(self): return self.type in predefined_objects or self.type in objects_by_name def is_type_generated(self): return self.type in enums_by_name or self.type in objects_by_name def get_occurrence_comment(self): occurrence_map = { OCCURRENCE__REQUIRED_ITEM: "/* required */", OCCURRENCE__REQUIRED_LIST: "/* required, list */", OCCURRENCE__OPTIONAL_ITEM: "/* optional */", OCCURRENCE__OPTIONAL_LIST: "/* optional, list */" } try: return occurrence_map[self.occurrence] except KeyError: raise ValueError("unknown occurrence value '%s'" % self.occurrence) class Parameter(Member): def __init__(self, type, name, occurrence): Member.__init__(self, type, occurrence) if ':' in name and name.startswith("_this"): self.name, self.autobind_name = name.split(":") else: self.name = name self.autobind_name = None def generate_parameter(self, is_last=False, is_header=True, offset=0): if self.occurrence == OCCURRENCE__IGNORED: raise ValueError("invalid function parameter occurrence value '%s'" % self.occurrence) elif self.autobind_name is not None: return "" else: string = " " string += " " * offset string += "%s%s" % (self.get_type_string(), self.name) if is_last: if is_header: string += "); " else: string += "), " else: string += ", " return aligned(string, self.get_occurrence_comment() + "\n") def generate_return(self, offset=0, end_of_line=";"): if self.occurrence == OCCURRENCE__IGNORED: raise ValueError("invalid function parameter occurrence value '%s'" % self.occurrence) else: string = " " string += " " * offset string += "%s%s)%s" \ % (self.get_type_string(True), self.name, end_of_line) return aligned(string, self.get_occurrence_comment() + "\n") def generate_require_code(self): if self.occurrence in [OCCURRENCE__REQUIRED_ITEM, OCCURRENCE__REQUIRED_LIST]: return " ESX_VI__METHOD__PARAMETER__REQUIRE(%s)\n" % self.name else: return "" def generate_serialize_code(self): if self.occurrence in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return " ESX_VI__METHOD__PARAMETER__SERIALIZE_LIST(%s, %s)\n" \ % (self.type, self.name) elif self.type == "String": return " ESX_VI__METHOD__PARAMETER__SERIALIZE_VALUE(String, %s)\n" \ % self.name else: return " ESX_VI__METHOD__PARAMETER__SERIALIZE(%s, %s)\n" \ % (self.type, self.name) def get_type_string(self, as_return_value=False): string = "" if self.type == "String" and \ self.occurrence not in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: if as_return_value: string += "char *" else: string += "const char *" elif self.is_enum(): string += "esxVI_%s " % self.type else: string += "esxVI_%s *" % self.type if as_return_value: string += "*" return string def get_occurrence_short_enum(self): if self.occurrence == OCCURRENCE__REQUIRED_ITEM: return "RequiredItem" elif self.occurrence == OCCURRENCE__REQUIRED_LIST: return "RequiredList" elif self.occurrence == OCCURRENCE__OPTIONAL_ITEM: return "OptionalItem" elif self.occurrence == OCCURRENCE__OPTIONAL_LIST: return "OptionalList" raise ValueError("unknown occurrence value '%s'" % self.occurrence) class Method: def __init__(self, name, parameters, returns): self.name = name self.parameters = [] self.autobind_parameter = None self.returns = returns for parameter in parameters: if parameter.autobind_name is None: self.parameters.append(parameter) else: self.autobind_parameter = parameter def generate_header(self): header = "int esxVI_%s\n" % self.name header += " (esxVI_Context *ctx" if len(self.parameters) > 0 or self.returns is not None: header += ",\n" for parameter in self.parameters[:-1]: header += parameter.generate_parameter() if self.returns is None: header += self.parameters[-1].generate_parameter(is_last=True) else: header += self.parameters[-1].generate_parameter() header += self.returns.generate_return() else: header += ");\n" header += "\n" return header def generate_source(self): source = "/* esxVI_%s */\n" % self.name source += "ESX_VI__METHOD(%s," % self.name if self.autobind_parameter is not None: autobind_names.add(self.autobind_parameter.autobind_name) source += " %s,\n" % self.autobind_parameter.autobind_name else: source += " /* explicit _this */,\n" source += " (esxVI_Context *ctx" if len(self.parameters) > 0 or self.returns is not None: source += ",\n" for parameter in self.parameters[:-1]: source += parameter.generate_parameter(is_header=False, offset=9) if self.returns is None: source += self.parameters[-1].generate_parameter(is_last=True, is_header=False, offset=9) else: source += self.parameters[-1].generate_parameter(is_header=False, offset=9) source += self.returns.generate_return(offset=9, end_of_line=",") else: source += "),\n" if self.returns is None: source += " void, /* nothing */, None,\n" elif self.returns.type == "String": source += " String, Value, %s,\n" \ % self.returns.get_occurrence_short_enum() else: source += " %s, /* nothing */, %s,\n" \ % (self.returns.type, self.returns.get_occurrence_short_enum()) source += "{\n" if self.autobind_parameter is not None: source += self.autobind_parameter.generate_require_code() for parameter in self.parameters: source += parameter.generate_require_code() source += "},\n" source += "{\n" if self.autobind_parameter is not None: source += self.autobind_parameter.generate_serialize_code() for parameter in self.parameters: source += parameter.generate_serialize_code() source += "})\n\n\n\n" return source class Property(Member): def __init__(self, type, name, occurrence): Member.__init__(self, type, occurrence) self.name = name def generate_struct_member(self): if self.occurrence == OCCURRENCE__IGNORED: return " /* FIXME: %s is currently ignored */\n" % self.name else: string = " %s%s; " % (self.get_type_string(), self.name) return aligned(string, self.get_occurrence_comment() + "\n") def generate_free_code(self): if self.type == "String" and \ self.occurrence not in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST, OCCURRENCE__IGNORED]: return " VIR_FREE(item->%s);\n" % self.name elif self.is_enum(): return "" else: if self.occurrence == OCCURRENCE__IGNORED: return " /* FIXME: %s is currently ignored */\n" % self.name else: return " esxVI_%s_Free(&item->%s);\n" % (self.type, self.name) def generate_validate_code(self, managed=False): if managed: macro = "ESX_VI__TEMPLATE__PROPERTY__MANAGED_REQUIRE" else: macro = "ESX_VI__TEMPLATE__PROPERTY__REQUIRE" if self.occurrence in [OCCURRENCE__REQUIRED_ITEM, OCCURRENCE__REQUIRED_LIST]: return " %s(%s)\n" % (macro, self.name) elif self.occurrence == OCCURRENCE__IGNORED: return " /* FIXME: %s is currently ignored */\n" % self.name else: return "" def generate_deep_copy_code(self): if self.occurrence == OCCURRENCE__IGNORED: return " /* FIXME: %s is currently ignored */\n" % self.name elif self.occurrence in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY_LIST(%s, %s)\n" \ % (self.type, self.name) elif self.type == "String": return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY_VALUE(String, %s)\n" \ % self.name elif self.is_enum(): return " (*dest)->%s = src->%s;\n" % (self.name, self.name) else: return " ESX_VI__TEMPLATE__PROPERTY__DEEP_COPY(%s, %s)\n" \ % (self.type, self.name) def generate_serialize_code(self): if self.occurrence == OCCURRENCE__IGNORED: return " /* FIXME: %s is currently ignored */\n" % self.name elif self.occurrence in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return " ESX_VI__TEMPLATE__PROPERTY__SERIALIZE_LIST(%s, %s)\n" \ % (self.type, self.name) elif self.type == "String": return " ESX_VI__TEMPLATE__PROPERTY__SERIALIZE_VALUE(String, %s)\n" \ % self.name else: return " ESX_VI__TEMPLATE__PROPERTY__SERIALIZE(%s, %s)\n" \ % (self.type, self.name) def generate_deserialize_code(self): if self.occurrence == OCCURRENCE__IGNORED: return " ESX_VI__TEMPLATE__PROPERTY__DESERIALIZE_IGNORE(%s) /* FIXME */\n" \ % self.name elif self.occurrence in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return " ESX_VI__TEMPLATE__PROPERTY__DESERIALIZE_LIST(%s, %s)\n" \ % (self.type, self.name) elif self.type == "String": return " ESX_VI__TEMPLATE__PROPERTY__DESERIALIZE_VALUE(String, %s)\n" \ % self.name else: return " ESX_VI__TEMPLATE__PROPERTY__DESERIALIZE(%s, %s)\n" \ % (self.type, self.name) def generate_lookup_code(self): if self.occurrence == OCCURRENCE__IGNORED: return " ESX_VI__TEMPLATE__PROPERTY__CAST_FROM_ANY_TYPE_IGNORE(%s) /* FIXME */\n" \ % self.name elif self.occurrence in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return " ESX_VI__TEMPLATE__PROPERTY__CAST_LIST_FROM_ANY_TYPE(%s, %s)\n" \ % (self.type, self.name) elif self.type == "String": return " ESX_VI__TEMPLATE__PROPERTY__CAST_VALUE_FROM_ANY_TYPE(String, %s)\n" \ % self.name else: return " ESX_VI__TEMPLATE__PROPERTY__CAST_FROM_ANY_TYPE(%s, %s)\n" \ % (self.type, self.name) def get_type_string(self): if self.type == "String" and \ self.occurrence not in [OCCURRENCE__REQUIRED_LIST, OCCURRENCE__OPTIONAL_LIST]: return "char *" elif self.is_enum(): return "esxVI_%s " % self.type else: return "esxVI_%s *" % self.type class Type: def __init__(self, kind, name): self.kind = kind self.name = name def generate_typedef(self): return "typedef %s _esxVI_%s esxVI_%s;\n" \ % (self.kind, self.name, self.name) def generate_typeenum(self): return " esxVI_Type_%s,\n" % self.name def generate_typetostring(self): string = " case esxVI_Type_%s:\n" % self.name string += " return \"%s\";\n\n" % self.name return string def generate_typefromstring(self): string = " if (STREQ(type, \"%s\"))\n" % self.name string += " return esxVI_Type_%s;\n" % self.name return string class GenericObject(Type): FEATURE__DYNAMIC_CAST = (1 << 1) FEATURE__LIST = (1 << 2) FEATURE__DEEP_COPY = (1 << 3) FEATURE__ANY_TYPE = (1 << 4) FEATURE__SERIALIZE = (1 << 5) FEATURE__DESERIALIZE = (1 << 6) def __init__(self, name, category, managed, generic_objects_by_name): Type.__init__(self, "struct", name) self.category = category self.managed = managed self.generic_objects_by_name = generic_objects_by_name def generate_comment(self): comment = separator comment += " * %s: %s\n" % (self.category, self.name) if self.extends is not None: comment += " * %s extends %s\n" \ % (' ' * len(self.category), self.extends) first = True if self.extended_by is not None: for extended_by in self.extended_by: if first: comment += " * %s extended by %s\n" \ % (' ' * len(self.category), extended_by) first = False else: comment += " * %s %s\n" \ % (' ' * len(self.category), extended_by) comment += " */\n\n" return comment def generate_struct_members(self, add_banner=False, struct_gap=False): members = "" if struct_gap: members += "\n" if self.extends is not None: obj = self.generic_objects_by_name[self.extends] members += obj.generate_struct_members(add_banner=True, struct_gap=False) + "\n" if self.extends is not None or add_banner: members += " /* %s */\n" % self.name for property in self.properties: members += property.generate_struct_member() if len(self.properties) < 1: members += " /* no properties */\n" return members def generate_dispatch(self, suffix, is_first=True): source = "" if self.extended_by is not None: if not is_first: source += "\n" source += " /* %s */\n" % self.name for extended_by in self.extended_by: source += " ESX_VI__TEMPLATE__DISPATCH__%s(%s)\n" \ % (suffix, extended_by) for extended_by in self.extended_by: obj = self.generic_objects_by_name[extended_by] source += obj.generate_dispatch(suffix, False) return source def generate_free_code(self, add_banner=False): source = "" if self.extends is not None: obj = self.generic_objects_by_name[self.extends] source += obj.generate_free_code(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: string = "" for property in self.properties: string += property.generate_free_code() if len(string) < 1: source += " /* no properties to be freed */\n" else: source += string return source def generate_validate_code(self, add_banner=False): source = "" if self.extends is not None: obj = self.generic_objects_by_name[self.extends] source += obj.generate_validate_code(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: string = "" for property in self.properties: string += property.generate_validate_code(self.managed) if len(string) < 1: source += " /* no required properties */\n" else: source += string return source class Object(GenericObject): def __init__(self, name, extends, properties, features=0, extended_by=None): GenericObject.__init__(self, name, 'VI Object', False, objects_by_name) self.extends = extends self.features = features self.properties = properties self.extended_by = extended_by self.candidate_for_dynamic_cast = False if self.extended_by is not None: self.extended_by.sort() def generate_dynamic_cast_code(self, is_first=True): source = "" if self.extended_by is not None: if not is_first: source += "\n" source += " /* %s */\n" % self.name for extended_by in self.extended_by: source += " ESX_VI__TEMPLATE__DYNAMIC_CAST__ACCEPT(%s)\n" \ % extended_by for extended_by in self.extended_by: obj = objects_by_name[extended_by] source += obj.generate_dynamic_cast_code(False) return source def generate_deep_copy_code(self, add_banner=False): source = "" if self.extends is not None: obj = objects_by_name[self.extends] source += obj.generate_deep_copy_code(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: string = "" for property in self.properties: string += property.generate_deep_copy_code() if len(string) < 1: source += " /* no properties to be deep copied */\n" else: source += string return source def generate_serialize_code(self, add_banner=False): source = "" if self.extends is not None: obj = objects_by_name[self.extends] source += obj.generate_serialize_code(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: for property in self.properties: source += property.generate_serialize_code() return source def generate_deserialize_code(self, add_banner=False): source = "" if self.extends is not None: obj = objects_by_name[self.extends] source += obj.generate_deserialize_code(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: for property in self.properties: source += property.generate_deserialize_code() return source def generate_header(self): header = self.generate_comment() # struct header += "struct _esxVI_%s {\n" % self.name if self.features & Object.FEATURE__LIST: header += aligned(" esxVI_%s *_next; " % self.name, "/* optional */\n") else: header += aligned(" esxVI_%s *_unused; " % self.name, "/* optional */\n") header += aligned(" esxVI_Type _type; ", "/* required */\n") header += self.generate_struct_members(struct_gap=True) header += "};\n\n" # functions header += "int esxVI_%s_Alloc(esxVI_%s **item);\n" \ % (self.name, self.name) header += "void esxVI_%s_Free(esxVI_%s **item);\n" \ % (self.name, self.name) header += "int esxVI_%s_Validate(esxVI_%s *item);\n" \ % (self.name, self.name) if self.features & Object.FEATURE__DYNAMIC_CAST: if self.extended_by is not None or self.extends is not None: header += "esxVI_%s *esxVI_%s_DynamicCast(void *item);\n" \ % (self.name, self.name) else: report_error("cannot add dynamic cast support for an untyped object") if self.features & Object.FEATURE__LIST: header += "int esxVI_%s_AppendToList(esxVI_%s **list, esxVI_%s *item);\n" \ % (self.name, self.name, self.name) if self.features & Object.FEATURE__DEEP_COPY: header += "int esxVI_%s_DeepCopy(esxVI_%s **dst, esxVI_%s *src);\n" \ % (self.name, self.name, self.name) if self.features & Object.FEATURE__LIST: header += (( "int esxVI_%s_DeepCopyList(esxVI_%s **dstList, " " esxVI_%s *srcList);\n") % (self.name, self.name, self.name)) if self.features & Object.FEATURE__ANY_TYPE: header += (( "int esxVI_%s_CastFromAnyType(esxVI_AnyType *anyType, " " esxVI_%s **item);\n") % (self.name, self.name)) if self.features & Object.FEATURE__LIST: header += (( "int esxVI_%s_CastListFromAnyType(esxVI_AnyType *anyType, " " esxVI_%s **list);\n") % (self.name, self.name)) if self.features & Object.FEATURE__SERIALIZE: header += (( "int esxVI_%s_Serialize(esxVI_%s *item, " " const char *element, " " virBuffer *output);\n") % (self.name, self.name)) if self.features & Object.FEATURE__LIST: header += (( "int esxVI_%s_SerializeList(esxVI_%s *list, " " const char *element, " " virBuffer *output);\n") % (self.name, self.name)) if self.features & Object.FEATURE__DESERIALIZE: header += "int esxVI_%s_Deserialize(xmlNodePtr node, esxVI_%s **item);\n" \ % (self.name, self.name) if self.features & Object.FEATURE__LIST: header += (( "int esxVI_%s_DeserializeList(xmlNodePtr node, " " esxVI_%s **list);\n") % (self.name, self.name)) header += "\n\n\n" return header def generate_source(self): source = separator source += " * VI Object: %s\n" % self.name if self.extends is not None: source += " * extends %s\n" % self.extends first = True if self.extended_by is not None: for extended_by in self.extended_by: if first: source += " * extended by %s\n" % extended_by first = False else: source += " * %s\n" % extended_by source += " */\n\n" # functions source += "/* esxVI_%s_Alloc */\n" % self.name source += "ESX_VI__TEMPLATE__ALLOC(%s)\n\n" % self.name # free source += "/* esxVI_%s_Free */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__FREE(%s,\n" % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_FREE(%s,\n" % self.name source += "{\n" source += self.generate_dispatch('FREE') source += "},\n" source += "{\n" if self.features & Object.FEATURE__LIST: base_class = get_base_class(self) if base_class: # avoid "dereferencing type-punned pointer will break # strict-aliasing rules" warnings source += " esxVI_%s *baseNext = (esxVI_%s *)item->_next;\n" \ % (base_class, base_class) source += " esxVI_%s_Free(&baseNext);\n\n" % base_class else: source += " esxVI_%s_Free(&item->_next);\n\n" % self.name source += self.generate_free_code() source += "})\n\n" # validate source += "/* esxVI_%s_Validate */\n" % self.name source += "ESX_VI__TEMPLATE__VALIDATE(%s,\n" % self.name source += "{\n" source += self.generate_validate_code() source += "})\n\n" # dynamic cast if self.features & Object.FEATURE__DYNAMIC_CAST: if self.extended_by is not None or self.extends is not None: source += "/* esxVI_%s_DynamicCast */\n" % self.name source += "ESX_VI__TEMPLATE__DYNAMIC_CAST(%s,\n" % self.name source += "{\n" source += self.generate_dynamic_cast_code() source += "})\n\n" else: report_error("cannot add dynamic cast support for an untyped object") # append to list if self.features & Object.FEATURE__LIST: source += "/* esxVI_%s_AppendToList */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__APPEND(%s)\n\n" % self.name # deep copy if self.features & Object.FEATURE__DEEP_COPY: source += "/* esxVI_%s_DeepCopy */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__DEEP_COPY(%s,\n" % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_DEEP_COPY(%s,\n" % self.name source += "{\n" source += self.generate_dispatch('DEEP_COPY') source += "},\n" source += "{\n" source += self.generate_deep_copy_code() source += "})\n\n" if self.features & Object.FEATURE__LIST: source += "/* esxVI_%s_DeepCopyList */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__DEEP_COPY(%s)\n\n" \ % self.name # cast from any type if self.features & Object.FEATURE__ANY_TYPE: source += "/* esxVI_%s_CastFromAnyType */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__CAST_FROM_ANY_TYPE(%s)\n\n" \ % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_CAST_FROM_ANY_TYPE(%s,\n" \ % self.name source += "{\n" source += self.generate_dispatch('CAST_FROM_ANY_TYPE') source += "})\n\n" if self.features & Object.FEATURE__LIST: source += "/* esxVI_%s_CastListFromAnyType */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__CAST_FROM_ANY_TYPE(%s)\n\n" \ % self.name # serialize if self.features & Object.FEATURE__SERIALIZE: source += "/* esxVI_%s_Serialize */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__SERIALIZE(%s,\n" % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_SERIALIZE(%s,\n" % self.name source += "{\n" source += self.generate_dispatch('SERIALIZE') source += "},\n" source += "{\n" source += self.generate_serialize_code() source += "})\n\n" if self.features & Object.FEATURE__LIST: source += "/* esxVI_%s_SerializeList */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__SERIALIZE(%s)\n\n" \ % self.name # deserialize if self.features & Object.FEATURE__DESERIALIZE: source += "/* esxVI_%s_Deserialize */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__DESERIALIZE(%s,\n" % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_DESERIALIZE(%s,\n" \ % self.name source += "{\n" source += self.generate_dispatch('DESERIALIZE') source += "},\n" source += "{\n" source += self.generate_deserialize_code() source += "})\n\n" if self.features & Object.FEATURE__LIST: source += "/* esxVI_%s_DeserializeList */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__DESERIALIZE(%s)\n\n" \ % self.name source += "\n\n" return source class ManagedObject(GenericObject): def __init__(self, name, extends, properties, features=0, extended_by=None): GenericObject.__init__(self, name, 'VI Managed Object', True, managed_objects_by_name) self.extends = extends self.features = features self.properties = properties self.extended_by = extended_by if self.extended_by is not None: self.extended_by.sort() def generate_lookup_code1(self, add_banner=False): source = "" if self.extends is not None: obj = managed_objects_by_name[self.extends] source += obj.generate_lookup_code1(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: string = "" for property in self.properties: string += " \"%s\\0\"\n" % property.name if len(string) < 1: source += " /* no properties */\n" else: source += string return source def generate_lookup_code2(self, add_banner=False): source = "" if self.extends is not None: obj = managed_objects_by_name[self.extends] source += obj.generate_lookup_code2(add_banner=True) + "\n" if self.extends is not None or add_banner: source += " /* %s */\n" % self.name if len(self.properties) < 1: source += " /* no properties */\n" else: string = "" for property in self.properties: string += property.generate_lookup_code() if len(string) < 1: source += " /* no properties */\n" else: source += string return source def generate_header(self): header = self.generate_comment() # struct header += "struct _esxVI_%s {\n" % self.name if self.features & Object.FEATURE__LIST: header += aligned(" esxVI_%s *_next; " % self.name, "/* optional */\n") else: header += aligned(" esxVI_%s *_unused; " % self.name, "/* optional */\n") header += aligned(" esxVI_Type _type; ", "/* required */\n") header += aligned(" esxVI_ManagedObjectReference *_reference; ", "/* required */\n") header += "\n" header += self.generate_struct_members() header += "};\n\n" # functions header += "int esxVI_%s_Alloc(esxVI_%s **item);\n" % (self.name, self.name) header += "void esxVI_%s_Free(esxVI_%s **item);\n" % (self.name, self.name) header += ("int esxVI_%s_Validate(esxVI_%s *item, " " esxVI_String *selectedPropertyNameList);\n") \ % (self.name, self.name) if self.features & Object.FEATURE__LIST: header += "int esxVI_%s_AppendToList(esxVI_%s **list, esxVI_%s *item);\n" \ % (self.name, self.name, self.name) header += "\n\n\n" return header def generate_helper_header(self): # functions return ( "int esxVI_Lookup%(name)s(esxVI_Context *ctx," " const char *name," " esxVI_ManagedObjectReference *root," " esxVI_String *selectedPropertyNameList," " esxVI_%(name)s **item," " esxVI_Occurrence occurrence);\n\n" % {"name": self.name} ) def generate_source(self): source = self.generate_comment() # functions source += "/* esxVI_%s_Alloc */\n" % self.name source += "ESX_VI__TEMPLATE__ALLOC(%s)\n\n" % self.name # free source += "/* esxVI_%s_Free */\n" % self.name if self.extended_by is None: source += "ESX_VI__TEMPLATE__FREE(%s,\n" % self.name else: source += "ESX_VI__TEMPLATE__DYNAMIC_FREE(%s,\n" % self.name source += "{\n" source += self.generate_dispatch('FREE') source += "},\n" source += "{\n" if self.features & ManagedObject.FEATURE__LIST: if self.extends is not None: # avoid "dereferencing type-punned pointer will break # strict-aliasing rules" warnings source += " esxVI_%s *next = (esxVI_%s *)item->_next;\n\n" \ % (self.extends, self.extends) source += " esxVI_%s_Free(&next);\n" % self.extends source += " item->_next = (esxVI_%s *)next;\n\n" % self.name else: source += " esxVI_%s_Free(&item->_next);\n" % self.name source += " esxVI_ManagedObjectReference_Free(&item->_reference);\n\n" source += self.generate_free_code() source += "})\n\n" # validate source += "/* esxVI_%s_Validate */\n" % self.name source += "ESX_VI__TEMPLATE__MANAGED_VALIDATE(%s,\n" % self.name source += "{\n" source += self.generate_validate_code() source += "})\n\n" # append to list if self.features & ManagedObject.FEATURE__LIST: source += "/* esxVI_%s_AppendToList */\n" % self.name source += "ESX_VI__TEMPLATE__LIST__APPEND(%s)\n\n" % self.name source += "\n\n" return source def generate_helper_source(self): # lookup return ( "/* esxVI_Lookup%(name)s */\n" "ESX_VI__TEMPLATE__LOOKUP(%(name)s,\n" "{\n" "%(lookup_code1)s},\n" "{\n" "%(lookup_code2)s})" "\n\n\n\n" % {"name": self.name, "lookup_code1": self.generate_lookup_code1(), "lookup_code2": self.generate_lookup_code2()} ) class Enum(Type): FEATURE__ANY_TYPE = (1 << 1) FEATURE__SERIALIZE = (1 << 2) FEATURE__DESERIALIZE = (1 << 3) def __init__(self, name, values, features=0): Type.__init__(self, "enum", name) self.values = values self.features = features def generate_header(self): header = separator header += " * VI Enum: %s\n" % self.name header += " */\n\n" header += "enum _esxVI_%s {\n" % self.name header += " esxVI_%s_Undefined = 0,\n" % self.name for value in self.values: header += " esxVI_%s_%s,\n" % (self.name, capitalize_first(value)) header += "};\n\n" # functions if self.features & Enum.FEATURE__ANY_TYPE: header += ("int esxVI_%s_CastFromAnyType(esxVI_AnyType *anyType, " " esxVI_%s *item);\n") \ % (self.name, self.name) if self.features & Enum.FEATURE__SERIALIZE: header += ("int esxVI_%s_Serialize(esxVI_%s item, const char *element, " " virBuffer *output);\n") \ % (self.name, self.name) if self.features & Enum.FEATURE__DESERIALIZE: header += ("int esxVI_%s_Deserialize(xmlNodePtr node, " " esxVI_%s *item);\n") \ % (self.name, self.name) header += "\n\n\n" return header def generate_source(self): source = separator source += " * VI Enum: %s\n" % self.name source += " */\n\n" source += "static const esxVI_Enumeration _esxVI_%s_Enumeration = {\n" \ % self.name source += " esxVI_Type_%s, {\n" % self.name for value in self.values: source += " { \"%s\", esxVI_%s_%s },\n" \ % (value, self.name, capitalize_first(value)) source += " { NULL, -1 },\n" source += " },\n" source += "};\n\n" # functions if self.features & Enum.FEATURE__ANY_TYPE: source += "/* esxVI_%s_CastFromAnyType */\n" % self.name source += "ESX_VI__TEMPLATE__ENUMERATION__CAST_FROM_ANY_TYPE(%s)\n\n" \ % self.name if self.features & Enum.FEATURE__SERIALIZE: source += "/* esxVI_%s_Serialize */\n" % self.name source += "ESX_VI__TEMPLATE__ENUMERATION__SERIALIZE(%s)\n\n" \ % self.name if self.features & Enum.FEATURE__DESERIALIZE: source += "/* esxVI_%s_Deserialize */\n" % self.name source += "ESX_VI__TEMPLATE__ENUMERATION__DESERIALIZE(%s)\n\n" \ % self.name source += "\n\n" return source def report_error(message): print("error: " + message) sys.exit(1) def capitalize_first(string): return string[:1].upper() + string[1:] def parse_object(block): # expected format: [managed] object [extends ] header_items = block[0][1].split() managed = False if header_items[0] == "managed": managed = True del header_items[0] if len(header_items) < 2: report_error("line %d: invalid block header" % (number)) assert header_items[0] == "object" name = header_items[1] extends = None if len(header_items) > 2: if header_items[2] != "extends": report_error("line %d: invalid block header" % (number)) else: extends = header_items[3] properties = [] for line in block[1:]: # expected format: items = line[1].split() if len(items) != 3: report_error("line %d: invalid property" % line[0]) if items[2] not in valid_occurrences: report_error("line %d: invalid occurrence" % line[0]) properties.append(Property(type=items[0], name=items[1], occurrence=items[2])) if managed: return ManagedObject(name=name, extends=extends, properties=properties) else: return Object(name=name, extends=extends, properties=properties) def parse_enum(block): # expected format: enum header_items = block[0][1].split() if len(header_items) < 2: report_error("line %d: invalid block header" % (number)) assert header_items[0] == "enum" name = header_items[1] values = [] for line in block[1:]: # expected format: values.append(line[1]) return Enum(name=name, values=values) def parse_method(block): # expected format: method [returns ] header_items = block[0][1].split() if len(header_items) < 2: report_error("line %d: invalid block header" % (number)) assert header_items[0] == "method" name = header_items[1] returns = None if len(header_items) > 2: if header_items[2] != "returns": report_error("line %d: invalid block header" % (number)) else: returns = Parameter(type=header_items[3], name="output", occurrence=header_items[4]) parameters = [] for line in block[1:]: # expected format: items = line[1].split() if len(items) != 3: report_error("line %d: invalid property" % line[0]) if items[2] not in valid_occurrences: report_error("line %d: invalid occurrence" % line[0]) parameters.append(Parameter(type=items[0], name=items[1], occurrence=items[2])) return Method(name=name, parameters=parameters, returns=returns) def is_known_type(type): return (type in predefined_objects or type in predefined_enums or type in objects_by_name or type in managed_objects_by_name or type in enums_by_name) def get_base_class(obj): if not obj.extends: return None base_class = None try: base_class = base_class_by_name[obj.extends] except KeyError: parent = objects_by_name[obj.extends] base_class = get_base_class(parent) if not base_class: base_class = parent.name base_class_by_name[name] = base_class return base_class def open_file(filename): return open(filename, "wt") predefined_enums = ["Boolean"] predefined_objects = ["AnyType", "Byte", "Int", "Long", "String", "DateTime", "MethodFault", "ManagedObjectReference"] additional_enum_features = { "ManagedEntityStatus": Enum.FEATURE__ANY_TYPE, "TaskInfoState": Enum.FEATURE__ANY_TYPE, "VirtualMachinePowerState": Enum.FEATURE__ANY_TYPE } additional_object_features = { "AutoStartDefaults": Object.FEATURE__ANY_TYPE, "AutoStartPowerInfo": Object.FEATURE__ANY_TYPE, "DatastoreHostMount": (Object.FEATURE__DEEP_COPY | Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE), "DatastoreInfo": Object.FEATURE__ANY_TYPE | Object.FEATURE__DYNAMIC_CAST, "GuestNicInfo": Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "HostConfigManager": Object.FEATURE__ANY_TYPE, "HostCpuIdInfo": Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "HostDatastoreBrowserSearchResults": (Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE), "HostHostBusAdapter": Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "HostInternetScsiHba": (Object.FEATURE__DYNAMIC_CAST | Object.FEATURE__DEEP_COPY), "HostInternetScsiTargetTransport": Object.FEATURE__DYNAMIC_CAST, "HostScsiDisk": (Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE | Object.FEATURE__DYNAMIC_CAST), "HostScsiTopologyInterface": (Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE), "HostScsiTopologyLun": (Object.FEATURE__ANY_TYPE | Object.FEATURE__LIST | Object.FEATURE__DEEP_COPY), "HostScsiTopologyTarget": Object.FEATURE__ANY_TYPE | Object.FEATURE__LIST, "HostPortGroup": Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "HostVirtualSwitch": (Object.FEATURE__DEEP_COPY | Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE), "ManagedObjectReference": Object.FEATURE__ANY_TYPE, "ObjectContent": Object.FEATURE__DEEP_COPY, "PhysicalNic": (Object.FEATURE__DEEP_COPY | Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE), "ResourcePoolResourceUsage": Object.FEATURE__ANY_TYPE, "ScsiLun": (Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE | Object.FEATURE__DEEP_COPY), "ScsiLunDurableName": Object.FEATURE__LIST, "ServiceContent": Object.FEATURE__DESERIALIZE, "SharesInfo": Object.FEATURE__ANY_TYPE, "TaskInfo": Object.FEATURE__LIST | Object.FEATURE__ANY_TYPE, "UserSession": Object.FEATURE__ANY_TYPE, "VirtualMachineQuestionInfo": Object.FEATURE__ANY_TYPE, "VirtualMachineSnapshotTree": (Object.FEATURE__DEEP_COPY | Object.FEATURE__ANY_TYPE), "VmEventArgument": Object.FEATURE__DESERIALIZE } removed_object_features = {} if len(sys.argv) != 4: report_error("usage: %s srcdir builddir header" % sys.argv[0]) input_filename = os.path.join(sys.argv[1], "esx/esx_vi_generator.input") output_dirname = os.path.join(sys.argv[2], "esx") header = sys.argv[3] == "header" if header: types_typedef = open_file(os.path.join(output_dirname, "esx_vi_types.generated.typedef")) types_typeenum = open_file(os.path.join(output_dirname, "esx_vi_types.generated.typeenum")) types_header = open_file(os.path.join(output_dirname, "esx_vi_types.generated.h")) methods_header = open_file(os.path.join(output_dirname, "esx_vi_methods.generated.h")) helpers_header = open_file(os.path.join(output_dirname, "esx_vi.generated.h")) else: types_typetostring = open_file(os.path.join(output_dirname, "esx_vi_types.generated.typetostring")) types_typefromstring = open_file(os.path.join(output_dirname, "esx_vi_types.generated.typefromstring")) types_source = open_file(os.path.join(output_dirname, "esx_vi_types.generated.c")) methods_macro = open_file(os.path.join(output_dirname, "esx_vi_methods.generated.macro")) methods_source = open_file(os.path.join(output_dirname, "esx_vi_methods.generated.c")) helpers_source = open_file(os.path.join(output_dirname, "esx_vi.generated.c")) number = 0 objects_by_name = {} managed_objects_by_name = {} enums_by_name = {} methods_by_name = {} block = None base_class_by_name = {} # parse input file for line in open(input_filename, "rt").readlines(): number += 1 if "#" in line: line = line[:line.index("#")] line = line.lstrip().rstrip() if len(line) < 1: continue if line.startswith("object") or line.startswith("managed object") or \ line.startswith("enum") or line.startswith("method"): if block is not None: report_error("line %d: nested block found" % (number)) else: block = [] if block is not None: if line == "end": if block[0][1].startswith("object"): obj = parse_object(block) objects_by_name[obj.name] = obj elif block[0][1].startswith("managed object"): obj = parse_object(block) managed_objects_by_name[obj.name] = obj elif block[0][1].startswith("enum"): enum = parse_enum(block) enums_by_name[enum.name] = enum else: method = parse_method(block) methods_by_name[method.name] = method block = None else: block.append((number, line)) for method in methods_by_name.values(): # method parameter types must be serializable for parameter in method.parameters: if not parameter.is_type_generated(): continue if parameter.is_enum(): enums_by_name[parameter.type].features |= Enum.FEATURE__SERIALIZE else: objects_by_name[parameter.type].features |= Object.FEATURE__SERIALIZE objects_by_name[parameter.type].candidate_for_dynamic_cast = True # detect list usage if parameter.occurrence == OCCURRENCE__REQUIRED_LIST or \ parameter.occurrence == OCCURRENCE__OPTIONAL_LIST: if parameter.is_enum(): report_error("unsupported usage of enum '%s' as list in '%s'" % (parameter.type, method.name)) else: objects_by_name[parameter.type].features |= Object.FEATURE__LIST # method return types must be deserializable if method.returns and method.returns.is_type_generated(): if method.returns.is_enum(): enums_by_name[method.returns.type].features |= Enum.FEATURE__DESERIALIZE else: objects_by_name[method.returns.type].features |= Object.FEATURE__DESERIALIZE objects_by_name[method.returns.type].candidate_for_dynamic_cast = True # detect list usage if method.returns.occurrence == OCCURRENCE__REQUIRED_LIST or \ method.returns.occurrence == OCCURRENCE__OPTIONAL_LIST: if method.returns.is_enum(): report_error("unsupported usage of enum '%s' as list in '%s'" % (method.returns.type, method.name)) else: objects_by_name[method.returns.type].features |= Object.FEATURE__LIST for enum in enums_by_name.values(): # apply additional features if enum.name in additional_enum_features: enum.features |= additional_enum_features[enum.name] if additional_enum_features[enum.name] & Enum.FEATURE__ANY_TYPE: enum.features |= Enum.FEATURE__DESERIALIZE for obj in objects_by_name.values(): for property in obj.properties: if property.occurrence != OCCURRENCE__IGNORED and \ not is_known_type(property.type): report_error("object '%s' contains unknown property type '%s'" % (obj.name, property.type)) if obj.extends is not None: if not is_known_type(obj.extends): report_error("object '%s' extends unknown object '%s'" % (obj.name, obj.extends)) for property in obj.properties: if not property.is_type_generated(): continue if property.is_enum(): enums_by_name[property.type].candidate_for_dynamic_cast = True else: objects_by_name[property.type].candidate_for_dynamic_cast = True # detect list usage if property.occurrence == OCCURRENCE__REQUIRED_LIST or \ property.occurrence == OCCURRENCE__OPTIONAL_LIST: if property.is_enum(): report_error("unsupported usage of enum '%s' as list in '%s'" % (property.type, obj.type)) else: objects_by_name[property.type].features |= Object.FEATURE__LIST # apply/remove additional features if obj.name in additional_object_features: obj.features |= additional_object_features[obj.name] if additional_object_features[obj.name] & Object.FEATURE__ANY_TYPE: obj.features |= Object.FEATURE__DESERIALIZE if obj.name in removed_object_features: obj.features &= ~removed_object_features[obj.name] # detect extended_by relation if obj.extends is not None: extended_obj = objects_by_name[obj.extends] if extended_obj.extended_by is None: extended_obj.extended_by = [obj.name] else: extended_obj.extended_by.append(obj.name) extended_obj.extended_by.sort() for obj in objects_by_name.values(): # if an object is a candidate (it is used directly as parameter or return # type or is a member of another object) and it is extended by another # object then this type needs the dynamic cast feature if obj.candidate_for_dynamic_cast and obj.extended_by: obj.features |= Object.FEATURE__DYNAMIC_CAST def propagate_feature(obj, feature): global features_have_changed if not (obj.features & feature): return for property in obj.properties: if (property.occurrence == OCCURRENCE__IGNORED or not property.is_type_generated()): continue if property.is_enum(): if (feature == Object.FEATURE__SERIALIZE and not (enums_by_name[property.type].features & Enum.FEATURE__SERIALIZE)): enums_by_name[property.type].features |= Enum.FEATURE__SERIALIZE features_have_changed = True elif (feature == Object.FEATURE__DESERIALIZE and not (enums_by_name[property.type].features & Enum.FEATURE__DESERIALIZE)): enums_by_name[property.type].features |= Enum.FEATURE__DESERIALIZE features_have_changed = True elif property.is_object(): if not (objects_by_name[property.type].features & feature): objects_by_name[property.type].features |= feature features_have_changed = True if obj.name != property.type: propagate_feature(objects_by_name[property.type], feature) def inherit_features(obj): global features_have_changed if obj.extended_by is not None: for extended_by in obj.extended_by: previous = objects_by_name[extended_by].features objects_by_name[extended_by].features |= obj.features if objects_by_name[extended_by].features != previous: features_have_changed = True if obj.extends is not None: previous = objects_by_name[obj.extends].features objects_by_name[obj.extends].features |= obj.features if objects_by_name[obj.extends].features != previous: features_have_changed = True if obj.extended_by is not None: for extended_by in obj.extended_by: inherit_features(objects_by_name[extended_by]) # there are two directions to spread features: # 1) up and down the inheritance chain # 2) from object types to their member property types # spreading needs to be done alternating on both directions because they can # affect each other features_have_changed = True while features_have_changed: features_have_changed = False for obj in objects_by_name.values(): propagate_feature(obj, Object.FEATURE__DEEP_COPY) propagate_feature(obj, Object.FEATURE__SERIALIZE) propagate_feature(obj, Object.FEATURE__DESERIALIZE) for obj in objects_by_name.values(): inherit_features(obj) for obj in managed_objects_by_name.values(): for property in obj.properties: if property.occurrence != OCCURRENCE__IGNORED and \ not is_known_type(property.type): report_error("object '%s' contains unknown property type '%s'" % (obj.name, property.type)) if obj.extends is not None: if not is_known_type(obj.extends): report_error("object '%s' extends unknown object '%s'" % (obj.name, obj.extends)) # detect extended_by relation if obj.extends is not None: extended_obj = managed_objects_by_name[obj.extends] if extended_obj.extended_by is None: extended_obj.extended_by = [obj.name] else: extended_obj.extended_by.append(obj.name) extended_obj.extended_by.sort() notice = "/* Generated by esx_vi_generator.py */\n\n\n\n" if (header): types_typedef.write(notice) types_typeenum.write(notice) types_header.write(notice) methods_header.write(notice) helpers_header.write(notice) else: types_typetostring.write(notice) types_typefromstring.write(notice) types_source.write(notice) methods_macro.write(notice) methods_source.write(notice) helpers_source.write(notice) # output enums if header: types_typedef.write(separator + " * VI Enums\n" + " */\n\n") names = sorted(enums_by_name.keys()) for name in names: if header: types_typedef.write(enums_by_name[name].generate_typedef()) types_typeenum.write(enums_by_name[name].generate_typeenum()) types_header.write(enums_by_name[name].generate_header()) else: types_typetostring.write(enums_by_name[name].generate_typetostring()) types_typefromstring.write(enums_by_name[name].generate_typefromstring()) types_source.write(enums_by_name[name].generate_source()) # output objects if header: types_typedef.write("\n\n\n" + separator + " * VI Objects\n" + " */\n\n") types_typeenum.write("\n") else: types_typetostring.write("\n") types_typefromstring.write("\n") names = sorted(objects_by_name.keys()) for name in names: if header: types_typedef.write(objects_by_name[name].generate_typedef()) types_typeenum.write(objects_by_name[name].generate_typeenum()) types_header.write(objects_by_name[name].generate_header()) else: types_typetostring.write(objects_by_name[name].generate_typetostring()) types_typefromstring.write(objects_by_name[name].generate_typefromstring()) types_source.write(objects_by_name[name].generate_source()) # output managed objects if header: types_typedef.write("\n\n\n" + separator + " * VI Managed Objects\n" + " */\n\n") types_typeenum.write("\n") else: types_typetostring.write("\n") types_typefromstring.write("\n") names = sorted(managed_objects_by_name.keys()) for name in names: if header: types_typedef.write(managed_objects_by_name[name].generate_typedef()) types_typeenum.write(managed_objects_by_name[name].generate_typeenum()) types_header.write(managed_objects_by_name[name].generate_header()) else: types_typetostring.write(managed_objects_by_name[name].generate_typetostring()) types_typefromstring.write(managed_objects_by_name[name].generate_typefromstring()) types_source.write(managed_objects_by_name[name].generate_source()) # output methods names = sorted(methods_by_name.keys()) for name in names: if header: methods_header.write(methods_by_name[name].generate_header()) else: methods_source.write(methods_by_name[name].generate_source()) if not header: names = list(autobind_names) names.sort() for name in names: string = aligned("#define ESX_VI__METHOD__PARAMETER__THIS__%s " % name, "\\\n", 78) string += " ESX_VI__METHOD__PARAMETER__THIS_FROM_SERVICE(ManagedObjectReference, \\\n" string += aligned("", "%s)\n\n\n\n" % name, 49) methods_macro.write(string) # output helpers names = sorted(managed_objects_by_name.keys()) for name in names: if header: helpers_header.write(managed_objects_by_name[name].generate_helper_header()) else: helpers_source.write(managed_objects_by_name[name].generate_helper_source())