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 <ot@cisco.com>
Signed-off-by: Klement Sekera <ksekera@cisco.com>
diff --git a/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py b/extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py
index 299796b..3c2db15 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 @@
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 @@
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 @@
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 9d04ec2..3f882c4 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -151,6 +151,25 @@
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 @@
'''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 @@
s['Service'] = []
s['types'] = []
s['Import'] = []
+ s['Alias'] = {}
for o in objs:
tname = o.__class__.__name__
if isinstance(o, Define):
@@ -608,6 +632,8 @@
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 @@
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 b56e072..2a66ff3 100644
--- a/src/tools/vppapigen/vppapigen_c.py
+++ b/src/tools/vppapigen/vppapigen_c.py
@@ -94,7 +94,7 @@
return '#endif\n\n'
-def typedefs(objs, filename):
+def typedefs(objs, aliases, filename):
name = filename.replace('.', '_')
output = '''\
@@ -106,6 +106,15 @@
#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 @@
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 2991bec..b57e16c 100644
--- a/src/tools/vppapigen/vppapigen_json.py
+++ b/src/tools/vppapigen/vppapigen_json.py
@@ -65,5 +65,6 @@
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 2010d8b..84e0483 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 0000000..f6fb421
--- /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 5cad28f..3dc087c 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 @@
{
u32 context;
i32 retval;
- u32 sw_if_index;
+ vl_api_interface_index_t sw_if_index;
};
/**
@@ -80,7 +81,7 @@
{
u32 client_index;
u32 context;
- u32 sw_if_index;
+ vl_api_interface_index_t sw_if_index;
};
/**
@@ -106,7 +107,7 @@
{
u32 context;
i32 retval;
- u32 sw_if_index;
+ vl_api_interface_index_t sw_if_index;
};
/**
@@ -116,7 +117,7 @@
{
u32 client_index;
u32 context;
- u32 sw_if_index;
+ vl_api_interface_index_t sw_if_index;
};
/**
@@ -126,13 +127,13 @@
{
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 ca4b955..bd2682f 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 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 @@
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 @@
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 bd0f738..a001cca 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 @@
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 @@
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 eb1006d..9939bc0 100755
--- a/src/vpp-api/vapi/vapi_c_gen.py
+++ b/src/vpp-api/vapi/vapi_c_gen.py
@@ -5,15 +5,18 @@
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 @@
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 @@
try:
self.get_swap_to_host_func_name()
return True
- except:
+ except KeyError:
pass
return False
@@ -335,8 +352,8 @@
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 @@
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 @@
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 @@
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 6b62bc4..c08993d 100755
--- a/src/vpp-api/vapi/vapi_cpp_gen.py
+++ b/src/vpp-api/vapi/vapi_cpp_gen.py
@@ -5,7 +5,7 @@
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 @@
pass
+class CppAlias(CAlias):
+ pass
+
+
class CppSimpleType (CSimpleType):
pass
@@ -251,7 +255,8 @@
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 39acca0..3eb5d36 100644
--- a/src/vpp-api/vapi/vapi_json_parser.py
+++ b/src/vpp-api/vapi/vapi_json_parser.py
@@ -45,6 +45,10 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 4a7c64e..24c9f4d 100644
--- a/test/ext/fake.api.json
+++ b/test/ext/fake.api.json
@@ -43,5 +43,7 @@
{"crc" : "0xcafebafe"}
]
],
+ "aliases" : {
+ },
"vl_api_version" :"0x224c7aad"
}