2 # Copyright (c) 2020 Cisco and/or its affiliates.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at:
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
22 This module creates C code for core VPP, VPP plugins and client side VAT and
31 from io import StringIO
34 process_imports = False
37 ###############################################################################
39 '''Class to generate functions converting from VPP binary API to JSON.'''
41 noprint_fields = {'_vl_msg_id': None,
44 is_number = {'u8': None,
55 def __init__(self, module, types, defines, imported_types, stream):
58 self.defines = defines
60 self.types_hash = {'vl_api_'+d.name+'_t':
61 d for d in types + imported_types}
62 self.defines_hash = {d.name: d for d in defines}
65 '''Output the top boilerplate.'''
66 write = self.stream.write
67 write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
68 write('#define included_{}_api_tojson_h\n'.format(self.module))
69 write('#include <vppinfra/cJSON.h>\n\n')
70 write('#include <vppinfra/jsonformat.h>\n\n')
71 if self.module == 'interface_types':
72 write('#define vl_printfun\n')
73 write('#include <vnet/interface_types.api.h>\n\n')
76 '''Output the bottom boilerplate.'''
77 write = self.stream.write
80 def get_base_type(self, t):
83 vt = self.types_hash[t]
84 if vt.type == 'Using' and 'length' not in vt.alias:
85 vt_type = vt.alias['type']
90 def get_json_func(self, t):
91 '''Given the type, returns the function to use to create a
93 vt, vt_type = self.get_base_type(t)
95 if t in self.is_number or vt_type in self.is_number:
96 return 'cJSON_AddNumberToObject', '', False
98 return 'cJSON_AddBoolToObject', '', False
100 # Lookup type name check if it's enum
101 if vt.type == 'Enum' or vt.type == 'EnumFlag':
102 return '{t}_tojson'.format(t=t), '', True
103 return '{t}_tojson'.format(t=t), '&', True
105 def get_json_array_func(self, t):
106 '''Given a type returns the function to create a cJSON object
108 if t in self.is_number:
109 return 'cJSON_CreateNumber', ''
111 return 'cJSON_CreateBool', ''
112 vt, vt_type = self.get_base_type(t)
113 if vt.type == 'Enum' or vt.type == 'EnumFlag':
114 return '{t}_tojson'.format(t=t), ''
115 return '{t}_tojson'.format(t=t), '&'
117 def print_string(self, o):
118 '''Create cJSON object from vl_api_string_t'''
119 write = self.stream.write
121 write(' vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
122 .format(n=o.fieldname))
125 write(' cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
126 .format(n=o.fieldname))
128 def print_field(self, o):
129 '''Called for every field in a typedef or define.'''
130 write = self.stream.write
131 if o.fieldname in self.noprint_fields:
134 f, p, newobj = self.get_json_func(o.fieldtype)
137 write(' cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
138 .format(f=f, p=p, n=o.fieldname))
140 write(' {f}(o, "{n}", {p}a->{n});\n'
141 .format(f=f, p=p, n=o.fieldname))
143 _dispatch['Field'] = print_field
145 def print_array(self, o):
146 '''Converts a VPP API array to cJSON array.'''
147 write = self.stream.write
152 cJSON *array = cJSON_AddArrayToObject(o, "{n}");
153 for (i = 0; i < {lfield}; i++) {{
154 cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
159 if o.fieldtype == 'string':
163 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
164 if o.fieldtype == 'u8':
166 # What is length field doing here?
167 write(' u8 *s = format(0, "0x%U", format_hex_bytes, '
168 '&a->{n}, {lfield});\n'
169 .format(n=o.fieldname, lfield=lfield))
170 write(' cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
171 .format(n=o.fieldname))
172 write(' vec_free(s);\n')
176 f, p = self.get_json_array_func(o.fieldtype)
177 write(forloop.format(lfield=lfield,
183 _dispatch['Array'] = print_array
185 def print_enum(self, o):
186 '''Create cJSON object (string) for VPP API enum'''
187 write = self.stream.write
188 write('static inline cJSON *vl_api_{name}_t_tojson '
189 '(vl_api_{name}_t a) {{\n'.format(name=o.name))
191 write(" switch(a) {\n")
193 write(" case %s:\n" % b[1])
194 write(' return cJSON_CreateString("{}");\n'.format(b[0]))
195 write(' default: return cJSON_CreateString("Invalid ENUM");\n')
197 write(' return 0;\n')
200 _dispatch['Enum'] = print_enum
202 def print_enum_flag(self, o):
203 '''Create cJSON object (string) for VPP API enum'''
204 write = self.stream.write
205 write('static inline cJSON *vl_api_{name}_t_tojson '
206 '(vl_api_{name}_t a) {{\n'.format(name=o.name))
207 write(' cJSON *array = cJSON_CreateArray();\n')
212 write(' if (a & {})\n'.format(b[0]))
213 write(' cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
214 write(' return array;\n')
217 _dispatch['EnumFlag'] = print_enum_flag
219 def print_typedef(self, o):
220 '''Create cJSON (dictionary) object from VPP API typedef'''
221 write = self.stream.write
222 write('static inline cJSON *vl_api_{name}_t_tojson '
223 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
224 write(' cJSON *o = cJSON_CreateObject();\n')
227 self._dispatch[t.type](self, t)
229 write(' return o;\n')
232 def print_define(self, o):
233 '''Create cJSON (dictionary) object from VPP API define'''
234 write = self.stream.write
235 write('static inline cJSON *vl_api_{name}_t_tojson '
236 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
237 write(' cJSON *o = cJSON_CreateObject();\n')
238 write(' cJSON_AddStringToObject(o, "_msgname", "{}");\n'
240 write(' cJSON_AddStringToObject(o, "_crc", "{crc:08x}");\n'
244 self._dispatch[t.type](self, t)
246 write(' return o;\n')
249 def print_using(self, o):
250 '''Create cJSON (dictionary) object from VPP API aliased type'''
254 write = self.stream.write
255 write('static inline cJSON *vl_api_{name}_t_tojson '
256 '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
258 write(' u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
260 write(' cJSON *o = cJSON_CreateString((char *)s);\n')
261 write(' vec_free(s);\n')
262 write(' return o;\n')
265 _dispatch['Typedef'] = print_typedef
266 _dispatch['Define'] = print_define
267 _dispatch['Using'] = print_using
268 _dispatch['Union'] = print_typedef
270 def generate_function(self, t):
271 '''Main entry point'''
272 write = self.stream.write
274 write('/* Manual print {} */\n'.format(t.name))
276 self._dispatch[t.type](self, t)
278 def generate_types(self):
279 '''Main entry point'''
281 self.generate_function(t)
283 def generate_defines(self):
284 '''Main entry point'''
285 for t in self.defines:
286 self.generate_function(t)
291 Parse JSON objects into VPP API binary message structures.
294 noprint_fields = {'_vl_msg_id': None,
295 'client_index': None,
297 is_number = {'u8': None,
308 def __init__(self, module, types, defines, imported_types, stream):
311 self.defines = defines
313 self.types_hash = {'vl_api_'+d.name+'_t':
314 d for d in types + imported_types}
315 self.defines_hash = {d.name: d for d in defines}
318 '''Output the top boilerplate.'''
319 write = self.stream.write
320 write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
321 write('#define included_{}_api_fromjson_h\n'.format(self.module))
322 write('#include <vppinfra/cJSON.h>\n\n')
323 write('#include <vppinfra/jsonformat.h>\n\n')
324 write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
326 def is_base_type(self, t):
327 '''Check if a type is one of the VPP API base types'''
328 if t in self.is_number:
335 '''Output the bottom boilerplate.'''
336 write = self.stream.write
339 def print_string(self, o, toplevel=False):
340 '''Convert JSON string to vl_api_string_t'''
341 write = self.stream.write
343 msgvar = "a" if toplevel else "*mp"
344 msgsize = "l" if toplevel else "*len"
347 write(' char *p = cJSON_GetStringValue(item);\n')
348 write(' size_t plen = strlen(p);\n')
349 write(' {msgvar} = cJSON_realloc({msgvar}, {msgsize} + plen, {msgsize});\n'
350 .format(msgvar=msgvar, msgsize=msgsize))
351 write(' if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
352 write(' vl_api_c_string_to_api_string(p, (void *){msgvar} + '
353 '{msgsize} - sizeof(vl_api_string_t));\n'
354 .format(msgvar=msgvar, msgsize=msgsize))
355 write(' {msgsize} += plen;\n'.format(msgsize=msgsize))
357 write(' strncpy_s((char *)a->{n}, sizeof(a->{n}), '
358 'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
359 .format(n=o.fieldname))
361 def print_field(self, o, toplevel=False):
362 '''Called for every field in a typedef or define.'''
363 write = self.stream.write
364 if o.fieldname in self.noprint_fields:
366 is_bt = self.is_base_type(o.fieldtype)
367 t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
369 msgvar = "(void **)&a" if toplevel else "mp"
370 msgsize = "&l" if toplevel else "len"
373 write(' vl_api_{t}_fromjson(item, &a->{n});\n'
374 .format(t=o.fieldtype, n=o.fieldname))
376 write(' if ({t}_fromjson({msgvar}, '
377 '{msgsize}, item, &a->{n}) < 0) goto error;\n'
378 .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
380 _dispatch['Field'] = print_field
382 def print_array(self, o, toplevel=False):
383 '''Convert JSON array to VPP API array'''
384 write = self.stream.write
389 cJSON *array = cJSON_GetObjectItem(o, "{n}");
390 int size = cJSON_GetArraySize(array);
391 if (size != {lfield}) goto error;
392 for (i = 0; i < size; i++) {{
393 cJSON *e = cJSON_GetArrayItem(array, i);
401 cJSON *array = cJSON_GetObjectItem(o, "{n}");
402 int size = cJSON_GetArraySize(array);
404 {realloc} = cJSON_realloc({realloc}, {msgsize} + sizeof({t}) * size, {msgsize});
405 {t} *d = (void *){realloc} + {msgsize};
406 {msgsize} += sizeof({t}) * size;
407 for (i = 0; i < size; i++) {{
408 cJSON *e = cJSON_GetArrayItem(array, i);
414 if o.fieldtype == 'string':
415 self.print_string(o, toplevel)
418 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
419 msgvar = "(void **)&a" if toplevel else "mp"
420 realloc = "a" if toplevel else "*mp"
421 msgsize = "l" if toplevel else "*len"
423 if o.fieldtype == 'u8':
425 write(' s = u8string_fromjson(o, "{}");\n'
426 .format(o.fieldname))
427 write(' if (!s) goto error;\n')
428 write(' {} = vec_len(s);\n'.format(lfield))
430 write(' {realloc} = cJSON_realloc({realloc}, {msgsize} + '
431 'vec_len(s), {msgsize});\n'.format(msgvar=msgvar, msgsize=msgsize, realloc=realloc))
432 write(' memcpy((void *){realloc} + {msgsize}, s, '
433 'vec_len(s));\n'.format(realloc=realloc, msgsize=msgsize))
434 write(' {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
436 write(' vec_free(s);\n')
438 write(' if (u8string_fromjson2(o, "{n}", a->{n}) < 0) goto error;\n'
439 .format(n=o.fieldname))
442 is_bt = self.is_base_type(o.fieldtype)
446 call = ('vl_api_{t}_fromjson(e, &d[i]);'
447 .format(t=o.fieldtype))
449 call = ('if ({t}_fromjson({msgvar}, len, e, &d[i]) < 0) goto error; '
450 .format(t=o.fieldtype, msgvar=msgvar))
451 write(forloop_vla.format(lfield=lfield,
459 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
460 .format(t=t, n=o.fieldname))
462 call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
463 .format(t, msgvar, o.fieldname))
464 write(forloop.format(lfield=lfield,
472 _dispatch['Array'] = print_array
474 def print_enum(self, o):
475 '''Convert to JSON enum(string) to VPP API enum (int)'''
476 write = self.stream.write
477 write('static inline int vl_api_{n}_t_fromjson'
478 '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
480 write(' char *p = cJSON_GetStringValue(o);\n')
482 write(' if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
485 write(' return -1;\n')
488 _dispatch['Enum'] = print_enum
490 def print_enum_flag(self, o):
491 '''Convert to JSON enum(string) to VPP API enum (int)'''
492 write = self.stream.write
493 write('static inline int vl_api_{n}_t_fromjson '
494 '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
498 write(' for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
499 write(' cJSON *e = cJSON_GetArrayItem(o, i);\n')
500 write(' char *p = cJSON_GetStringValue(e);\n')
501 write(' if (!p) return -1;\n')
503 write(' if (strcmp(p, "{}") == 0) *a |= {};\n'
506 write(' return 0;\n')
509 _dispatch['EnumFlag'] = print_enum_flag
511 def print_typedef(self, o):
512 '''Convert from JSON object to VPP API binary representation'''
513 write = self.stream.write
515 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
516 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
517 .format(name=o.name))
518 write(' cJSON *item __attribute__ ((unused));\n')
519 write(' u8 *s __attribute__ ((unused));\n')
521 if t.type == 'Field' and t.is_lengthfield:
523 write('\n item = cJSON_GetObjectItem(o, "{}");\n'
524 .format(t.fieldname))
525 write(' if (!item) goto error;\n')
526 self._dispatch[t.type](self, t)
528 write('\n return 0;\n')
530 write(' return -1;\n')
533 def print_union(self, o):
534 '''Convert JSON object to VPP API binary union'''
535 write = self.stream.write
537 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
538 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
539 .format(name=o.name))
540 write(' cJSON *item __attribute__ ((unused));\n')
541 write(' u8 *s __attribute__ ((unused));\n')
543 if t.type == 'Field' and t.is_lengthfield:
545 write(' item = cJSON_GetObjectItem(o, "{}");\n'
546 .format(t.fieldname))
547 write(' if (item) {\n')
548 self._dispatch[t.type](self, t)
550 write('\n return 0;\n')
552 write(' return -1;\n')
555 def print_define(self, o):
556 '''Convert JSON object to VPP API message'''
557 write = self.stream.write
559 write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
560 '(cJSON *o, int *len) {{\n'.format(name=o.name))
561 write(' cJSON *item __attribute__ ((unused));\n')
562 write(' u8 *s __attribute__ ((unused));\n')
563 write(' int l = sizeof(vl_api_{}_t);\n'.format(o.name))
564 write(' vl_api_{}_t *a = cJSON_malloc(l);\n'.format(o.name))
568 if t.fieldname in self.noprint_fields:
570 if t.type == 'Field' and t.is_lengthfield:
572 write(' item = cJSON_GetObjectItem(o, "{}");\n'
573 .format(t.fieldname))
574 write(' if (!item) goto error;\n')
576 self._dispatch[t.type](self, t, toplevel=True)
579 write(' *len = l;\n')
580 write(' return a;\n')
584 write(' cJSON_free(a);\n')
585 write(' return 0;\n')
588 def print_using(self, o):
589 '''Convert JSON field to VPP type alias'''
590 write = self.stream.write
596 write('static inline int vl_api_{name}_t_fromjson (void **mp, '
597 'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
598 .format(name=o.name))
599 if 'length' in o.alias:
600 if t.fieldtype != 'u8':
601 raise ValueError("Error in processing type {} for {}"
602 .format(t.fieldtype, o.name))
603 write(' vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
604 .format(o.alias['length']))
606 write(' vl_api_{t}_fromjson(o, ({t} *)a);\n'
607 .format(t=t.fieldtype))
609 write(' return 0;\n')
612 _dispatch['Typedef'] = print_typedef
613 _dispatch['Define'] = print_define
614 _dispatch['Using'] = print_using
615 _dispatch['Union'] = print_union
617 def generate_function(self, t):
618 '''Main entry point'''
619 write = self.stream.write
621 write('/* Manual print {} */\n'.format(t.name))
623 self._dispatch[t.type](self, t)
625 def generate_types(self):
626 '''Main entry point'''
628 self.generate_function(t)
630 def generate_defines(self):
631 '''Main entry point'''
632 for t in self.defines:
633 self.generate_function(t)
636 def generate_tojson(s, modulename, stream):
637 '''Generate all functions to convert from API to JSON'''
640 write('/* Imported API files */\n')
641 for i in s['Import']:
642 f = i.filename.replace('plugins/', '')
643 write('#include <{}_tojson.h>\n'.format(f))
645 pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
649 pp.generate_defines()
654 def generate_fromjson(s, modulename, stream):
655 '''Generate all functions to convert from JSON to API'''
657 write('/* Imported API files */\n')
658 for i in s['Import']:
659 f = i.filename.replace('plugins/', '')
660 write('#include <{}_fromjson.h>\n'.format(f))
662 pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
666 pp.generate_defines()
671 ###############################################################################
674 DATESTRING = datetime.datetime.utcfromtimestamp(
675 int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
676 TOP_BOILERPLATE = '''\
678 * VLIB API definitions {datestring}
679 * Input file: {input_filename}
680 * Automatically generated: please edit the input file NOT this file!
684 #if defined(vl_msg_id)||defined(vl_union_id) \\
685 || defined(vl_printfun) ||defined(vl_endianfun) \\
686 || defined(vl_api_version)||defined(vl_typedefs) \\
687 || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
688 || defined(vl_api_version_tuple)
689 /* ok, something was selected */
691 #warning no content included from {input_filename}
694 #define VL_API_PACKED(x) x __attribute__ ((packed))
697 BOTTOM_BOILERPLATE = '''\
698 /****** API CRC (whole file) *****/
700 #ifdef vl_api_version
701 vl_api_version({input_filename}, {file_crc:#08x})
708 '''Generate macro to map API message id to handler'''
711 /****** Message ID / handler enum ******/
716 for t in s['Define']:
717 output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
718 (t.name.upper(), t.name)
725 '''Generate calls to name mapping macro'''
728 /****** Message names ******/
733 for t in s['Define']:
734 dont_trace = 0 if t.dont_trace else 1
735 output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
741 def msg_name_crc_list(s, suffix):
742 '''Generate list of names to CRC mappings'''
745 /****** Message name, crc list ******/
747 #ifdef vl_msg_name_crc_list
749 output += "#define foreach_vl_msg_name_crc_%s " % suffix
751 for t in s['Define']:
752 output += "\\\n_(VL_API_%s, %s, %08x) " % \
753 (t.name.upper(), t.name, t.crc)
759 def api2c(fieldtype):
760 '''Map between API type names and internal VPP type names'''
761 mappingtable = {'string': 'vl_api_string_t', }
762 if fieldtype in mappingtable:
763 return mappingtable[fieldtype]
767 def typedefs(filename):
768 '''Include in the main files to the types file'''
771 /****** Typedefs ******/
774 #include "{include}.api_types.h"
776 '''.format(include=filename)
780 FORMAT_STRINGS = {'u8': '%u',
793 '''Functions for pretty printing VPP API messages'''
795 noprint_fields = {'_vl_msg_id': None,
796 'client_index': None,
799 def __init__(self, stream):
803 def print_string(o, stream):
804 '''Pretty print a vl_api_string_t'''
807 write(' if (vl_api_string_len(&a->{f}) > 0) {{\n'
808 .format(f=o.fieldname))
809 write(' s = format(s, "\\n%U{f}: %U", '
810 'format_white_space, indent, '
811 'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
813 write(' s = format(s, "\\n%U{f}:", '
814 'format_white_space, indent);\n'.format(f=o.fieldname))
817 write(' s = format(s, "\\n%U{f}: %s", '
818 'format_white_space, indent, a->{f});\n'
819 .format(f=o.fieldname))
821 def print_field(self, o, stream):
822 '''Pretty print API field'''
824 if o.fieldname in self.noprint_fields:
826 if o.fieldtype in FORMAT_STRINGS:
827 f = FORMAT_STRINGS[o.fieldtype]
828 write(' s = format(s, "\\n%U{n}: {f}", '
829 'format_white_space, indent, a->{n});\n'
830 .format(n=o.fieldname, f=f))
832 write(' s = format(s, "\\n%U{n}: %U", '
833 'format_white_space, indent, '
834 'format_{t}, &a->{n}, indent);\n'
835 .format(n=o.fieldname, t=o.fieldtype))
837 _dispatch['Field'] = print_field
839 def print_array(self, o, stream):
840 '''Pretty print API array'''
844 for (i = 0; i < {lfield}; i++) {{
845 s = format(s, "\\n%U{n}: %U",
846 format_white_space, indent, format_{t}, &a->{n}[i], indent);
850 forloop_format = '''\
851 for (i = 0; i < {lfield}; i++) {{
852 s = format(s, "\\n%U{n}: {t}",
853 format_white_space, indent, a->{n}[i]);
857 if o.fieldtype == 'string':
858 self.print_string(o, stream)
861 if o.fieldtype == 'u8':
863 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
864 'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
865 .format(n=o.fieldname, lfield=o.lengthfield))
867 write(' s = format(s, "\\n%U{n}: %U", format_white_space, '
868 'indent, format_hex_bytes, a, {lfield});\n'
869 .format(n=o.fieldname, lfield=o.length))
872 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
873 if o.fieldtype in FORMAT_STRINGS:
874 write(forloop_format.format(lfield=lfield,
875 t=FORMAT_STRINGS[o.fieldtype],
878 write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
880 _dispatch['Array'] = print_array
883 def print_alias(k, v, stream):
884 '''Pretty print type alias'''
886 if ('length' in v.alias and v.alias['length'] and
887 v.alias['type'] == 'u8'):
888 write(' return format(s, "%U", format_hex_bytes, a, {});\n'
889 .format(v.alias['length']))
890 elif v.alias['type'] in FORMAT_STRINGS:
891 write(' return format(s, "{}", *a);\n'
892 .format(FORMAT_STRINGS[v.alias['type']]))
894 write(' return format(s, "{} (print not implemented)");\n'
898 def print_enum(o, stream):
899 '''Pretty print API enum'''
901 write(" switch(*a) {\n")
903 write(" case %s:\n" % b[1])
904 write(' return format(s, "{}");\n'.format(b[0]))
907 _dispatch['Enum'] = print_enum
908 _dispatch['EnumFlag'] = print_enum
910 def print_obj(self, o, stream):
914 if o.type in self._dispatch:
915 self._dispatch[o.type](self, o, stream)
917 write(' s = format(s, "\\n{} {} {} (print not implemented");\n'
918 .format(o.type, o.fieldtype, o.fieldname))
921 def printfun(objs, stream, modulename):
922 '''Main entry point for pretty print function generation'''
926 /****** Print functions *****/
928 #ifndef included_{module}_printfun
929 #define included_{module}_printfun
932 #define _uword_fmt \"%lld\"
933 #define _uword_cast (long long)
935 #define _uword_fmt \"%ld\"
936 #define _uword_cast long
939 #include "{module}.api_tojson.h"
940 #include "{module}.api_fromjson.h"
945 static inline void *vl_api_{name}_t_print{suffix} (vl_api_{name}_t *a, void *handle)
948 u32 indent __attribute__((unused)) = 2;
949 int i __attribute__((unused));
952 h = h.format(module=modulename)
955 pp = Printfun(stream)
958 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
960 write(signature.format(name=t.name, suffix=''))
961 write(' /* Message definition: vl_api_{}_t: */\n'.format(t.name))
962 write(" s = format(s, \"vl_api_%s_t:\");\n" % t.name)
964 pp.print_obj(o, stream)
965 write(' vec_add1(s, 0);\n')
966 write(' vl_print (handle, (char *)s);\n')
967 write(' vec_free (s);\n')
968 write(' return handle;\n')
971 write(signature.format(name=t.name, suffix='_json'))
972 write(' cJSON * o = vl_api_{}_t_tojson(a);\n'.format(t.name))
973 write(' (void)s;\n');
974 write(' char *out = cJSON_Print(o);\n')
975 write(' vl_print(handle, out);\n');
976 write(' cJSON_Delete(o);\n')
977 write(' cJSON_free(out);\n');
978 write(' return handle;\n')
982 write("\n#endif /* vl_printfun */\n")
987 def printfun_types(objs, stream, modulename):
988 '''Pretty print API types'''
990 pp = Printfun(stream)
993 /****** Print functions *****/
995 #ifndef included_{module}_printfun_types
996 #define included_{module}_printfun_types
999 h = h.format(module=modulename)
1003 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1005 vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1006 u32 indent __attribute__((unused)) = va_arg (*args, u32);
1007 int i __attribute__((unused));
1012 if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
1013 write(signature.format(name=t.name))
1014 pp.print_enum(t.block, stream)
1015 write(' return s;\n')
1020 write("/***** manual: vl_api_%s_t_print *****/\n\n" % t.name)
1023 if t.__class__.__name__ == 'Using':
1024 write(signature.format(name=t.name))
1025 pp.print_alias(t.name, t, stream)
1029 write(signature.format(name=t.name))
1031 pp.print_obj(o, stream)
1033 write(' return s;\n')
1037 write("\n#endif /* vl_printfun_types */\n")
1040 def generate_imports(imports):
1041 '''Add #include matching the API import statements'''
1042 output = '/* Imported API files */\n'
1043 output += '#ifndef vl_api_version\n'
1046 s = i.filename.replace('plugins/', '')
1047 output += '#include <{}.h>\n'.format(s)
1048 output += '#endif\n'
1053 'u16': 'clib_net_to_host_u16',
1054 'u32': 'clib_net_to_host_u32',
1055 'u64': 'clib_net_to_host_u64',
1056 'i16': 'clib_net_to_host_i16',
1057 'i32': 'clib_net_to_host_i32',
1058 'i64': 'clib_net_to_host_i64',
1059 'f64': 'clib_net_to_host_f64',
1063 def endianfun_array(o):
1064 '''Generate endian functions for arrays'''
1066 for (i = 0; i < {length}; i++) {{
1067 a->{name}[i] = {format}(a->{name}[i]);
1071 forloop_format = '''\
1072 for (i = 0; i < {length}; i++) {{
1073 {type}_endian(&a->{name}[i]);
1078 if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
1079 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1081 lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
1082 if o.fieldtype in ENDIAN_STRINGS:
1084 .format(length=lfield,
1085 format=ENDIAN_STRINGS[o.fieldtype],
1088 output += (forloop_format
1089 .format(length=lfield, type=o.fieldtype,
1094 NO_ENDIAN_CONVERSION = {'client_index': None}
1097 def endianfun_obj(o):
1098 '''Generate endian conversion function for type'''
1100 if o.type == 'Array':
1101 return endianfun_array(o)
1102 if o.type != 'Field':
1103 output += (' s = format(s, "\\n{} {} {} (print not implemented");\n'
1104 .format(o.type, o.fieldtype, o.fieldname))
1106 if o.fieldname in NO_ENDIAN_CONVERSION:
1107 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1109 if o.fieldtype in ENDIAN_STRINGS:
1110 output += (' a->{name} = {format}(a->{name});\n'
1111 .format(name=o.fieldname,
1112 format=ENDIAN_STRINGS[o.fieldtype]))
1113 elif o.fieldtype.startswith('vl_api_'):
1114 output += (' {type}_endian(&a->{name});\n'
1115 .format(type=o.fieldtype, name=o.fieldname))
1117 output += ' /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1122 def endianfun(objs, modulename):
1123 '''Main entry point for endian function generation'''
1126 /****** Endian swap functions *****/\n\
1128 #ifndef included_{module}_endianfun
1129 #define included_{module}_endianfun
1131 #undef clib_net_to_host_uword
1133 #define clib_net_to_host_uword clib_net_to_host_u64
1135 #define clib_net_to_host_uword clib_net_to_host_u32
1139 output = output.format(module=modulename)
1142 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1144 int i __attribute__((unused));
1148 if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag' :
1149 output += signature.format(name=t.name)
1150 if t.enumtype in ENDIAN_STRINGS:
1151 output += (' *a = {}(*a);\n'
1152 .format(ENDIAN_STRINGS[t.enumtype]))
1154 output += (' /* a->{name} = a->{name} (no-op) */\n'
1155 .format(name=t.name))
1161 output += "/***** manual: vl_api_%s_t_endian *****/\n\n" % t.name
1164 if t.__class__.__name__ == 'Using':
1165 output += signature.format(name=t.name)
1166 if ('length' in t.alias and t.alias['length'] and
1167 t.alias['type'] == 'u8'):
1168 output += (' /* a->{name} = a->{name} (no-op) */\n'
1169 .format(name=t.name))
1170 elif t.alias['type'] in FORMAT_STRINGS:
1171 output += (' *a = {}(*a);\n'
1172 .format(ENDIAN_STRINGS[t.alias['type']]))
1174 output += ' /* Not Implemented yet {} */'.format(t.name)
1178 output += signature.format(name=t.name)
1181 output += endianfun_obj(o)
1184 output += "\n#endif"
1185 output += "\n#endif /* vl_endianfun */\n\n"
1190 def version_tuple(s, module):
1191 '''Generate semantic version string'''
1193 /****** Version tuple *****/
1195 #ifdef vl_api_version_tuple
1198 if 'version' in s['Option']:
1199 v = s['Option']['version']
1200 (major, minor, patch) = v.split('.')
1201 output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1202 (module, major, minor, patch)
1204 output += "\n#endif /* vl_api_version_tuple */\n\n"
1209 def generate_include_enum(s, module, stream):
1210 '''Generate <name>.api_enum.h'''
1211 write = stream.write
1214 write('typedef enum {\n')
1215 for t in s['Define']:
1216 write(' VL_API_{},\n'.format(t.name.upper()))
1217 write(' VL_MSG_{}_LAST\n'.format(module.upper()))
1218 write('}} vl_api_{}_enum_t;\n'.format(module))
1221 def generate_include_counters(s, stream):
1222 '''Include file for the counter data model types.'''
1223 write = stream.write
1226 csetname = counters.name
1227 write('typedef enum {\n')
1228 for c in counters.block:
1229 write(' {}_ERROR_{},\n'
1230 .format(csetname.upper(), c['name'].upper()))
1231 write(' {}_N_ERROR\n'.format(csetname.upper()))
1232 write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1234 write('extern vlib_error_desc_t {}_error_counters[];\n'.format(csetname))
1237 def generate_include_types(s, module, stream):
1238 '''Generate separate API _types file.'''
1239 write = stream.write
1241 write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1242 write('#define included_{module}_api_types_h\n'.format(module=module))
1244 if 'version' in s['Option']:
1245 v = s['Option']['version']
1246 (major, minor, patch) = v.split('.')
1247 write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1248 .format(m=module.upper(), v=major))
1249 write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1250 .format(m=module.upper(), v=minor))
1251 write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1252 .format(m=module.upper(), v=patch))
1255 write('/* Imported API files */\n')
1256 for i in s['Import']:
1257 filename = i.filename.replace('plugins/', '')
1258 write('#include <{}_types.h>\n'.format(filename))
1260 for o in itertools.chain(s['types'], s['Define']):
1261 tname = o.__class__.__name__
1262 if tname == 'Using':
1263 if 'length' in o.alias:
1264 write('typedef %s vl_api_%s_t[%s];\n' %
1265 (o.alias['type'], o.name, o.alias['length']))
1267 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1268 elif tname == 'Enum' or tname == 'EnumFlag':
1269 if o.enumtype == 'u32':
1270 write("typedef enum {\n")
1272 write("typedef enum __attribute__((packed)) {\n")
1275 write(" %s = %s,\n" % (b[0], b[1]))
1276 write('} vl_api_%s_t;\n' % o.name)
1277 if o.enumtype != 'u32':
1278 size1 = 'sizeof(vl_api_%s_t)' % o.name
1279 size2 = 'sizeof(%s)' % o.enumtype
1280 err_str = 'size of API enum %s is wrong' % o.name
1281 write('STATIC_ASSERT(%s == %s, "%s");\n'
1282 % (size1, size2, err_str))
1284 if tname == 'Union':
1285 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1288 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
1291 if b.type == 'Option':
1293 if b.type == 'Field':
1294 write(" %s %s;\n" % (api2c(b.fieldtype),
1296 elif b.type == 'Array':
1298 write(" %s %s[0];\n" % (api2c(b.fieldtype),
1301 # Fixed length strings decay to nul terminated u8
1302 if b.fieldtype == 'string':
1305 .format(api2c(b.fieldtype),
1308 write(' u8 {}[{}];\n'
1309 .format(b.fieldname, b.length))
1311 write(" %s %s[%s];\n" %
1312 (api2c(b.fieldtype), b.fieldname,
1315 raise ValueError("Error in processing type {} for {}"
1318 write('} vl_api_%s_t;\n' % o.name)
1319 write(f'#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n')
1321 for t in s['Define']:
1322 write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1323 .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1328 def generate_c_boilerplate(services, defines, counters, file_crc,
1330 '''VPP side plugin.'''
1331 write = stream.write
1332 define_hash = {d.name: d for d in defines}
1335 #define vl_endianfun /* define message structures */
1336 #include "{module}.api.h"
1339 /* instantiate all the print functions we know about */
1340 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1342 #include "{module}.api.h"
1347 write(hdr.format(module=module))
1348 write('static u16\n')
1349 write('setup_message_id_table (void) {\n')
1350 write(' api_main_t *am = my_api_main;\n')
1351 write(' vl_msg_api_msg_config_t c;\n')
1352 write(' u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1353 'VL_MSG_{m}_LAST);\n'
1354 .format(module, crc=file_crc, m=module.upper()))
1357 write(' vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1358 ' VL_API_{ID} + msg_id_base);\n'
1359 .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1361 d = define_hash[s.caller]
1362 write(' c = (vl_msg_api_msg_config_t) '
1363 ' {{.id = VL_API_{ID} + msg_id_base,\n'
1365 ' .handler = vl_api_{n}_t_handler,\n'
1366 ' .cleanup = vl_noop_handler,\n'
1367 ' .endian = vl_api_{n}_t_endian,\n'
1368 ' .print = vl_api_{n}_t_print,\n'
1371 ' .print_json = vl_api_{n}_t_print_json,\n'
1372 ' .tojson = vl_api_{n}_t_tojson,\n'
1373 ' .fromjson = vl_api_{n}_t_fromjson,\n'
1374 ' .is_autoendian = {auto}}};\n'
1375 .format(n=s.caller, ID=s.caller.upper(),
1377 write(' vl_msg_api_config (&c);\n')
1379 d = define_hash[s.reply]
1380 write(' c = (vl_msg_api_msg_config_t) '
1381 '{{.id = VL_API_{ID} + msg_id_base,\n'
1384 ' .cleanup = vl_noop_handler,\n'
1385 ' .endian = vl_api_{n}_t_endian,\n'
1386 ' .print = vl_api_{n}_t_print,\n'
1389 ' .print_json = vl_api_{n}_t_print_json,\n'
1390 ' .tojson = vl_api_{n}_t_tojson,\n'
1391 ' .fromjson = vl_api_{n}_t_fromjson,\n'
1392 ' .is_autoendian = {auto}}};\n'
1393 .format(n=s.reply, ID=s.reply.upper(),
1395 write(' vl_msg_api_config (&c);\n')
1399 write(' return msg_id_base;\n')
1402 severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1403 'info': 'VL_COUNTER_SEVERITY_INFO',
1404 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1406 for cnt in counters:
1408 write('vlib_error_desc_t {}_error_counters[] = {{\n'.format(csetname))
1411 write(' .name = "{}",\n'.format(c['name']))
1412 write(' .desc = "{}",\n'.format(c['description']))
1413 write(' .severity = {},\n'.format(severity[c['severity']]))
1418 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1420 '''Generate code for legacy style VAT. To be deleted.'''
1421 write = stream.write
1423 define_hash = {d.name: d for d in defines}
1426 #define vl_endianfun /* define message structures */
1427 #include "{module}.api.h"
1430 /* instantiate all the print functions we know about */
1431 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1433 #include "{module}.api.h"
1438 write(hdr.format(module=module))
1441 d = define_hash[s.reply]
1446 ' * Manual definition requested for: \n'
1447 ' * vl_api_{n}_t_handler()\n'
1451 if not define_hash[s.caller].autoreply:
1452 write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1455 write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1456 write('static void\n')
1457 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1459 write(' vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1460 write(' i32 retval = ntohl(mp->retval);\n')
1461 write(' if (vam->async_mode) {\n')
1462 write(' vam->async_errors += (retval < 0);\n')
1463 write(' } else {\n')
1464 write(' vam->retval = retval;\n')
1465 write(' vam->result_ready = 1;\n')
1471 if define_hash[e].manual_print:
1473 write('static void\n')
1474 write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1475 write(' vl_print(0, "{n} event called:");\n'.format(n=e))
1476 write(' vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1479 write('static void\n')
1480 write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1482 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1484 ' vl_api_{n}_t_handler, '
1485 ' vl_noop_handler,\n'
1486 ' vl_api_{n}_t_endian, '
1487 ' vl_api_{n}_t_print,\n'
1488 ' sizeof(vl_api_{n}_t), 1,\n'
1489 ' vl_api_{n}_t_print_json,\n'
1490 ' vl_api_{n}_t_tojson,\n'
1491 ' vl_api_{n}_t_fromjson);\n'
1492 .format(n=s.reply, ID=s.reply.upper()))
1493 write(' hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1494 .format(n=s.caller))
1496 write(' hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1498 help=define_hash[s.caller].options['vat_help']))
1504 write(' vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1506 ' vl_api_{n}_t_handler, '
1507 ' vl_noop_handler,\n'
1508 ' vl_api_{n}_t_endian, '
1509 ' vl_api_{n}_t_print,\n'
1510 ' sizeof(vl_api_{n}_t), 1,\n'
1511 ' vl_api_{n}_t_print_json,\n'
1512 ' vl_api_{n}_t_tojson,\n'
1513 ' vl_api_{n}_t_fromjson);\n'
1514 .format(n=e, ID=e.upper()))
1517 write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1519 write(' {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1520 write(' mainp->vat_main = vam;\n')
1521 write(' mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1522 ' ("{n}_{crc:08x}");\n'
1523 .format(n=module, crc=file_crc))
1524 write(' if (mainp->msg_id_base == (u16) ~0)\n')
1525 write(' return clib_error_return (0, "{} plugin not loaded...");\n'
1527 write(' setup_message_id_table (vam, mainp->msg_id_base);\n')
1528 write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1529 write(' VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1531 write(' return 0;\n')
1536 '''Check if a method is generated already.'''
1537 def _f(module, d, processed, *args):
1538 if d.name in processed:
1540 processed[d.name] = True
1541 return func(module, d, *args)
1545 def c_test_api_service(s, dump, stream):
1546 '''Generate JSON code for a service.'''
1547 write = stream.write
1549 req_reply_template = '''\
1556 mp = vl_api_{n}_t_fromjson(o, &len);
1558 fprintf(stderr, "Failed converting JSON to API\\n");
1562 mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1563 vl_api_{n}_t_endian(mp);
1564 vac_write((char *)mp, len);
1570 vac_read(&p, &l, 5); // XXX: Fix timeout
1571 if (p == 0 || l == 0) return 0;
1572 // XXX Will fail in case of event received. Do loop
1573 if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1574 fprintf(stderr, "Mismatched reply\\n");
1577 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1578 vl_api_{r}_t_endian(rmp);
1579 return vl_api_{r}_t_tojson(rmp);
1583 dump_details_template = '''\
1587 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1590 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1592 fprintf(stderr, "Failed converting JSON to API\\n");
1595 mp->_vl_msg_id = msg_id;
1596 vl_api_{n}_t_endian(mp);
1597 vac_write((char *)mp, len);
1600 vat2_control_ping(123); // FIX CONTEXT
1601 cJSON *reply = cJSON_CreateArray();
1603 u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1604 u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1610 vac_read(&p, &l, 5); // XXX: Fix timeout
1611 if (p == 0 || l == 0) {{
1616 /* Message can be one of [_details, control_ping_reply
1617 * or unrelated event]
1619 u16 reply_msg_id = ntohs(*((u16 *)p));
1620 if (reply_msg_id == ping_reply_msg_id) {{
1624 if (reply_msg_id == details_msg_id) {{
1625 if (l < sizeof(vl_api_{r}_t)) {{
1629 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1630 vl_api_{r}_t_endian(rmp);
1631 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1638 gets_details_reply_template = '''\
1642 u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1645 vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1647 fprintf(stderr, "Failed converting JSON to API\\n");
1650 mp->_vl_msg_id = msg_id;
1652 vl_api_{n}_t_endian(mp);
1653 vac_write((char *)mp, len);
1656 cJSON *reply = cJSON_CreateArray();
1658 u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1659 u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1665 vac_read(&p, &l, 5); // XXX: Fix timeout
1667 /* Message can be one of [_details, control_ping_reply
1668 * or unrelated event]
1670 u16 msg_id = ntohs(*((u16 *)p));
1671 if (msg_id == reply_msg_id) {{
1672 vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1673 vl_api_{r}_t_endian(rmp);
1674 cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1678 if (msg_id == details_msg_id) {{
1679 vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1680 vl_api_{d}_t_endian(rmp);
1681 cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1690 if s.stream_message:
1691 write(gets_details_reply_template
1692 .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1693 R=s.reply.upper(), d=s.stream_message,
1694 D=s.stream_message.upper()))
1696 write(dump_details_template.format(n=s.caller, r=s.reply,
1700 write(req_reply_template.format(n=s.caller, r=s.reply,
1705 def generate_c_test2_boilerplate(services, defines, module, stream):
1706 '''Generate code for VAT2 plugin.'''
1707 write = stream.write
1709 define_hash = {d.name: d for d in defines}
1713 #include <vlibapi/api.h>
1714 #include <vlibmemory/api.h>
1715 #include <vppinfra/error.h>
1716 #include <vnet/ip/ip_format_fns.h>
1717 #include <vnet/ethernet/ethernet_format_fns.h>
1719 #define vl_typedefs /* define message structures */
1720 #include <vlibmemory/vl_memory_api_h.h>
1721 #include <vlibmemory/vlib.api_types.h>
1722 #include <vlibmemory/vlib.api.h>
1725 #include "{module}.api_enum.h"
1726 #include "{module}.api_types.h"
1728 #define vl_endianfun /* define message structures */
1729 #include "{module}.api.h"
1732 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1734 #include "{module}.api.h"
1737 #include "{module}.api_tojson.h"
1738 #include "{module}.api_fromjson.h"
1739 #include <vpp-api/client/vppapiclient.h>
1741 #include <vat2/vat2_helpers.h>
1745 write(hdr.format(module=module))
1748 if s.reply not in define_hash:
1750 c_test_api_service(s, s.stream, stream)
1752 write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n')
1753 # write('__attribute__((constructor))')
1754 write('clib_error_t *\n')
1755 write('vat2_register_plugin (void) {\n')
1757 if s.reply not in define_hash:
1759 crc = define_hash[s.caller].crc
1760 write(' vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'
1761 .format(n=s.caller, crc=crc))
1762 write(' return 0;\n')
1767 # Plugin entry point
1769 def run(args, apifilename, s):
1770 '''Main plugin entry point.'''
1773 if not args.outputdir:
1774 sys.stderr.write('Missing --outputdir argument')
1777 basename = os.path.basename(apifilename)
1778 filename, _ = os.path.splitext(basename)
1779 modulename = filename.replace('.', '_')
1780 filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1781 filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1782 filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1783 filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1784 filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1786 filename_c_tojson = (os.path.join(args.outputdir +
1787 '/' + basename + '_tojson.h'))
1788 filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1789 basename + '_fromjson.h'))
1791 # Generate separate types file
1793 generate_include_types(s, modulename, st)
1794 with open(filename_types, 'w') as fd:
1796 shutil.copyfileobj(st, fd)
1799 # Generate separate enum file
1801 st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1802 st.write('#define included_{}_api_enum_h\n'.format(modulename))
1803 generate_include_enum(s, modulename, st)
1804 generate_include_counters(s['Counters'], st)
1805 st.write('#endif\n')
1806 with open(filename_enum, 'w') as fd:
1808 shutil.copyfileobj(st, fd)
1811 # Generate separate C file
1813 generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1814 s['file_crc'], modulename, st)
1815 with open(filename_c, 'w') as fd:
1817 shutil.copyfileobj(st, fd)
1820 # Generate separate C test file
1822 plugin = bool('plugin' in apifilename)
1823 generate_c_test_boilerplate(s['Service'], s['Define'],
1825 modulename, plugin, st)
1826 with open(filename_c_test, 'w') as fd:
1828 shutil.copyfileobj(st, fd)
1831 # Fully autogenerated VATv2 C test file
1833 generate_c_test2_boilerplate(s['Service'], s['Define'],
1835 with open(filename_c_test2, 'w') as fd:
1837 shutil.copyfileobj(st, fd)
1840 # Generate separate JSON file
1842 generate_tojson(s, modulename, st)
1843 with open(filename_c_tojson, 'w') as fd:
1845 shutil.copyfileobj(st, fd)
1848 generate_fromjson(s, modulename, st)
1849 with open(filename_c_fromjson, 'w') as fd:
1851 shutil.copyfileobj(st, fd)
1854 output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1855 input_filename=basename)
1856 output += generate_imports(s['Import'])
1857 output += msg_ids(s)
1858 output += msg_names(s)
1859 output += msg_name_crc_list(s, filename)
1860 output += typedefs(modulename)
1861 printfun_types(s['types'], stream, modulename)
1862 printfun(s['Define'], stream, modulename)
1863 output += stream.getvalue()
1865 output += endianfun(s['types'] + s['Define'], modulename)
1866 output += version_tuple(s, basename)
1867 output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1868 file_crc=s['file_crc'])