diff options
author | Ole Troan <ot@cisco.com> | 2017-10-12 13:06:35 +0200 |
---|---|---|
committer | Neale Ranns <nranns@cisco.com> | 2018-01-23 13:03:53 +0000 |
commit | 9d42087149a6870965896be74dc6260f72d2cac9 (patch) | |
tree | f86ed97a4b28845934aeb5fbc9dd4e33b9bdfdac /src/tools/vppapigen/C.py | |
parent | 2aa22909c70ff5c5eed6a7f7a0f8a587c9260da8 (diff) |
VPPAPIGEN: vppapigen replacement in Python PLY.
This is a version of the VPP API generator in Python PLY. It supports
the existing language, and has a plugin architecture for generators.
Currently C and JSON are supported.
Changes:
- vl_api_version to option version = "major.minor.patch"
- enum support
- Added error checking and reporting
- import support (removed the C pre-processor)
- services (tying request/reply together)
Version:
option version = "1.0.0";
Enum:
enum colours {
RED,
BLUE = 50,
};
define foo {
vl_api_colours_t colours;
};
Services:
service {
rpc foo returns foo_reply;
rpc foo_dump returns stream foo_details;
rpc want_stats returns want_stats_reply
events ip4_counters, ip6_counters;
};
Future planned features:
- unions
- bool, text
- array support (including length)
- proto3 output plugin
- Refactor C/C++ generator as a plugin
- Refactor Java generator as a plugin
Change-Id: Ifa289966c790e1b1a8e2938a91e69331e3a58bdf
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/tools/vppapigen/C.py')
-rw-r--r-- | src/tools/vppapigen/C.py | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/src/tools/vppapigen/C.py b/src/tools/vppapigen/C.py new file mode 100644 index 00000000000..2a8930666fa --- /dev/null +++ b/src/tools/vppapigen/C.py @@ -0,0 +1,279 @@ +# C generation +import datetime +import os + +datestring = datetime.datetime.now() +input_filename = 'inputfil' +top_boilerplate = '''\ +/* + * VLIB API definitions {datestring} + * Input file: {input_filename} + * Automatically generated: please edit the input file NOT this file! + */ + +#if defined(vl_msg_id)||defined(vl_union_id) \\ + || defined(vl_printfun) ||defined(vl_endianfun) \\ + || defined(vl_api_version)||defined(vl_typedefs) \\ + || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\ + || defined(vl_api_version_tuple) +/* ok, something was selected */ +#else +#warning no content included from {input_filename} +#endif + +#define VL_API_PACKED(x) x __attribute__ ((packed)) +''' + +bottom_boilerplate = '''\ +/****** API CRC (whole file) *****/ + +#ifdef vl_api_version +vl_api_version({input_filename}, {file_crc:#08x}) + +#endif +''' + + +def msg_ids(s): + output = '''\ + +/****** Message ID / handler enum ******/ + +#ifdef vl_msg_id +''' + + for t in s['defines']: + output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \ + (t.name.upper(), t.name) + output += "#endif" + + return output + + +def msg_names(s): + output = '''\ + +/****** Message names ******/ + +#ifdef vl_msg_name +''' + + for t in s['defines']: + dont_trace = 0 if t.dont_trace else 1 + output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace) + output += "#endif" + + return output + + +def msg_name_crc_list(s, suffix): + output = '''\ + +/****** Message name, crc list ******/ + +#ifdef vl_msg_name_crc_list +''' + output += "#define foreach_vl_msg_name_crc_%s " % suffix + + for t in s['defines']: + output += "\\\n_(VL_API_%s, %s, %08x) " % \ + (t.name.upper(), t.name, t.crc) + output += "\n#endif" + + return output + + +def duplicate_wrapper_head(name): + s = "#ifndef defined_%s\n" % name + s += "#define defined_%s\n" % name + return s + + +def duplicate_wrapper_tail(): + return '#endif\n\n' + + +def typedefs(s, filename): + name = filename.replace('.', '_') + output = '''\ + + +/****** Typedefs ******/ + +#ifdef vl_typedefs +#ifndef included_{module} +#define included_{module} +''' + output = output.format(module=name) + for e in s['enums']: + output += duplicate_wrapper_head(e.name) + output += "typedef enum {\n" + for b in e.block: + output += " %s = %s,\n" % (b[0], b[1]) + output += '} vl_api_%s_t;\n' % e.name + output += duplicate_wrapper_tail() + + for t in s['typedefs'] + s['defines']: + output += duplicate_wrapper_head(t.name) + output += "typedef VL_API_PACKED(struct _vl_api_%s {\n" % t.name + for b in t.block: + if b.type == 'Field': + output += " %s %s;\n" % (b.fieldtype, b.fieldname) + elif b.type == 'Array': + if b.lengthfield: + output += " %s %s[0];\n" % (b.fieldtype, b.fieldname) + else: + output += " %s %s[%s];\n" % (b.fieldtype, b.fieldname, + b.length) + else: + raise ValueError("Error in processing array type %s" % b) + + output += '}) vl_api_%s_t;\n' % t.name + output += duplicate_wrapper_tail() + + output += "\n#endif" + output += "\n#endif\n\n" + + return output + + +format_strings = {'u8': '%u', + 'i8': '%d', + 'u16': '%u', + 'i16': '%d', + 'u32': '%u', + 'i32': '%ld', + 'u64': '%llu', + 'i64': '%llu', + 'f64': '%.2f', } + + +def printfun(s): + output = '''\ +/****** Print functions *****/ +#ifdef vl_printfun + +#ifdef LP64 +#define _uword_fmt \"%lld\" +#define _uword_cast (long long) +#else +#define _uword_fmt \"%ld\" +#define _uword_cast long +#endif + +''' + for t in s['typedefs'] + s['defines']: + if t.manual_print: + output += "/***** manual: vl_api_%s_t_print *****/\n\n" % t.name + continue + output += duplicate_wrapper_head(t.name + '_t_print') + output += "static inline void *vl_api_%s_t_print (vl_api_%s_t *a," % \ + (t.name, t.name) + output += "void *handle)\n{\n" + output += " vl_print(handle, \"vl_api_%s_t:\\n\");\n" % t.name + + for o in t.block: + if o.type != 'Field': + continue + if o.fieldtype in format_strings: + output += " vl_print(handle, \"%s: %s\\n\", a->%s);\n" % \ + (o.fieldname, format_strings[o.fieldtype], + o.fieldname) + + output += ' return handle;\n' + output += '}\n\n' + output += duplicate_wrapper_tail() + + output += "\n#endif /* vl_printfun */\n" + + return output + + +endian_strings = { + 'u16': 'clib_net_to_host_u16', + 'u32': 'clib_net_to_host_u32', + 'u64': 'clib_net_to_host_u64', + 'i16': 'clib_net_to_host_u16', + 'i32': 'clib_net_to_host_u32', + 'i64': 'clib_net_to_host_u64', +} + + +def endianfun(s): + output = '''\ + +/****** Endian swap functions *****/\n\ +#ifdef vl_endianfun + +#undef clib_net_to_host_uword +#ifdef LP64 +#define clib_net_to_host_uword clib_net_to_host_u64 +#else +#define clib_net_to_host_uword clib_net_to_host_u32 +#endif + +''' + + for t in s['typedefs'] + s['defines']: + if t.manual_endian: + output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name + continue + output += duplicate_wrapper_head(t.name + '_t_endian') + output += "static inline void vl_api_%s_t_endian (vl_api_%s_t *a)" % \ + (t.name, t.name) + output += "\n{\n" + + for o in t.block: + if o.type != 'Field': + continue + if o.fieldtype in endian_strings: + output += " a->%s = %s(a->%s);\n" % \ + (o.fieldname, endian_strings[o.fieldtype], o.fieldname) + else: + output += " /* a->%s = a->%s (no-op) */\n" % \ + (o.fieldname, o.fieldname) + + output += '}\n\n' + output += duplicate_wrapper_tail() + output += "\n#endif /* vl_endianfun */\n\n" + + return output + + +def version_tuple(s, module): + output = '''\ +/****** Version tuple *****/ + +#ifdef vl_api_version_tuple + +''' + if 'version' in s['options']: + v = s['options']['version'] + (major, minor, patch) = v.split('.') + output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \ + (module, major, minor, patch) + + output += "\n#endif /* vl_api_version_tuple */\n\n" + + return output + + +# +# Plugin entry point +# +def run(input_filename, s, file_crc): + basename = os.path.basename(input_filename) + filename, file_extension = os.path.splitext(basename) + output = top_boilerplate.format(datestring=datestring, + input_filename=basename) + output += msg_ids(s) + output += msg_names(s) + output += msg_name_crc_list(s, filename) + output += typedefs(s, filename + file_extension) + output += printfun(s) + output += endianfun(s) + output += version_tuple(s, basename) + output += bottom_boilerplate.format(input_filename=basename, + file_crc=file_crc) + + return output |