From 53fffa1db7cb04982db8977acd61b808ef60d5a8 Mon Sep 17 00:00:00 2001 From: Ole Troan Date: Tue, 13 Nov 2018 12:36:56 +0100 Subject: [PATCH] API: Add support for type aliases Previously all types are compound. This adds support for aliases, so one can do things like: typedef u32 interface_index; or typedef u8 ip4_address[4]; Change-Id: I0455cad0123fc88acb491d2a3ea2725426bdb246 Signed-off-by: Ole Troan Signed-off-by: Klement Sekera --- extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py | 7 ++++ src/tools/vppapigen/vppapigen.py | 29 ++++++++++++++++- src/tools/vppapigen/vppapigen_c.py | 13 ++++++-- src/tools/vppapigen/vppapigen_json.py | 1 + src/vnet/interface.api | 18 +++++++++++ src/vnet/interface_types.api | 18 +++++++++++ src/vnet/ipip/ipip.api | 13 ++++---- src/vpp-api/python/vpp_papi/vpp_papi.py | 11 +++++-- src/vpp-api/python/vpp_papi/vpp_serializer.py | 14 +++++++- src/vpp-api/vapi/vapi_c_gen.py | 43 ++++++++++++++++++------- src/vpp-api/vapi/vapi_cpp_gen.py | 9 ++++-- src/vpp-api/vapi/vapi_json_parser.py | 33 +++++++++++++++---- test/ext/fake.api.json | 2 ++ 13 files changed, 179 insertions(+), 32 deletions(-) create mode 100644 src/vnet/interface_types.api diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py index 299796b908b..3c2db15d176 100755 --- a/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py +++ b/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py @@ -320,6 +320,7 @@ class JVppModel(object): types = {} self._messages = [] self._services = {} + self._aliases = {} for file_name in json_api_files: with open(file_name) as f: j = json.load(f) @@ -328,6 +329,8 @@ class JVppModel(object): types.update({d[0]: {'type': 'union', 'data': d} for d in j['unions']}) self._messages.extend(j['messages']) self._services.update(j['services']) + self._aliases.update(j['aliases']) + self._parse_types(types) def _parse_types(self, types): @@ -481,6 +484,10 @@ class JVppModel(object): def _parse_field(self, field, fields): type_name = _extract_type_name(field[0]) + if type_name in self._aliases and type_name not in self._types_by_name: + aliased_type = self._types_by_name.get(self._aliases.get(type_name).get("type")) + self._types_by_name[type_name] = aliased_type + if type_name in self._types_by_name: if len(field) > 2: # Array field diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py index 9d04ec24cfc..3f882c455e6 100755 --- a/src/tools/vppapigen/vppapigen.py +++ b/src/tools/vppapigen/vppapigen.py @@ -151,6 +151,25 @@ class Typedef(): return self.name + str(self.flags) + str(self.block) +class Using(): + def __init__(self, name, alias): + global global_crc + self.name = name + + if isinstance(alias, Array): + a = { 'type': alias.fieldtype, + 'length': alias.length } + else: + a = { 'type': alias.fieldtype } + self.alias = a + self.crc = binascii.crc32(str(alias)) & 0xffffffff + global_crc = binascii.crc32(str(alias), global_crc) + global_type_add(name) + + def __repr__(self): + return self.name + str(self.alias) + + class Union(): def __init__(self, name, block): self.type = 'Union' @@ -457,6 +476,10 @@ class VPPAPIParser(object): '''typedef : TYPEDEF ID '{' block_statements_opt '}' ';' ''' p[0] = Typedef(p[2], [], p[4]) + def p_typedef_alias(self, p): + '''typedef : TYPEDEF declaration ''' + p[0] = Using(p[2].fieldname, p[2]) + def p_block_statements_opt(self, p): '''block_statements_opt : block_statements ''' p[0] = p[1] @@ -594,6 +617,7 @@ class VPPAPI(object): s['Service'] = [] s['types'] = [] s['Import'] = [] + s['Alias'] = {} for o in objs: tname = o.__class__.__name__ if isinstance(o, Define): @@ -608,6 +632,8 @@ class VPPAPI(object): s['Service'].append(o2) elif isinstance(o, Enum) or isinstance(o, Typedef) or isinstance(o, Union): s['types'].append(o) + elif isinstance(o, Using): + s['Alias'][o.name] = o.alias else: if tname not in s: raise ValueError('Unknown class type: {} {}'.format(tname, o)) @@ -686,7 +712,8 @@ class VPPAPI(object): if in_import and not (isinstance(o, Enum) or isinstance(o, Union) or isinstance(o, Typedef) or - isinstance(o, Import)): + isinstance(o, Import) or + isinstance(o, Using)): continue if isinstance(o, Import): self.process_imports(o.result, True, result) diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index b56e0722122..2a66ff3159e 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -94,7 +94,7 @@ def duplicate_wrapper_tail(): return '#endif\n\n' -def typedefs(objs, filename): +def typedefs(objs, aliases, filename): name = filename.replace('.', '_') output = '''\ @@ -106,6 +106,15 @@ def typedefs(objs, filename): #define included_{module} ''' output = output.format(module=name) + + for k, v in aliases.items(): + output += duplicate_wrapper_head(k) + if 'length' in v: + output += 'typedef %s vl_api_%s_t[%s];\n' % (v['type'], k, v['length']) + else: + output += 'typedef %s vl_api_%s_t;\n' % (v['type'], k) + output += duplicate_wrapper_tail() + for o in objs: tname = o.__class__.__name__ output += duplicate_wrapper_head(o.name) @@ -276,7 +285,7 @@ def run(input_filename, s, file_crc): output += msg_ids(s) output += msg_names(s) output += msg_name_crc_list(s, filename) - output += typedefs(s['types'] + s['Define'], filename + file_extension) + output += typedefs(s['types'] + s['Define'], s['Alias'], filename + file_extension) output += printfun(s['types'] + s['Define']) output += endianfun(s['types'] + s['Define']) output += version_tuple(s, basename) diff --git a/src/tools/vppapigen/vppapigen_json.py b/src/tools/vppapigen/vppapigen_json.py index 2991bec57ac..b57e16c990d 100644 --- a/src/tools/vppapigen/vppapigen_json.py +++ b/src/tools/vppapigen/vppapigen_json.py @@ -65,5 +65,6 @@ def run(filename, s, file_crc): j['unions'] = walk_defs([o for o in s['types'] if o.__class__.__name__ == 'Union']) j['enums'] = walk_enums([o for o in s['types'] if o.__class__.__name__ == 'Enum']) j['services'] = walk_services(s['Service']) + j['aliases'] = s['Alias'] j['vl_api_version'] = hex(file_crc) return json.dumps(j, indent=4, separators=(',', ': ')) diff --git a/src/vnet/interface.api b/src/vnet/interface.api index 2010d8b5f54..84e0483df67 100644 --- a/src/vnet/interface.api +++ b/src/vnet/interface.api @@ -1,5 +1,23 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + option version = "2.2.0"; +import "vnet/interface_types.api"; + service { rpc want_interface_events returns want_interface_events_reply events sw_interface_event; diff --git a/src/vnet/interface_types.api b/src/vnet/interface_types.api new file mode 100644 index 00000000000..f6fb4219ef8 --- /dev/null +++ b/src/vnet/interface_types.api @@ -0,0 +1,18 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2018 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +typedef u32 interface_index; + diff --git a/src/vnet/ipip/ipip.api b/src/vnet/ipip/ipip.api index 5cad28f2814..3dc087c954f 100644 --- a/src/vnet/ipip/ipip.api +++ b/src/vnet/ipip/ipip.api @@ -49,6 +49,7 @@ */ option version = "1.1.0"; +import "vnet/interface_types.api"; /** * Create an IP{v4,v6} over IP{v4,v6} tunnel. @@ -70,7 +71,7 @@ define ipip_add_tunnel_reply { u32 context; i32 retval; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; }; /** @@ -80,7 +81,7 @@ autoreply define ipip_del_tunnel { u32 client_index; u32 context; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; }; /** @@ -106,7 +107,7 @@ define ipip_6rd_add_tunnel_reply { u32 context; i32 retval; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; }; /** @@ -116,7 +117,7 @@ autoreply define ipip_6rd_del_tunnel { u32 client_index; u32 context; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; }; /** @@ -126,13 +127,13 @@ define ipip_tunnel_dump { u32 client_index; u32 context; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; }; define ipip_tunnel_details { u32 context; - u32 sw_if_index; + vl_api_interface_index_t sw_if_index; u32 instance; u8 is_ipv6; u8 src_address[16]; diff --git a/src/vpp-api/python/vpp_papi/vpp_papi.py b/src/vpp-api/python/vpp_papi/vpp_papi.py index ca4b955fd07..bd2682f5b8f 100644 --- a/src/vpp-api/python/vpp_papi/vpp_papi.py +++ b/src/vpp-api/python/vpp_papi/vpp_papi.py @@ -27,7 +27,7 @@ import fnmatch import weakref import atexit from . vpp_serializer import VPPType, VPPEnumType, VPPUnionType, BaseTypes -from . vpp_serializer import VPPMessage, vpp_get_type +from . vpp_serializer import VPPMessage, vpp_get_type, VPPTypeAlias from . vpp_format import VPPFormat if sys.version[0] == '2': @@ -102,13 +102,15 @@ class VPP(object): for t in api['types']: t[0] = 'vl_api_' + t[0] + '_t' types[t[0]] = {'type': 'type', 'data': t} + for t, v in api['aliases'].items(): + types['vl_api_' + t + '_t'] = {'type': 'alias', 'data': v} i = 0 while True: unresolved = {} for k, v in types.items(): t = v['data'] - if not vpp_get_type(t[0]): + if not vpp_get_type(k): if v['type'] == 'enum': try: VPPEnumType(t[0], t[1:]) @@ -124,6 +126,11 @@ class VPP(object): VPPType(t[0], t[1:]) except ValueError: unresolved[k] = v + elif v['type'] == 'alias': + try: + VPPTypeAlias(k, t) + except ValueError: + unresolved[k] = v if len(unresolved) == 0: break if i > 3: diff --git a/src/vpp-api/python/vpp_papi/vpp_serializer.py b/src/vpp-api/python/vpp_papi/vpp_serializer.py index bd0f73803da..a001cca565a 100644 --- a/src/vpp-api/python/vpp_papi/vpp_serializer.py +++ b/src/vpp-api/python/vpp_papi/vpp_serializer.py @@ -79,7 +79,7 @@ class FixedList_u8(object): self.packer = BaseTypes(field_type, num) self.size = self.packer.size - def pack(self, list, kwargs): + def pack(self, list, kwargs = None): """Packs a fixed length bytestring. Left-pads with zeros if input data is too short.""" if not list: @@ -277,6 +277,18 @@ class VPPUnionType(object): return self.tuple._make(r), maxsize +def VPPTypeAlias(name, msgdef): + t = vpp_get_type(msgdef['type']) + if not t: + raise ValueError() + if 'length' in msgdef: + if msgdef['length'] == 0: + raise ValueError() + types[name] = FixedList(name, msgdef['type'], msgdef['length']) + else: + types[name] = t + + class VPPType(object): # Set everything up to be able to pack / unpack def __init__(self, name, msgdef): diff --git a/src/vpp-api/vapi/vapi_c_gen.py b/src/vpp-api/vapi/vapi_c_gen.py index eb1006d3a7e..9939bc0556c 100755 --- a/src/vpp-api/vapi/vapi_c_gen.py +++ b/src/vpp-api/vapi/vapi_c_gen.py @@ -5,15 +5,18 @@ import os import sys import logging from vapi_json_parser import Field, Struct, Enum, Union, Message, JsonParser,\ - SimpleType, StructType + SimpleType, StructType, Alias class CField(Field): + def get_c_name(self): + return self.name + def get_c_def(self): if self.len is not None: - return "%s %s[%d]" % (self.type.get_c_name(), self.name, self.len) + return "%s %s[%d];" % (self.type.get_c_name(), self.name, self.len) else: - return "%s %s" % (self.type.get_c_name(), self.name) + return "%s %s;" % (self.type.get_c_name(), self.name) def get_swap_to_be_code(self, struct, var): if self.len is not None: @@ -95,12 +98,26 @@ class CField(Field): return result +class CAlias(CField): + def get_c_name(self): + return self.name + + def get_c_def(self): + return "typedef %s" % super(CAlias, self).get_c_def() + # if self.len is not None: + # return "typedef %s %s[%d];" % (self.type.get_c_name(), self.name, self.len) + # else: + # return "typedef %s %s;" % (self.type.get_c_name(), self.name) + + # def needs_byte_swap + + class CStruct(Struct): def get_c_def(self): return "\n".join([ - "typedef struct __attribute__((__packed__)) {\n%s;" % ( - ";\n".join([" %s" % x.get_c_def() - for x in self.fields])), + "typedef struct __attribute__((__packed__)) {\n%s" % ( + "\n".join([" %s" % x.get_c_def() + for x in self.fields])), "} %s;" % self.get_c_name()]) def get_vla_assign_code(self, prefix, path): @@ -156,7 +173,7 @@ class CSimpleType (SimpleType): try: self.get_swap_to_host_func_name() return True - except: + except KeyError: pass return False @@ -335,8 +352,8 @@ class CMessage (Message): if self.has_payload(): return "\n".join([ "typedef struct __attribute__ ((__packed__)) {", - "%s; " % - ";\n".join(self.payload_members), + "%s " % + "\n".join(self.payload_members), "} %s;" % self.get_payload_struct_name(), "", "typedef struct __attribute__ ((__packed__)) {", @@ -609,7 +626,8 @@ def emit_definition(parser, json_file, emitted, o): if (o not in parser.enums_by_json[json_file] and o not in parser.types_by_json[json_file] and o not in parser.unions_by_json[json_file] and - o.name not in parser.messages_by_json[json_file]): + o.name not in parser.messages_by_json[json_file] and + o not in parser.aliases_by_json[json_file]): return guard = "defined_%s" % o.get_c_name() print("#ifndef %s" % guard) @@ -690,6 +708,8 @@ def gen_json_unified_header(parser, logger, j, io, name): emitted = [] for e in parser.enums_by_json[j]: emit_definition(parser, j, emitted, e) + for a in parser.aliases_by_json[j]: + emit_definition(parser, j, emitted, a) for u in parser.unions_by_json[j]: emit_definition(parser, j, emitted, u) for t in parser.types_by_json[j]: @@ -765,7 +785,8 @@ if __name__ == '__main__': union_class=CUnion, struct_type_class=CStructType, field_class=CField, - message_class=CMessage) + message_class=CMessage, + alias_class=CAlias) # not using the model of having separate generated header and code files # with generated symbols present in shared library (per discussion with diff --git a/src/vpp-api/vapi/vapi_cpp_gen.py b/src/vpp-api/vapi/vapi_cpp_gen.py index 6b62bc4da22..c08993dc918 100755 --- a/src/vpp-api/vapi/vapi_cpp_gen.py +++ b/src/vpp-api/vapi/vapi_cpp_gen.py @@ -5,7 +5,7 @@ import os import sys import logging from vapi_c_gen import CField, CEnum, CStruct, CSimpleType, CStructType,\ - CMessage, json_to_c_header_name + CMessage, json_to_c_header_name, CAlias from vapi_json_parser import JsonParser @@ -21,6 +21,10 @@ class CppEnum(CEnum): pass +class CppAlias(CAlias): + pass + + class CppSimpleType (CSimpleType): pass @@ -251,7 +255,8 @@ if __name__ == '__main__': struct_type_class=CppStructType, field_class=CppField, enum_class=CppEnum, - message_class=CppMessage) + message_class=CppMessage, + alias_class=CppAlias) gen_cpp_headers(jsonparser, logger, args.prefix, args.gen_h_prefix, args.remove_path) diff --git a/src/vpp-api/vapi/vapi_json_parser.py b/src/vpp-api/vapi/vapi_json_parser.py index 39acca0b538..3eb5d3616e1 100644 --- a/src/vpp-api/vapi/vapi_json_parser.py +++ b/src/vpp-api/vapi/vapi_json_parser.py @@ -45,6 +45,10 @@ class Field(object): return self.is_vla() or self.type.has_vla() +class Alias(Field): + pass + + class Type(object): def __init__(self, name): self.name = name @@ -55,12 +59,6 @@ class Type(object): class SimpleType (Type): - def __init__(self, name): - super(SimpleType, self).__init__(name) - - def __str__(self): - return self.name - def has_vla(self): return False @@ -290,11 +288,12 @@ class JsonParser(object): def __init__(self, logger, files, simple_type_class=SimpleType, enum_class=Enum, union_class=Union, struct_type_class=StructType, field_class=Field, - message_class=Message): + message_class=Message, alias_class=Alias): self.services = {} self.messages = {} self.enums = {} self.unions = {} + self.aliases = {} self.types = { x: simple_type_class(x) for x in [ 'i8', 'i16', 'i32', 'i64', @@ -310,6 +309,7 @@ class JsonParser(object): self.union_class = union_class self.struct_type_class = struct_type_class self.field_class = field_class + self.alias_class = alias_class self.message_class = message_class self.exceptions = [] @@ -317,6 +317,7 @@ class JsonParser(object): self.types_by_json = {} self.enums_by_json = {} self.unions_by_json = {} + self.aliases_by_json = {} self.messages_by_json = {} self.logger = logger for f in files: @@ -329,6 +330,7 @@ class JsonParser(object): self.types_by_json[path] = [] self.enums_by_json[path] = [] self.unions_by_json[path] = [] + self.aliases_by_json[path] = [] self.messages_by_json[path] = {} with open(path) as f: j = json.load(f) @@ -369,6 +371,19 @@ class JsonParser(object): self.unions[union.name] = union self.logger.debug("Parsed union: %s" % union) self.unions_by_json[path].append(union) + for name, body in j['aliases'].iteritems(): + if name in self.aliases: + progress = progress + 1 + continue + if 'length' in body: + array_len = body['length'] + else: + array_len = None + t = self.types[body['type']] + alias = self.alias_class(name, t, array_len) + self.aliases[name] = alias + self.logger.debug("Parsed alias: %s" % alias) + self.aliases_by_json[path].append(alias) for t in j['types']: if t[0] in self.types: progress = progress + 1 @@ -429,12 +444,16 @@ class JsonParser(object): return self.enums[name] elif name in self.unions: return self.unions[name] + elif name in self.aliases: + return self.aliases[name] elif mundane_name in self.types: return self.types[mundane_name] elif mundane_name in self.enums: return self.enums[mundane_name] elif mundane_name in self.unions: return self.unions[mundane_name] + elif mundane_name in self.aliases: + return self.aliases[mundane_name] raise ParseError( "Could not find type, enum or union by magic name `%s' nor by " "mundane name `%s'" % (name, mundane_name)) diff --git a/test/ext/fake.api.json b/test/ext/fake.api.json index 4a7c64e7dfd..24c9f4dbfa1 100644 --- a/test/ext/fake.api.json +++ b/test/ext/fake.api.json @@ -43,5 +43,7 @@ {"crc" : "0xcafebafe"} ] ], + "aliases" : { + }, "vl_api_version" :"0x224c7aad" } -- 2.16.6