X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Ftools%2Fvppapigen%2Fvppapigen_c.py;h=07975ce23b4196f519bdaed6b7dceb39823dcf1f;hb=148c7b768;hp=7c383c27472de0273fda3cc113fb0f3f2f05d732;hpb=75761b933f3e28494bae4b2cf5636a07ffe6ce18;p=vpp.git diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py index 7c383c27472..07975ce23b4 100644 --- a/src/tools/vppapigen/vppapigen_c.py +++ b/src/tools/vppapigen/vppapigen_c.py @@ -4,6 +4,7 @@ import os import time import sys from io import StringIO +import shutil datestring = datetime.datetime.utcfromtimestamp( int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))) @@ -95,80 +96,16 @@ def api2c(fieldtype): return fieldtype -def typedefs(objs, filename): - name = filename.replace('.', '_') - output = '''\ +def typedefs(filename): + output = '''\ /****** Typedefs ******/ #ifdef vl_typedefs -#ifndef included_{module}_typedef -#define included_{module}_typedef -''' - output = output.format(module=name) - - for o in objs: - tname = o.__class__.__name__ - if tname == 'Using': - if 'length' in o.alias: - output += 'typedef %s vl_api_%s_t[%s];\n' % (o.alias['type'], o.name, o.alias['length']) - else: - output += 'typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name) - elif tname == 'Enum': - if o.enumtype == 'u32': - output += "typedef enum {\n" - else: - output += "typedef enum __attribute__((__packed__)) {\n" - - for b in o.block: - output += " %s = %s,\n" % (b[0], b[1]) - output += '} vl_api_%s_t;\n' % o.name - if o.enumtype != 'u32': - size1 = 'sizeof(vl_api_%s_t)' % o.name - size2 = 'sizeof(%s)' % o.enumtype - err_str = 'size of API enum %s is wrong' % o.name - output += ('STATIC_ASSERT(%s == %s, "%s");\n' - % (size1, size2, err_str)) - else: - if tname == 'Union': - output += "typedef VL_API_PACKED(union _vl_api_%s {\n" % o.name - else: - output += ("typedef VL_API_PACKED(struct _vl_api_%s {\n" - % o.name) - for b in o.block: - if b.type == 'Option': - continue - if b.type == 'Field': - output += " %s %s;\n" % (api2c(b.fieldtype), - b.fieldname) - elif b.type == 'Array': - if b.lengthfield: - output += " %s %s[0];\n" % (api2c(b.fieldtype), - b.fieldname) - else: - # Fixed length strings decay to nul terminated u8 - if b.fieldtype == 'string': - if b.modern_vla: - output += (' {} {};\n' - .format(api2c(b.fieldtype), - b.fieldname)) - else: - output += (' u8 {}[{}];\n' - .format(b.fieldname, b.length)) - else: - output += (" %s %s[%s];\n" % - (api2c(b.fieldtype), b.fieldname, - b.length)) - else: - raise ValueError("Error in processing type {} for {}" - .format(b, o.name)) - - output += '}) vl_api_%s_t;\n' % o.name - - output += "\n#endif" - output += "\n#endif\n\n" - +#include "{include}.api_types.h" +#endif +'''.format(include=filename) return output @@ -180,7 +117,7 @@ format_strings = {'u8': '%u', 'u32': '%u', 'i32': '%ld', 'u64': '%llu', - 'i64': '%llu', + 'i64': '%lld', 'f64': '%.2f'} noprint_fields = {'_vl_msg_id': None, @@ -199,10 +136,9 @@ class Printfun(): if o.modern_vla: write(' if (vl_api_string_len(&a->{f}) > 0) {{\n' .format(f=o.fieldname)) - write(' s = format(s, "\\n%U{f}: %.*s", ' + write(' s = format(s, "\\n%U{f}: %U", ' 'format_white_space, indent, ' - 'vl_api_string_len(&a->{f}) - 1, ' - 'vl_api_from_api_string(&a->{f}));\n'.format(f=o.fieldname)) + 'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname)) write(' } else {\n') write(' s = format(s, "\\n%U{f}:", ' 'format_white_space, indent);\n'.format(f=o.fieldname)) @@ -422,10 +358,10 @@ 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', - 'f64': 'clib_net_to_host_u64', + 'i16': 'clib_net_to_host_i16', + 'i32': 'clib_net_to_host_i32', + 'i64': 'clib_net_to_host_i64', + 'f64': 'clib_net_to_host_f64', } @@ -458,6 +394,7 @@ def endianfun_array(o): name=o.fieldname)) return output +no_endian_conversion = {'client_index': None} def endianfun_obj(o): output = '' @@ -467,6 +404,9 @@ def endianfun_obj(o): output += (' s = format(s, "\\n{} {} {} (print not implemented");\n' .format(o.type, o.fieldtype, o.fieldname)) return output + if o.fieldname in no_endian_conversion: + output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname) + return output if o.fieldtype in endian_strings: output += (' a->{name} = {format}(a->{name});\n' .format(name=o.fieldname, @@ -521,7 +461,6 @@ static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a) output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name continue - if t.__class__.__name__ == 'Using': output += signature.format(name=t.name) if ('length' in t.alias and t.alias['length'] and @@ -566,14 +505,358 @@ def version_tuple(s, module): return output +def generate_include_enum(s, module, stream): + write = stream.write + + if len(s['Define']): + write('typedef enum {\n') + for t in s['Define']: + write(' VL_API_{},\n'.format(t.name.upper())) + write(' VL_MSG_{}_LAST\n'.format(module.upper())) + write('}} vl_api_{}_enum_t;\n'.format(module)) + + +def generate_include_counters(s, module, stream): + write = stream.write + + for counters in s: + csetname = counters.name + write('typedef enum {\n') + for c in counters.block: + write(' {}_ERROR_{},\n' + .format(csetname.upper(), c['name'].upper())) + write(' {}_N_ERROR\n'.format(csetname.upper())) + write('}} vl_counter_{}_enum_t;\n'.format(csetname)) + + # write('extern char *{}_error_strings[];\n'.format(csetname)) + # write('extern char *{}_description_strings[];\n'.format(csetname)) + write('extern vl_counter_t {}_error_counters[];\n'.format(csetname)) + +# +# Generate separate API _types file. +# +def generate_include_types(s, module, stream): + write = stream.write + + write('#ifndef included_{module}_api_types_h\n'.format(module=module)) + write('#define included_{module}_api_types_h\n'.format(module=module)) + + if 'version' in s['Option']: + v = s['Option']['version'] + (major, minor, patch) = v.split('.') + write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'.format(m=module.upper(), v=major)) + write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'.format(m=module.upper(), v=minor)) + write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'.format(m=module.upper(), v=patch)) + + if len(s['Import']): + write('/* Imported API files */\n') + for i in s['Import']: + filename = i.filename.replace('plugins/', '') + write('#include <{}_types.h>\n'.format(filename)) + + for o in s['types'] + s['Define']: + tname = o.__class__.__name__ + if tname == 'Using': + if 'length' in o.alias: + write('typedef %s vl_api_%s_t[%s];\n' % (o.alias['type'], o.name, o.alias['length'])) + else: + write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name)) + elif tname == 'Enum': + if o.enumtype == 'u32': + write("typedef enum {\n") + else: + write("typedef enum __attribute__((packed)) {\n") + + for b in o.block: + write(" %s = %s,\n" % (b[0], b[1])) + write('} vl_api_%s_t;\n' % o.name) + if o.enumtype != 'u32': + size1 = 'sizeof(vl_api_%s_t)' % o.name + size2 = 'sizeof(%s)' % o.enumtype + err_str = 'size of API enum %s is wrong' % o.name + write('STATIC_ASSERT(%s == %s, "%s");\n' + % (size1, size2, err_str)) + else: + if tname == 'Union': + write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name) + else: + write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") + % o.name) + for b in o.block: + if b.type == 'Option': + continue + if b.type == 'Field': + write(" %s %s;\n" % (api2c(b.fieldtype), + b.fieldname)) + elif b.type == 'Array': + if b.lengthfield: + write(" %s %s[0];\n" % (api2c(b.fieldtype), + b.fieldname)) + else: + # Fixed length strings decay to nul terminated u8 + if b.fieldtype == 'string': + if b.modern_vla: + write(' {} {};\n' + .format(api2c(b.fieldtype), + b.fieldname)) + else: + write(' u8 {}[{}];\n' + .format(b.fieldname, b.length)) + else: + write(" %s %s[%s];\n" % + (api2c(b.fieldtype), b.fieldname, + b.length)) + else: + raise ValueError("Error in processing type {} for {}" + .format(b, o.name)) + + write('} vl_api_%s_t;\n' % o.name) + + for t in s['Define']: + write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n' + .format(n=t.name, ID=t.name.upper(), crc=t.crc)) + + write("\n#endif\n") + + +def generate_c_boilerplate(services, defines, counters, file_crc, + module, stream): + write = stream.write + define_hash = {d.name: d for d in defines} + + hdr = '''\ +#define vl_endianfun /* define message structures */ +#include "{module}.api.h" +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include "{module}.api.h" +#undef vl_printfun + +''' + + write(hdr.format(module=module)) + write('static u16\n') + write('setup_message_id_table (void) {\n') + write(' api_main_t *am = my_api_main;\n') + write(' vl_msg_api_msg_config_t c;\n') + write(' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", VL_MSG_{m}_LAST);\n' + .format(module, crc=file_crc, m=module.upper())) + + + for d in defines: + write(' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n' + ' VL_API_{ID} + msg_id_base);\n' + .format(n=d.name, ID=d.name.upper(), crc=d.crc)) + for s in services: + d = define_hash[s.caller] + write(' c = (vl_msg_api_msg_config_t) {{.id = VL_API_{ID} + msg_id_base,\n' + ' .name = "{n}",\n' + ' .handler = vl_api_{n}_t_handler,\n' + ' .cleanup = vl_noop_handler,\n' + ' .endian = vl_api_{n}_t_endian,\n' + ' .print = vl_api_{n}_t_print,\n' + ' .is_autoendian = 0}};\n' + .format(n=s.caller, ID=s.caller.upper())) + write(' vl_msg_api_config (&c);\n') + try: + d = define_hash[s.reply] + write(' c = (vl_msg_api_msg_config_t) {{.id = VL_API_{ID} + msg_id_base,\n' + ' .name = "{n}",\n' + ' .handler = 0,\n' + ' .cleanup = vl_noop_handler,\n' + ' .endian = vl_api_{n}_t_endian,\n' + ' .print = vl_api_{n}_t_print,\n' + ' .is_autoendian = 0}};\n' + .format(n=s.reply, ID=s.reply.upper())) + write(' vl_msg_api_config (&c);\n') + except KeyError: + pass + + write(' return msg_id_base;\n') + write('}\n') + + severity = {'error': 'VL_COUNTER_SEVERITY_ERROR', + 'info': 'VL_COUNTER_SEVERITY_INFO', + 'warn': 'VL_COUNTER_SEVERITY_WARN'} + + for cnt in counters: + csetname = cnt.name + ''' + write('char *{}_error_strings[] = {{\n'.format(csetname)) + for c in cnt.block: + write(' "{}",\n'.format(c['name'])) + write('};\n') + write('char *{}_description_strings[] = {{\n'.format(csetname)) + for c in cnt.block: + write(' "{}",\n'.format(c['description'])) + write('};\n') + ''' + write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname)) + for c in cnt.block: + write(' {\n') + write(' .name = "{}",\n'.format(c['name'])) + write(' .desc = "{}",\n'.format(c['description'])) + write(' .severity = {},\n'.format(severity[c['severity']])) + write(' },\n') + write('};\n') + +def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream): + write = stream.write + + define_hash = {d.name:d for d in defines} + replies = {} + + hdr = '''\ +#define vl_endianfun /* define message structures */ +#include "{module}.api.h" +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include "{module}.api.h" +#undef vl_printfun + +''' + + write(hdr.format(module=module)) + for s in services: + try: + d = define_hash[s.reply] + except: + continue + if d.manual_print: + write('/* Manual definition requested for: vl_api_{n}_t_handler() */\n' + .format(n=s.reply)) + continue + if not define_hash[s.caller].autoreply: + write('/* Only autoreply is supported (vl_api_{n}_t_handler()) */\n' + .format(n=s.reply)) + continue + write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper())) + write('static void\n') + write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=s.reply)) + write(' vat_main_t * vam = {}_test_main.vat_main;\n'.format(module)) + write(' i32 retval = ntohl(mp->retval);\n') + write(' if (vam->async_mode) {\n') + write(' vam->async_errors += (retval < 0);\n') + write(' } else {\n') + write(' vam->retval = retval;\n') + write(' vam->result_ready = 1;\n') + write(' }\n') + write('}\n') + write('#endif\n') + + for e in s.events: + if define_hash[e].manual_print: + continue + write('static void\n') + write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e)) + write(' vl_print(0, "{n} event called:");\n'.format(n=e)) + write(' vl_api_{n}_t_print(mp, 0);\n'.format(n=e)) + write('}\n') + + write('static void\n') + write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n') + for s in services: + write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "{n}",\n' + ' vl_api_{n}_t_handler, vl_noop_handler,\n' + ' vl_api_{n}_t_endian, vl_api_{n}_t_print,\n' + ' sizeof(vl_api_{n}_t), 1);\n' + .format(n=s.reply, ID=s.reply.upper())) + write(' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(n=s.caller)) + try: + write(' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n' + .format(n=s.caller, help=define_hash[s.caller].options['vat_help'])) + except: + pass + + # Events + for e in s.events: + write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, "{n}",\n' + ' vl_api_{n}_t_handler, vl_noop_handler,\n' + ' vl_api_{n}_t_endian, vl_api_{n}_t_print,\n' + ' sizeof(vl_api_{n}_t), 1);\n' + .format(n=e, ID=e.upper())) + + write('}\n') + if plugin: + write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n') + else: + write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'.format(module)) + write('{\n') + write(' {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module)) + write(' mainp->vat_main = vam;\n') + write(' mainp->msg_id_base = vl_client_get_first_plugin_msg_id ("{n}_{crc:08x}");\n' + .format(n=module, crc=file_crc)) + write(' if (mainp->msg_id_base == (u16) ~0)\n') + write(' return clib_error_return (0, "{} plugin not loaded...");\n'.format(module)) + write(' setup_message_id_table (vam, mainp->msg_id_base);\n') + write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n') + write(' VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n') + write('#endif\n') + write(' return 0;\n') + write('}\n') + # # Plugin entry point # -def run(input_filename, s): +def run(args, input_filename, s): stream = StringIO() + + if not args.outputdir: + sys.stderr.write('Missing --outputdir argument') + return None + basename = os.path.basename(input_filename) filename, file_extension = os.path.splitext(basename) modulename = filename.replace('.', '_') + filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h') + filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h') + filename_c = os.path.join(args.outputdir + '/' + basename + '.c') + filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c') + + # Generate separate types file + st = StringIO() + generate_include_types(s, modulename, st) + with open (filename_types, 'w') as fd: + st.seek (0) + shutil.copyfileobj (st, fd) + st.close() + + # Generate separate enum file + st = StringIO() + st.write('#ifndef included_{}_api_enum_h\n'.format(modulename)) + st.write('#define included_{}_api_enum_h\n'.format(modulename)) + generate_include_enum(s, modulename, st) + generate_include_counters(s['Counters'], modulename, st) + st.write('#endif\n') + with open (filename_enum, 'w') as fd: + st.seek (0) + shutil.copyfileobj (st, fd) + st.close() + + # Generate separate C file + st = StringIO() + generate_c_boilerplate(s['Service'], s['Define'], s['Counters'], + s['file_crc'], modulename, st) + with open (filename_c, 'w') as fd: + st.seek (0) + shutil.copyfileobj(st, fd) + st.close() + + # Generate separate C test file + st = StringIO() + plugin = True if 'plugin' in input_filename else False + generate_c_test_boilerplate(s['Service'], s['Define'], s['file_crc'], + modulename, plugin, st) + with open (filename_c_test, 'w') as fd: + st.seek (0) + shutil.copyfileobj(st, fd) + st.close() output = top_boilerplate.format(datestring=datestring, input_filename=basename) @@ -581,10 +864,11 @@ def run(input_filename, s): 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(modulename) printfun_types(s['types'], stream, modulename) printfun(s['Define'], stream, modulename) output += stream.getvalue() + stream.close() output += endianfun(s['types'] + s['Define'], modulename) output += version_tuple(s, basename) output += bottom_boilerplate.format(input_filename=basename,