aboutsummaryrefslogtreecommitdiffstats
path: root/src/tools/vppapigen/C.py
diff options
context:
space:
mode:
authorOle Troan <ot@cisco.com>2017-10-12 13:06:35 +0200
committerNeale Ranns <nranns@cisco.com>2018-01-23 13:03:53 +0000
commit9d42087149a6870965896be74dc6260f72d2cac9 (patch)
treef86ed97a4b28845934aeb5fbc9dd4e33b9bdfdac /src/tools/vppapigen/C.py
parent2aa22909c70ff5c5eed6a7f7a0f8a587c9260da8 (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.py279
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