vppapigen: fix fromjson coverity errors in generation
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 #
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:
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14 #
15
16 #
17 # Provide two classes FromJSON and TOJSON that converts between JSON and VPP's
18 # binary API format
19 #
20
21 '''
22 This module creates C code for core VPP, VPP plugins and client side VAT and
23 VAT2 tests.
24 '''
25
26 import datetime
27 import os
28 import time
29 import sys
30 from io import StringIO
31 import shutil
32
33 process_imports = False
34
35
36 ###############################################################################
37 class ToJSON():
38     '''Class to generate functions converting from VPP binary API to JSON.'''
39     _dispatch = {}
40     noprint_fields = {'_vl_msg_id': None,
41                       'client_index': None,
42                       'context': None}
43     is_number = {'u8': None,
44                  'i8': None,
45                  'u16': None,
46                  'i16': None,
47                  'u32': None,
48                  'i32': None,
49                  'u64': None,
50                  'i64': None,
51                  'f64': None,
52                  }
53
54     def __init__(self, module, types, defines, imported_types, stream):
55         self.stream = stream
56         self.module = module
57         self.defines = defines
58         self.types = types
59         self.types_hash = {'vl_api_'+d.name+'_t':
60                            d for d in types + imported_types}
61         self.defines_hash = {d.name: d for d in defines}
62
63     def header(self):
64         '''Output the top boilerplate.'''
65         write = self.stream.write
66         write('#ifndef included_{}_api_tojson_h\n'.format(self.module))
67         write('#define included_{}_api_tojson_h\n'.format(self.module))
68         write('#include <vppinfra/cJSON.h>\n\n')
69         write('#include <vat2/jsonconvert.h>\n\n')
70
71     def footer(self):
72         '''Output the bottom boilerplate.'''
73         write = self.stream.write
74         write('#endif\n')
75
76     def get_base_type(self, t):
77         vt_type = None
78         try:
79             vt = self.types_hash[t]
80             if vt.type == 'Using' and 'length' not in vt.alias:
81                 vt_type = vt.alias['type']
82         except KeyError:
83             vt = t
84         return vt, vt_type
85
86     def get_json_func(self, t):
87         '''Given the type, returns the function to use to create a
88         cJSON object'''
89         vt, vt_type = self.get_base_type(t)
90
91         if t in self.is_number or vt_type in self.is_number:
92             return 'cJSON_AddNumberToObject', '', False
93         if t == 'bool':
94             return 'cJSON_AddBoolToObject', '', False
95
96         # Lookup type name check if it's enum
97         if vt.type == 'Enum' or vt.type == 'EnumFlag':
98             return '{t}_tojson'.format(t=t), '', True
99         return '{t}_tojson'.format(t=t), '&', True
100
101     def get_json_array_func(self, t):
102         '''Given a type returns the function to create a cJSON object
103         for arrays.'''
104         if t in self.is_number:
105             return 'cJSON_CreateNumber', ''
106         if t == 'bool':
107             return 'cJSON_CreateBool', ''
108         vt, vt_type = self.get_base_type(t)
109         if vt.type == 'Enum' or vt.type == 'EnumFlag':
110             return '{t}_tojson'.format(t=t), ''
111         return '{t}_tojson'.format(t=t), '&'
112
113     def print_string(self, o):
114         '''Create cJSON object from vl_api_string_t'''
115         write = self.stream.write
116         if o.modern_vla:
117             write('    vl_api_string_cJSON_AddToObject(o, "{n}", &a->{n});\n'
118                   .format(n=o.fieldname))
119         else:
120
121             write('    cJSON_AddStringToObject(o, "{n}", (char *)a->{n});\n'
122                   .format(n=o.fieldname))
123
124     def print_field(self, o):
125         '''Called for every field in a typedef or define.'''
126         write = self.stream.write
127         if o.fieldname in self.noprint_fields:
128             return
129
130         f, p, newobj = self.get_json_func(o.fieldtype)
131
132         if newobj:
133             write('    cJSON_AddItemToObject(o, "{n}", {f}({p}a->{n}));\n'
134                   .format(f=f, p=p, n=o.fieldname))
135         else:
136             write('    {f}(o, "{n}", {p}a->{n});\n'
137                   .format(f=f, p=p, n=o.fieldname))
138
139     _dispatch['Field'] = print_field
140
141     def print_array(self, o):
142         '''Converts a VPP API array to cJSON array.'''
143         write = self.stream.write
144
145         forloop = '''\
146     {{
147         int i;
148         cJSON *array = cJSON_AddArrayToObject(o, "{n}");
149         for (i = 0; i < {lfield}; i++) {{
150             cJSON_AddItemToArray(array, {f}({p}a->{n}[i]));
151         }}
152     }}
153 '''
154
155         if o.fieldtype == 'string':
156             self.print_string(o)
157             return
158
159         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
160         if o.fieldtype == 'u8':
161             write('    {\n')
162             # What is length field doing here?
163             write('    u8 *s = format(0, "0x%U", format_hex_bytes, '
164                   '&a->{n}, {lfield});\n'
165                   .format(n=o.fieldname, lfield=lfield))
166             write('    cJSON_AddStringToObject(o, "{n}", (char *)s);\n'
167                   .format(n=o.fieldname))
168             write('    vec_free(s);\n')
169             write('    }\n')
170             return
171
172         f, p = self.get_json_array_func(o.fieldtype)
173         write(forloop.format(lfield=lfield,
174                              t=o.fieldtype,
175                              n=o.fieldname,
176                              f=f,
177                              p=p))
178
179     _dispatch['Array'] = print_array
180
181     def print_enum(self, o):
182         '''Create cJSON object (string) for VPP API enum'''
183         write = self.stream.write
184         write('static inline cJSON *vl_api_{name}_t_tojson '
185               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
186
187         write("    switch(a) {\n")
188         for b in o.block:
189             write("    case %s:\n" % b[1])
190             write('        return cJSON_CreateString("{}");\n'.format(b[0]))
191         write('    default: return cJSON_CreateString("Invalid ENUM");\n')
192         write('    }\n')
193         write('    return 0;\n')
194         write('}\n')
195
196     _dispatch['Enum'] = print_enum
197
198     def print_enum_flag(self, o):
199         '''Create cJSON object (string) for VPP API enum'''
200         write = self.stream.write
201         write('static inline cJSON *vl_api_{name}_t_tojson '
202               '(vl_api_{name}_t a) {{\n'.format(name=o.name))
203         write('    cJSON *array = cJSON_CreateArray();\n')
204
205         for b in o.block:
206             write('    if (a & {})\n'.format(b[0]))
207             write('       cJSON_AddItemToArray(array, cJSON_CreateString("{}"));\n'.format(b[0]))
208         write('    return array;\n')
209         write('}\n')
210
211     _dispatch['EnumFlag'] = print_enum_flag
212
213     def print_typedef(self, o):
214         '''Create cJSON (dictionary) object from VPP API typedef'''
215         write = self.stream.write
216         write('static inline cJSON *vl_api_{name}_t_tojson '
217               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
218         write('    cJSON *o = cJSON_CreateObject();\n')
219
220         for t in o.block:
221             self._dispatch[t.type](self, t)
222
223         write('    return o;\n')
224         write('}\n')
225
226     def print_define(self, o):
227         '''Create cJSON (dictionary) object from VPP API define'''
228         write = self.stream.write
229         write('static inline cJSON *vl_api_{name}_t_tojson '
230               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
231         write('    cJSON *o = cJSON_CreateObject();\n')
232         write('    cJSON_AddStringToObject(o, "_msgname", "{}");\n'
233               .format(o.name))
234
235         for t in o.block:
236             self._dispatch[t.type](self, t)
237
238         write('    return o;\n')
239         write('}\n')
240
241     def print_using(self, o):
242         '''Create cJSON (dictionary) object from VPP API aliased type'''
243         if o.manual_print:
244             return
245
246         write = self.stream.write
247         write('static inline cJSON *vl_api_{name}_t_tojson '
248               '(vl_api_{name}_t *a) {{\n'.format(name=o.name))
249
250         write('    u8 *s = format(0, "%U", format_vl_api_{}_t, a);\n'
251               .format(o.name))
252         write('    cJSON *o = cJSON_CreateString((char *)s);\n')
253         write('    vec_free(s);\n')
254         write('    return o;\n')
255         write('}\n')
256
257     _dispatch['Typedef'] = print_typedef
258     _dispatch['Define'] = print_define
259     _dispatch['Using'] = print_using
260     _dispatch['Union'] = print_typedef
261
262     def generate_function(self, t):
263         '''Main entry point'''
264         write = self.stream.write
265         if t.manual_print:
266             write('/* Manual print {} */\n'.format(t.name))
267             return
268         self._dispatch[t.type](self, t)
269
270     def generate_types(self):
271         '''Main entry point'''
272         for t in self.types:
273             self.generate_function(t)
274
275     def generate_defines(self):
276         '''Main entry point'''
277         for t in self.defines:
278             self.generate_function(t)
279
280
281 class FromJSON():
282     '''
283     Parse JSON objects into VPP API binary message structures.
284     '''
285     _dispatch = {}
286     noprint_fields = {'_vl_msg_id': None,
287                       'client_index': None,
288                       'context': None}
289     is_number = {'u8': None,
290                  'i8': None,
291                  'u16': None,
292                  'i16': None,
293                  'u32': None,
294                  'i32': None,
295                  'u64': None,
296                  'i64': None,
297                  'f64': None,
298                  }
299
300     def __init__(self, module, types, defines, imported_types, stream):
301         self.stream = stream
302         self.module = module
303         self.defines = defines
304         self.types = types
305         self.types_hash = {'vl_api_'+d.name+'_t':
306                            d for d in types + imported_types}
307         self.defines_hash = {d.name: d for d in defines}
308
309     def header(self):
310         '''Output the top boilerplate.'''
311         write = self.stream.write
312         write('#ifndef included_{}_api_fromjson_h\n'.format(self.module))
313         write('#define included_{}_api_fromjson_h\n'.format(self.module))
314         write('#include <vppinfra/cJSON.h>\n\n')
315         write('#include <vat2/jsonconvert.h>\n\n')
316         write('#pragma GCC diagnostic ignored "-Wunused-label"\n')
317
318     def is_base_type(self, t):
319         '''Check if a type is one of the VPP API base types'''
320         if t in self.is_number:
321             return True
322         if t == 'bool':
323             return True
324         return False
325
326     def footer(self):
327         '''Output the bottom boilerplate.'''
328         write = self.stream.write
329         write('#endif\n')
330
331     def print_string(self, o, toplevel=False):
332         '''Convert JSON string to vl_api_string_t'''
333         write = self.stream.write
334
335         msgvar = "a" if toplevel else "*mp"
336         msgsize = "l" if toplevel else "*len"
337
338         if o.modern_vla:
339             write('    char *p = cJSON_GetStringValue(item);\n')
340             write('    size_t plen = strlen(p);\n')
341             write('    {msgvar} = realloc({msgvar}, {msgsize} + plen);\n'
342                   .format(msgvar=msgvar, msgsize=msgsize))
343             write('    if ({msgvar} == 0) goto error;\n'.format(msgvar=msgvar))
344             write('    vl_api_c_string_to_api_string(p, (void *){msgvar} + '
345                   '{msgsize} - sizeof(vl_api_string_t));\n'
346                   .format(msgvar=msgvar, msgsize=msgsize))
347             write('    {msgsize} += plen;\n'.format(msgsize=msgsize))
348         else:
349             write('    strncpy_s((char *)a->{n}, sizeof(a->{n}), '
350                   'cJSON_GetStringValue(item), sizeof(a->{n}) - 1);\n'
351                   .format(n=o.fieldname))
352
353     def print_field(self, o, toplevel=False):
354         '''Called for every field in a typedef or define.'''
355         write = self.stream.write
356         if o.fieldname in self.noprint_fields:
357             return
358         is_bt = self.is_base_type(o.fieldtype)
359         t = 'vl_api_{}'.format(o.fieldtype) if is_bt else o.fieldtype
360
361         msgvar = "(void **)&a" if toplevel else "mp"
362         msgsize = "&l" if toplevel else "len"
363
364         if is_bt:
365             write('    vl_api_{t}_fromjson(item, &a->{n});\n'
366                   .format(t=o.fieldtype, n=o.fieldname))
367         else:
368             write('    if ({t}_fromjson({msgvar}, '
369                   '{msgsize}, item, &a->{n}) < 0) goto error;\n'
370                   .format(t=t, n=o.fieldname, msgvar=msgvar, msgsize=msgsize))
371
372     _dispatch['Field'] = print_field
373
374     def print_array(self, o, toplevel=False):
375         '''Convert JSON array to VPP API array'''
376         write = self.stream.write
377
378         forloop = '''\
379     {{
380         int i;
381         cJSON *array = cJSON_GetObjectItem(o, "{n}");
382         int size = cJSON_GetArraySize(array);
383         if (size != {lfield}) return 0;
384         for (i = 0; i < size; i++) {{
385             cJSON *e = cJSON_GetArrayItem(array, i);
386             {call}
387         }}
388     }}
389 '''
390         forloop_vla = '''\
391     {{
392         int i;
393         cJSON *array = cJSON_GetObjectItem(o, "{n}");
394         int size = cJSON_GetArraySize(array);
395         {lfield} = size;
396         *{msgvar} = realloc({msgvar}, {msgsize} + sizeof({t}) * size);
397         {t} *d = (void *){msgvar} + {msgsize};
398         {msgsize} += sizeof({t}) * size;
399         for (i = 0; i < size; i++) {{
400             cJSON *e = cJSON_GetArrayItem(array, i);
401             {call}
402         }}
403     }}
404 '''
405         t = o.fieldtype
406         if o.fieldtype == 'string':
407             self.print_string(o, toplevel)
408             return
409
410         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
411         msgvar = "(void **)&a" if toplevel else "mp"
412         msgsize = "l" if toplevel else "*len"
413
414         if o.fieldtype == 'u8':
415             if o.lengthfield:
416                 write('    s = u8string_fromjson(o, "{}");\n'
417                       .format(o.fieldname))
418                 write('    if (!s) return 0;\n')
419                 write('    {} = vec_len(s);\n'.format(lfield))
420
421                 write('    *{msgvar} = realloc({msgvar}, {msgsize} + '
422                       'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
423                 write('    memcpy((void *){msgvar} + {msgsize}, s, '
424                       'vec_len(s));\n'.format(msgvar=msgvar, msgsize=msgsize))
425                 write('    {msgsize} += vec_len(s);\n'.format(msgsize=msgsize))
426
427                 write('    vec_free(s);\n')
428             else:
429                 write('    u8string_fromjson2(o, "{n}", a->{n});\n'
430                       .format(n=o.fieldname))
431             return
432
433         is_bt = self.is_base_type(o.fieldtype)
434
435         if o.lengthfield:
436             if is_bt:
437                 call = ('vl_api_{t}_fromjson(e, &d[i]);'
438                         .format(t=o.fieldtype))
439             else:
440                 call = ('{t}_fromjson({msgvar}, len, e, &d[i]); '
441                         .format(t=o.fieldtype, msgvar=msgvar))
442             write(forloop_vla.format(lfield=lfield,
443                                      t=o.fieldtype,
444                                      n=o.fieldname,
445                                      call=call,
446                                      msgvar=msgvar,
447                                      msgsize=msgsize))
448         else:
449             if is_bt:
450                 call = ('vl_api_{t}_fromjson(e, &a->{n}[i]);'
451                         .format(t=t, n=o.fieldname))
452             else:
453                 call = ('if ({}_fromjson({}, len, e, &a->{}[i]) < 0) goto error;'
454                         .format(t, msgvar, o.fieldname))
455             write(forloop.format(lfield=lfield,
456                                  t=t,
457                                  n=o.fieldname,
458                                  call=call,
459                                  msgvar=msgvar,
460                                  msgsize=msgsize))
461
462     _dispatch['Array'] = print_array
463
464     def print_enum(self, o):
465         '''Convert to JSON enum(string) to VPP API enum (int)'''
466         write = self.stream.write
467         write('static inline int vl_api_{n}_t_fromjson'
468               '(void **mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
469               .format(n=o.name))
470         write('    char *p = cJSON_GetStringValue(o);\n')
471         for b in o.block:
472             write('    if (strcmp(p, "{}") == 0) {{*a = {}; return 0;}}\n'
473                   .format(b[0], b[1]))
474         write('    *a = 0;\n')
475         write('    return -1;\n')
476         write('}\n')
477
478     _dispatch['Enum'] = print_enum
479
480     def print_enum_flag(self, o):
481         '''Convert to JSON enum(string) to VPP API enum (int)'''
482         write = self.stream.write
483         write('static inline void *vl_api_{n}_t_fromjson '
484               '(void *mp, int *len, cJSON *o, vl_api_{n}_t *a) {{\n'
485               .format(n=o.name))
486         write('   int i;\n')
487         write('   *a = 0;\n')
488         write('   for (i = 0; i < cJSON_GetArraySize(o); i++) {\n')
489         write('       cJSON *e = cJSON_GetArrayItem(o, i);\n')
490         write('       char *p = cJSON_GetStringValue(e);\n')
491         write('       if (!p) return 0;\n')
492         for b in o.block:
493             write('       if (strcmp(p, "{}") == 0) *a |= {};\n'
494                   .format(b[0], b[1]))
495         write('    }\n')
496         write('   return mp;\n')
497         write('}\n')
498
499     _dispatch['EnumFlag'] = print_enum_flag
500
501     def print_typedef(self, o):
502         '''Convert from JSON object to VPP API binary representation'''
503         write = self.stream.write
504
505         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
506               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
507               .format(name=o.name))
508         write('    cJSON *item __attribute__ ((unused));\n')
509         write('    u8 *s __attribute__ ((unused));\n')
510         for t in o.block:
511             if t.type == 'Field' and t.is_lengthfield:
512                 continue
513             write('\n    item = cJSON_GetObjectItem(o, "{}");\n'
514                   .format(t.fieldname))
515             write('    if (!item) goto error;\n')
516
517             self._dispatch[t.type](self, t)
518
519         write('\n    return 0;\n')
520         write('\n  error:\n')
521         write('    return -1;\n')
522         write('}\n')
523
524     def print_union(self, o):
525         '''Convert JSON object to VPP API binary union'''
526         write = self.stream.write
527
528         write('static inline int vl_api_{name}_t_fromjson (void **mp, '
529               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
530               .format(name=o.name))
531         write('    cJSON *item __attribute__ ((unused));\n')
532         write('    u8 *s __attribute__ ((unused));\n')
533         for t in o.block:
534             if t.type == 'Field' and t.is_lengthfield:
535                 continue
536             write('    item = cJSON_GetObjectItem(o, "{}");\n'
537                   .format(t.fieldname))
538             write('    if (item) {\n')
539             self._dispatch[t.type](self, t)
540             write('    };\n')
541         write('\n    return 0;\n')
542         write('\n  error:\n')
543         write('    return -1;\n')
544         write('}\n')
545
546     def print_define(self, o):
547         '''Convert JSON object to VPP API message'''
548         write = self.stream.write
549         write('static inline vl_api_{name}_t *vl_api_{name}_t_fromjson '
550               '(cJSON *o, int *len) {{\n'.format(name=o.name))
551         write('    cJSON *item __attribute__ ((unused));\n')
552         write('    u8 *s __attribute__ ((unused));\n')
553         write('    int l = sizeof(vl_api_{}_t);\n'.format(o.name))
554         write('    vl_api_{}_t *a = malloc(l);\n'.format(o.name))
555         write('\n')
556
557         for t in o.block:
558             if t.fieldname in self.noprint_fields:
559                 continue
560             if t.type == 'Field' and t.is_lengthfield:
561                 continue
562             write('    item = cJSON_GetObjectItem(o, "{}");\n'
563                   .format(t.fieldname))
564             write('    if (!item) goto error;\n')
565             self._dispatch[t.type](self, t, toplevel=True)
566             write('\n')
567
568         write('    *len = l;\n')
569         write('    return a;\n')
570         write('\n  error:\n')
571         write('    free(a);\n')
572         write('    return 0;\n')
573         write('}\n')
574
575     def print_using(self, o):
576         '''Convert JSON field to VPP type alias'''
577         write = self.stream.write
578
579         if o.manual_print:
580             return
581
582         t = o.using
583         write('static inline void *vl_api_{name}_t_fromjson (void *mp, '
584               'int *len, cJSON *o, vl_api_{name}_t *a) {{\n'
585               .format(name=o.name))
586         if 'length' in o.alias:
587             if t.fieldtype != 'u8':
588                 raise ValueError("Error in processing type {} for {}"
589                                  .format(t.fieldtype, o.name))
590             write('    vl_api_u8_string_fromjson(o, (u8 *)a, {});\n'
591                   .format(o.alias['length']))
592         else:
593             write('    vl_api_{t}_fromjson(o, ({t} *)a);\n'
594                   .format(t=t.fieldtype))
595
596         write('    return mp;\n')
597         write('}\n')
598
599     _dispatch['Typedef'] = print_typedef
600     _dispatch['Define'] = print_define
601     _dispatch['Using'] = print_using
602     _dispatch['Union'] = print_union
603
604     def generate_function(self, t):
605         '''Main entry point'''
606         write = self.stream.write
607         if t.manual_print:
608             write('/* Manual print {} */\n'.format(t.name))
609             return
610         self._dispatch[t.type](self, t)
611
612     def generate_types(self):
613         '''Main entry point'''
614         for t in self.types:
615             self.generate_function(t)
616
617     def generate_defines(self):
618         '''Main entry point'''
619         for t in self.defines:
620             self.generate_function(t)
621
622
623 def generate_tojson(s, modulename, stream):
624     '''Generate all functions to convert from API to JSON'''
625     write = stream.write
626
627     write('/* Imported API files */\n')
628     for i in s['Import']:
629         f = i.filename.replace('plugins/', '')
630         write('#include <{}_tojson.h>\n'.format(f))
631
632     pp = ToJSON(modulename, s['types'], s['Define'], s['imported']['types'],
633                 stream)
634     pp.header()
635     pp.generate_types()
636     pp.generate_defines()
637     pp.footer()
638     return ''
639
640
641 def generate_fromjson(s, modulename, stream):
642     '''Generate all functions to convert from JSON to API'''
643     write = stream.write
644     write('/* Imported API files */\n')
645     for i in s['Import']:
646         f = i.filename.replace('plugins/', '')
647         write('#include <{}_fromjson.h>\n'.format(f))
648
649     pp = FromJSON(modulename, s['types'], s['Define'], s['imported']['types'],
650                   stream)
651     pp.header()
652     pp.generate_types()
653     pp.generate_defines()
654     pp.footer()
655
656     return ''
657
658 ###############################################################################
659
660
661 DATESTRING = datetime.datetime.utcfromtimestamp(
662     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
663 TOP_BOILERPLATE = '''\
664 /*
665  * VLIB API definitions {datestring}
666  * Input file: {input_filename}
667  * Automatically generated: please edit the input file NOT this file!
668  */
669
670 #include <stdbool.h>
671 #if defined(vl_msg_id)||defined(vl_union_id) \\
672     || defined(vl_printfun) ||defined(vl_endianfun) \\
673     || defined(vl_api_version)||defined(vl_typedefs) \\
674     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
675     || defined(vl_api_version_tuple)
676 /* ok, something was selected */
677 #else
678 #warning no content included from {input_filename}
679 #endif
680
681 #define VL_API_PACKED(x) x __attribute__ ((packed))
682 '''
683
684 BOTTOM_BOILERPLATE = '''\
685 /****** API CRC (whole file) *****/
686
687 #ifdef vl_api_version
688 vl_api_version({input_filename}, {file_crc:#08x})
689
690 #endif
691 '''
692
693
694 def msg_ids(s):
695     '''Generate macro to map API message id to handler'''
696     output = '''\
697
698 /****** Message ID / handler enum ******/
699
700 #ifdef vl_msg_id
701 '''
702
703     for t in s['Define']:
704         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
705                   (t.name.upper(), t.name)
706     output += "#endif"
707
708     return output
709
710
711 def msg_names(s):
712     '''Generate calls to name mapping macro'''
713     output = '''\
714
715 /****** Message names ******/
716
717 #ifdef vl_msg_name
718 '''
719
720     for t in s['Define']:
721         dont_trace = 0 if t.dont_trace else 1
722         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
723     output += "#endif"
724
725     return output
726
727
728 def msg_name_crc_list(s, suffix):
729     '''Generate list of names to CRC mappings'''
730     output = '''\
731
732 /****** Message name, crc list ******/
733
734 #ifdef vl_msg_name_crc_list
735 '''
736     output += "#define foreach_vl_msg_name_crc_%s " % suffix
737
738     for t in s['Define']:
739         output += "\\\n_(VL_API_%s, %s, %08x) " % \
740                    (t.name.upper(), t.name, t.crc)
741     output += "\n#endif"
742
743     return output
744
745
746 def api2c(fieldtype):
747     '''Map between API type names and internal VPP type names'''
748     mappingtable = {'string': 'vl_api_string_t', }
749     if fieldtype in mappingtable:
750         return mappingtable[fieldtype]
751     return fieldtype
752
753
754 def typedefs(filename):
755     '''Include in the main files to the types file'''
756     output = '''\
757
758 /****** Typedefs ******/
759
760 #ifdef vl_typedefs
761 #include "{include}.api_types.h"
762 #endif
763 '''.format(include=filename)
764     return output
765
766
767 FORMAT_STRINGS = {'u8': '%u',
768                   'bool': '%u',
769                   'i8': '%d',
770                   'u16': '%u',
771                   'i16': '%d',
772                   'u32': '%u',
773                   'i32': '%ld',
774                   'u64': '%llu',
775                   'i64': '%lld',
776                   'f64': '%.2f'}
777
778
779 class Printfun():
780     '''Functions for pretty printing VPP API messages'''
781     _dispatch = {}
782     noprint_fields = {'_vl_msg_id': None,
783                       'client_index': None,
784                       'context': None}
785
786     def __init__(self, stream):
787         self.stream = stream
788
789     @staticmethod
790     def print_string(o, stream):
791         '''Pretty print a vl_api_string_t'''
792         write = stream.write
793         if o.modern_vla:
794             write('    if (vl_api_string_len(&a->{f}) > 0) {{\n'
795                   .format(f=o.fieldname))
796             write('        s = format(s, "\\n%U{f}: %U", '
797                   'format_white_space, indent, '
798                   'vl_api_format_string, (&a->{f}));\n'.format(f=o.fieldname))
799             write('    } else {\n')
800             write('        s = format(s, "\\n%U{f}:", '
801                   'format_white_space, indent);\n'.format(f=o.fieldname))
802             write('    }\n')
803         else:
804             write('    s = format(s, "\\n%U{f}: %s", '
805                   'format_white_space, indent, a->{f});\n'
806                   .format(f=o.fieldname))
807
808     def print_field(self, o, stream):
809         '''Pretty print API field'''
810         write = stream.write
811         if o.fieldname in self.noprint_fields:
812             return
813         if o.fieldtype in FORMAT_STRINGS:
814             f = FORMAT_STRINGS[o.fieldtype]
815             write('    s = format(s, "\\n%U{n}: {f}", '
816                   'format_white_space, indent, a->{n});\n'
817                   .format(n=o.fieldname, f=f))
818         else:
819             write('    s = format(s, "\\n%U{n}: %U", '
820                   'format_white_space, indent, '
821                   'format_{t}, &a->{n}, indent);\n'
822                   .format(n=o.fieldname, t=o.fieldtype))
823
824     _dispatch['Field'] = print_field
825
826     def print_array(self, o, stream):
827         '''Pretty print API array'''
828         write = stream.write
829
830         forloop = '''\
831     for (i = 0; i < {lfield}; i++) {{
832         s = format(s, "\\n%U{n}: %U",
833                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
834     }}
835 '''
836
837         forloop_format = '''\
838     for (i = 0; i < {lfield}; i++) {{
839         s = format(s, "\\n%U{n}: {t}",
840                    format_white_space, indent, a->{n}[i]);
841     }}
842 '''
843
844         if o.fieldtype == 'string':
845             self.print_string(o, stream)
846             return
847
848         if o.fieldtype == 'u8':
849             if o.lengthfield:
850                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
851                       'indent, format_hex_bytes, a->{n}, a->{lfield});\n'
852                       .format(n=o.fieldname, lfield=o.lengthfield))
853             else:
854                 write('    s = format(s, "\\n%U{n}: %U", format_white_space, '
855                       'indent, format_hex_bytes, a, {lfield});\n'
856                       .format(n=o.fieldname, lfield=o.length))
857             return
858
859         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
860         if o.fieldtype in FORMAT_STRINGS:
861             write(forloop_format.format(lfield=lfield,
862                                         t=FORMAT_STRINGS[o.fieldtype],
863                                         n=o.fieldname))
864         else:
865             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
866
867     _dispatch['Array'] = print_array
868
869     @staticmethod
870     def print_alias(k, v, stream):
871         '''Pretty print type alias'''
872         write = stream.write
873         if ('length' in v.alias and v.alias['length'] and
874                 v.alias['type'] == 'u8'):
875             write('    return format(s, "%U", format_hex_bytes, a, {});\n'
876                   .format(v.alias['length']))
877         elif v.alias['type'] in FORMAT_STRINGS:
878             write('    return format(s, "{}", *a);\n'
879                   .format(FORMAT_STRINGS[v.alias['type']]))
880         else:
881             write('    return format(s, "{} (print not implemented)");\n'
882                   .format(k))
883
884     @staticmethod
885     def print_enum(o, stream):
886         '''Pretty print API enum'''
887         write = stream.write
888         write("    switch(*a) {\n")
889         for b in o:
890             write("    case %s:\n" % b[1])
891             write('        return format(s, "{}");\n'.format(b[0]))
892         write('    }\n')
893
894     _dispatch['Enum'] = print_enum
895     _dispatch['EnumFlag'] = print_enum
896
897     def print_obj(self, o, stream):
898         '''Entry point'''
899         write = stream.write
900
901         if o.type in self._dispatch:
902             self._dispatch[o.type](self, o, stream)
903         else:
904             write('    s = format(s, "\\n{} {} {} (print not implemented");\n'
905                   .format(o.type, o.fieldtype, o.fieldname))
906
907
908 def printfun(objs, stream, modulename):
909     '''Main entry point for pretty print function generation'''
910     write = stream.write
911
912     h = '''\
913 /****** Print functions *****/
914 #ifdef vl_printfun
915 #ifndef included_{module}_printfun
916 #define included_{module}_printfun
917
918 #ifdef LP64
919 #define _uword_fmt \"%lld\"
920 #define _uword_cast (long long)
921 #else
922 #define _uword_fmt \"%ld\"
923 #define _uword_cast long
924 #endif
925
926 '''
927
928     signature = '''\
929 static inline void *vl_api_{name}_t_print (vl_api_{name}_t *a, void *handle)
930 {{
931     u8 *s = 0;
932     u32 indent __attribute__((unused)) = 2;
933     int i __attribute__((unused));
934 '''
935
936     h = h.format(module=modulename)
937     write(h)
938
939     pp = Printfun(stream)
940     for t in objs:
941         if t.manual_print:
942             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
943             continue
944         write(signature.format(name=t.name))
945         write('    /* Message definition: vl_api_{}_t: */\n'.format(t.name))
946         write("    s = format(s, \"vl_api_%s_t:\");\n" % t.name)
947         for o in t.block:
948             pp.print_obj(o, stream)
949         write('    vec_add1(s, 0);\n')
950         write('    vl_print (handle, (char *)s);\n')
951         write('    vec_free (s);\n')
952         write('    return handle;\n')
953         write('}\n\n')
954
955     write("\n#endif")
956     write("\n#endif /* vl_printfun */\n")
957
958     return ''
959
960
961 def printfun_types(objs, stream, modulename):
962     '''Pretty print API types'''
963     write = stream.write
964     pp = Printfun(stream)
965
966     h = '''\
967 /****** Print functions *****/
968 #ifdef vl_printfun
969 #ifndef included_{module}_printfun_types
970 #define included_{module}_printfun_types
971
972 '''
973     h = h.format(module=modulename)
974     write(h)
975
976     signature = '''\
977 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
978 {{
979     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
980     u32 indent __attribute__((unused)) = va_arg (*args, u32);
981     int i __attribute__((unused));
982     indent += 2;
983 '''
984
985     for t in objs:
986         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag':
987             write(signature.format(name=t.name))
988             pp.print_enum(t.block, stream)
989             write('    return s;\n')
990             write('}\n\n')
991             continue
992
993         if t.manual_print:
994             write("/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name)
995             continue
996
997         if t.__class__.__name__ == 'Using':
998             write(signature.format(name=t.name))
999             pp.print_alias(t.name, t, stream)
1000             write('}\n\n')
1001             continue
1002
1003         write(signature.format(name=t.name))
1004         for o in t.block:
1005             pp.print_obj(o, stream)
1006
1007         write('    return s;\n')
1008         write('}\n\n')
1009
1010     write("\n#endif")
1011     write("\n#endif /* vl_printfun_types */\n")
1012
1013
1014 def generate_imports(imports):
1015     '''Add #include matching the API import statements'''
1016     output = '/* Imported API files */\n'
1017     output += '#ifndef vl_api_version\n'
1018
1019     for i in imports:
1020         s = i.filename.replace('plugins/', '')
1021         output += '#include <{}.h>\n'.format(s)
1022     output += '#endif\n'
1023     return output
1024
1025
1026 ENDIAN_STRINGS = {
1027     'u16': 'clib_net_to_host_u16',
1028     'u32': 'clib_net_to_host_u32',
1029     'u64': 'clib_net_to_host_u64',
1030     'i16': 'clib_net_to_host_i16',
1031     'i32': 'clib_net_to_host_i32',
1032     'i64': 'clib_net_to_host_i64',
1033     'f64': 'clib_net_to_host_f64',
1034 }
1035
1036
1037 def endianfun_array(o):
1038     '''Generate endian functions for arrays'''
1039     forloop = '''\
1040     for (i = 0; i < {length}; i++) {{
1041         a->{name}[i] = {format}(a->{name}[i]);
1042     }}
1043 '''
1044
1045     forloop_format = '''\
1046     for (i = 0; i < {length}; i++) {{
1047         {type}_endian(&a->{name}[i]);
1048     }}
1049 '''
1050
1051     output = ''
1052     if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
1053         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1054     else:
1055         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length
1056         if o.fieldtype in ENDIAN_STRINGS:
1057             output += (forloop
1058                        .format(length=lfield,
1059                                format=ENDIAN_STRINGS[o.fieldtype],
1060                                name=o.fieldname))
1061         else:
1062             output += (forloop_format
1063                        .format(length=lfield, type=o.fieldtype,
1064                                name=o.fieldname))
1065     return output
1066
1067
1068 NO_ENDIAN_CONVERSION = {'client_index': None}
1069
1070
1071 def endianfun_obj(o):
1072     '''Generate endian conversion function for type'''
1073     output = ''
1074     if o.type == 'Array':
1075         return endianfun_array(o)
1076     if o.type != 'Field':
1077         output += ('    s = format(s, "\\n{} {} {} (print not implemented");\n'
1078                    .format(o.type, o.fieldtype, o.fieldname))
1079         return output
1080     if o.fieldname in NO_ENDIAN_CONVERSION:
1081         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1082         return output
1083     if o.fieldtype in ENDIAN_STRINGS:
1084         output += ('    a->{name} = {format}(a->{name});\n'
1085                    .format(name=o.fieldname,
1086                            format=ENDIAN_STRINGS[o.fieldtype]))
1087     elif o.fieldtype.startswith('vl_api_'):
1088         output += ('    {type}_endian(&a->{name});\n'
1089                    .format(type=o.fieldtype, name=o.fieldname))
1090     else:
1091         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
1092
1093     return output
1094
1095
1096 def endianfun(objs, modulename):
1097     '''Main entry point for endian function generation'''
1098     output = '''\
1099
1100 /****** Endian swap functions *****/\n\
1101 #ifdef vl_endianfun
1102 #ifndef included_{module}_endianfun
1103 #define included_{module}_endianfun
1104
1105 #undef clib_net_to_host_uword
1106 #ifdef LP64
1107 #define clib_net_to_host_uword clib_net_to_host_u64
1108 #else
1109 #define clib_net_to_host_uword clib_net_to_host_u32
1110 #endif
1111
1112 '''
1113     output = output.format(module=modulename)
1114
1115     signature = '''\
1116 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1117 {{
1118     int i __attribute__((unused));
1119 '''
1120
1121     for t in objs:
1122         if t.__class__.__name__ == 'Enum' or t.__class__.__name__ == 'EnumFlag' :
1123             output += signature.format(name=t.name)
1124             if t.enumtype in ENDIAN_STRINGS:
1125                 output += ('    *a = {}(*a);\n'
1126                            .format(ENDIAN_STRINGS[t.enumtype]))
1127             else:
1128                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1129                            .format(name=t.name))
1130
1131             output += '}\n\n'
1132             continue
1133
1134         if t.manual_endian:
1135             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1136             continue
1137
1138         if t.__class__.__name__ == 'Using':
1139             output += signature.format(name=t.name)
1140             if ('length' in t.alias and t.alias['length'] and
1141                     t.alias['type'] == 'u8'):
1142                 output += ('    /* a->{name} = a->{name} (no-op) */\n'
1143                            .format(name=t.name))
1144             elif t.alias['type'] in FORMAT_STRINGS:
1145                 output += ('    *a = {}(*a);\n'
1146                            .format(ENDIAN_STRINGS[t.alias['type']]))
1147             else:
1148                 output += '    /* Not Implemented yet {} */'.format(t.name)
1149             output += '}\n\n'
1150             continue
1151
1152         output += signature.format(name=t.name)
1153
1154         for o in t.block:
1155             output += endianfun_obj(o)
1156         output += '}\n\n'
1157
1158     output += "\n#endif"
1159     output += "\n#endif /* vl_endianfun */\n\n"
1160
1161     return output
1162
1163
1164 def version_tuple(s, module):
1165     '''Generate semantic version string'''
1166     output = '''\
1167 /****** Version tuple *****/
1168
1169 #ifdef vl_api_version_tuple
1170
1171 '''
1172     if 'version' in s['Option']:
1173         v = s['Option']['version']
1174         (major, minor, patch) = v.split('.')
1175         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
1176                   (module, major, minor, patch)
1177
1178     output += "\n#endif /* vl_api_version_tuple */\n\n"
1179
1180     return output
1181
1182
1183 def generate_include_enum(s, module, stream):
1184     '''Generate <name>.api_enum.h'''
1185     write = stream.write
1186
1187     if 'Define' in s:
1188         write('typedef enum {\n')
1189         for t in s['Define']:
1190             write('   VL_API_{},\n'.format(t.name.upper()))
1191         write('   VL_MSG_{}_LAST\n'.format(module.upper()))
1192         write('}} vl_api_{}_enum_t;\n'.format(module))
1193
1194
1195 def generate_include_counters(s, stream):
1196     '''Include file for the counter data model types.'''
1197     write = stream.write
1198
1199     for counters in s:
1200         csetname = counters.name
1201         write('typedef enum {\n')
1202         for c in counters.block:
1203             write('   {}_ERROR_{},\n'
1204                   .format(csetname.upper(), c['name'].upper()))
1205         write('   {}_N_ERROR\n'.format(csetname.upper()))
1206         write('}} vl_counter_{}_enum_t;\n'.format(csetname))
1207
1208         write('extern vl_counter_t {}_error_counters[];\n'.format(csetname))
1209
1210
1211 def generate_include_types(s, module, stream):
1212     '''Generate separate API _types file.'''
1213     write = stream.write
1214
1215     write('#ifndef included_{module}_api_types_h\n'.format(module=module))
1216     write('#define included_{module}_api_types_h\n'.format(module=module))
1217
1218     if 'version' in s['Option']:
1219         v = s['Option']['version']
1220         (major, minor, patch) = v.split('.')
1221         write('#define VL_API_{m}_API_VERSION_MAJOR {v}\n'
1222               .format(m=module.upper(), v=major))
1223         write('#define VL_API_{m}_API_VERSION_MINOR {v}\n'
1224               .format(m=module.upper(), v=minor))
1225         write('#define VL_API_{m}_API_VERSION_PATCH {v}\n'
1226               .format(m=module.upper(), v=patch))
1227
1228     if 'Import' in s:
1229         write('/* Imported API files */\n')
1230         for i in s['Import']:
1231             filename = i.filename.replace('plugins/', '')
1232             write('#include <{}_types.h>\n'.format(filename))
1233
1234     for o in s['types'] + s['Define']:
1235         tname = o.__class__.__name__
1236         if tname == 'Using':
1237             if 'length' in o.alias:
1238                 write('typedef %s vl_api_%s_t[%s];\n' %
1239                       (o.alias['type'], o.name, o.alias['length']))
1240             else:
1241                 write('typedef %s vl_api_%s_t;\n' % (o.alias['type'], o.name))
1242         elif tname == 'Enum' or tname == 'EnumFlag':
1243             if o.enumtype == 'u32':
1244                 write("typedef enum {\n")
1245             else:
1246                 write("typedef enum __attribute__((packed)) {\n")
1247
1248             for b in o.block:
1249                 write("    %s = %s,\n" % (b[0], b[1]))
1250             write('} vl_api_%s_t;\n' % o.name)
1251             if o.enumtype != 'u32':
1252                 size1 = 'sizeof(vl_api_%s_t)' % o.name
1253                 size2 = 'sizeof(%s)' % o.enumtype
1254                 err_str = 'size of API enum %s is wrong' % o.name
1255                 write('STATIC_ASSERT(%s == %s, "%s");\n'
1256                       % (size1, size2, err_str))
1257         else:
1258             if tname == 'Union':
1259                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n"
1260                       % o.name)
1261             else:
1262                 write(("typedef struct __attribute__ ((packed)) _vl_api_%s {\n")
1263                       % o.name)
1264             for b in o.block:
1265                 if b.type == 'Option':
1266                     continue
1267                 if b.type == 'Field':
1268                     write("    %s %s;\n" % (api2c(b.fieldtype),
1269                                             b.fieldname))
1270                 elif b.type == 'Array':
1271                     if b.lengthfield:
1272                         write("    %s %s[0];\n" % (api2c(b.fieldtype),
1273                                                    b.fieldname))
1274                     else:
1275                         # Fixed length strings decay to nul terminated u8
1276                         if b.fieldtype == 'string':
1277                             if b.modern_vla:
1278                                 write('    {} {};\n'
1279                                       .format(api2c(b.fieldtype),
1280                                               b.fieldname))
1281                             else:
1282                                 write('    u8 {}[{}];\n'
1283                                       .format(b.fieldname, b.length))
1284                         else:
1285                             write("    %s %s[%s];\n" %
1286                                   (api2c(b.fieldtype), b.fieldname,
1287                                    b.length))
1288                 else:
1289                     raise ValueError("Error in processing type {} for {}"
1290                                      .format(b, o.name))
1291
1292             write('} vl_api_%s_t;\n' % o.name)
1293
1294     for t in s['Define']:
1295         write('#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'
1296               .format(n=t.name, ID=t.name.upper(), crc=t.crc))
1297
1298     write("\n#endif\n")
1299
1300
1301 def generate_c_boilerplate(services, defines, counters, file_crc,
1302                            module, stream):
1303     '''VPP side plugin.'''
1304     write = stream.write
1305     define_hash = {d.name: d for d in defines}
1306
1307     hdr = '''\
1308 #define vl_endianfun            /* define message structures */
1309 #include "{module}.api.h"
1310 #undef vl_endianfun
1311
1312 /* instantiate all the print functions we know about */
1313 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1314 #define vl_printfun
1315 #include "{module}.api.h"
1316 #undef vl_printfun
1317
1318 '''
1319
1320     write(hdr.format(module=module))
1321     write('static u16\n')
1322     write('setup_message_id_table (void) {\n')
1323     write('   api_main_t *am = my_api_main;\n')
1324     write('   vl_msg_api_msg_config_t c;\n')
1325     write('   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1326           'VL_MSG_{m}_LAST);\n'
1327           .format(module, crc=file_crc, m=module.upper()))
1328
1329     for d in defines:
1330         write('   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1331               '                                VL_API_{ID} + msg_id_base);\n'
1332               .format(n=d.name, ID=d.name.upper(), crc=d.crc))
1333     for s in services:
1334         d = define_hash[s.caller]
1335         write('   c = (vl_msg_api_msg_config_t) '
1336               ' {{.id = VL_API_{ID} + msg_id_base,\n'
1337               '   .name = "{n}",\n'
1338               '   .handler = vl_api_{n}_t_handler,\n'
1339               '   .cleanup = vl_noop_handler,\n'
1340               '   .endian = vl_api_{n}_t_endian,\n'
1341               '   .print = vl_api_{n}_t_print,\n'
1342               '   .is_autoendian = {auto}}};\n'
1343               .format(n=s.caller, ID=s.caller.upper(),
1344                       auto=d.autoendian))
1345         write('   vl_msg_api_config (&c);\n')
1346         try:
1347             d = define_hash[s.reply]
1348             write('   c = (vl_msg_api_msg_config_t) '
1349                   '{{.id = VL_API_{ID} + msg_id_base,\n'
1350                   '  .name = "{n}",\n'
1351                   '  .handler = 0,\n'
1352                   '  .cleanup = vl_noop_handler,\n'
1353                   '  .endian = vl_api_{n}_t_endian,\n'
1354                   '  .print = vl_api_{n}_t_print,\n'
1355                   '  .is_autoendian = {auto}}};\n'
1356                   .format(n=s.reply, ID=s.reply.upper(),
1357                           auto=d.autoendian))
1358             write('   vl_msg_api_config (&c);\n')
1359         except KeyError:
1360             pass
1361
1362     write('   return msg_id_base;\n')
1363     write('}\n')
1364
1365     severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1366                 'info': 'VL_COUNTER_SEVERITY_INFO',
1367                 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1368
1369     for cnt in counters:
1370         csetname = cnt.name
1371         write('vl_counter_t {}_error_counters[] = {{\n'.format(csetname))
1372         for c in cnt.block:
1373             write('  {\n')
1374             write('   .name = "{}",\n'.format(c['name']))
1375             write('   .desc = "{}",\n'.format(c['description']))
1376             write('   .severity = {},\n'.format(severity[c['severity']]))
1377             write('  },\n')
1378         write('};\n')
1379
1380
1381 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1382                                 stream):
1383     '''Generate code for legacy style VAT. To be deleted.'''
1384     write = stream.write
1385
1386     define_hash = {d.name: d for d in defines}
1387
1388     hdr = '''\
1389 #define vl_endianfun            /* define message structures */
1390 #include "{module}.api.h"
1391 #undef vl_endianfun
1392
1393 /* instantiate all the print functions we know about */
1394 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1395 #define vl_printfun
1396 #include "{module}.api.h"
1397 #undef vl_printfun
1398
1399 '''
1400
1401     write(hdr.format(module=module))
1402     for s in services:
1403         try:
1404             d = define_hash[s.reply]
1405         except KeyError:
1406             continue
1407         if d.manual_print:
1408             write('/*\n'
1409                   ' * Manual definition requested for: \n'
1410                   ' * vl_api_{n}_t_handler()\n'
1411                   ' */\n'
1412                   .format(n=s.reply))
1413             continue
1414         if not define_hash[s.caller].autoreply:
1415             write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1416                   .format(n=s.reply))
1417             continue
1418         write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1419         write('static void\n')
1420         write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1421               .format(n=s.reply))
1422         write('   vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1423         write('   i32 retval = ntohl(mp->retval);\n')
1424         write('   if (vam->async_mode) {\n')
1425         write('      vam->async_errors += (retval < 0);\n')
1426         write('   } else {\n')
1427         write('      vam->retval = retval;\n')
1428         write('      vam->result_ready = 1;\n')
1429         write('   }\n')
1430         write('}\n')
1431         write('#endif\n')
1432
1433         for e in s.events:
1434             if define_hash[e].manual_print:
1435                 continue
1436             write('static void\n')
1437             write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1438             write('    vl_print(0, "{n} event called:");\n'.format(n=e))
1439             write('    vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1440             write('}\n')
1441
1442     write('static void\n')
1443     write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1444     for s in services:
1445         write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1446               '                           "{n}",\n'
1447               '                           vl_api_{n}_t_handler, '
1448               '                           vl_noop_handler,\n'
1449               '                           vl_api_{n}_t_endian, '
1450               '                           vl_api_{n}_t_print,\n'
1451               '                           sizeof(vl_api_{n}_t), 1);\n'
1452               .format(n=s.reply, ID=s.reply.upper()))
1453         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1454               .format(n=s.caller))
1455         try:
1456             write('   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1457                   .format(n=s.caller,
1458                           help=define_hash[s.caller].options['vat_help']))
1459         except KeyError:
1460             pass
1461
1462         # Events
1463         for e in s.events:
1464             write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1465                   '                          "{n}",\n'
1466                   '                           vl_api_{n}_t_handler, '
1467                   '                           vl_noop_handler,\n'
1468                   '                           vl_api_{n}_t_endian, '
1469                   '                           vl_api_{n}_t_print,\n'
1470                   '                           sizeof(vl_api_{n}_t), 1);\n'
1471                   .format(n=e, ID=e.upper()))
1472
1473     write('}\n')
1474     if plugin:
1475         write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1476     else:
1477         write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
1478               .format(module))
1479     write('{\n')
1480     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1481     write('   mainp->vat_main = vam;\n')
1482     write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1483           '                       ("{n}_{crc:08x}");\n'
1484           .format(n=module, crc=file_crc))
1485     write('   if (mainp->msg_id_base == (u16) ~0)\n')
1486     write('      return clib_error_return (0, "{} plugin not loaded...");\n'
1487           .format(module))
1488     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
1489     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1490     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1491     write('#endif\n')
1492     write('   return 0;\n')
1493     write('}\n')
1494
1495
1496 def apifunc(func):
1497     '''Check if a method is generated already.'''
1498     def _f(module, d, processed, *args):
1499         if d.name in processed:
1500             return None
1501         processed[d.name] = True
1502         return func(module, d, *args)
1503     return _f
1504
1505
1506 def c_test_api_service(s, dump, stream):
1507     '''Generate JSON code for a service.'''
1508     write = stream.write
1509
1510     req_reply_template = '''\
1511 static cJSON *
1512 api_{n} (cJSON *o)
1513 {{
1514   vl_api_{n}_t *mp;
1515   int len;
1516   if (!o) return 0;
1517   mp = vl_api_{n}_t_fromjson(o, &len);
1518   if (!mp) {{
1519     fprintf(stderr, "Failed converting JSON to API\\n");
1520     return 0;
1521   }}
1522
1523   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1524   vl_api_{n}_t_endian(mp);
1525   vac_write((char *)mp, len);
1526   free(mp);
1527
1528   /* Read reply */
1529   char *p;
1530   int l;
1531   vac_read(&p, &l, 5); // XXX: Fix timeout
1532     // XXX Will fail in case of event received. Do loop
1533   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1534     fprintf(stderr, "Mismatched reply\\n");
1535     return 0;
1536   }}
1537   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1538   vl_api_{r}_t_endian(rmp);
1539   return vl_api_{r}_t_tojson(rmp);
1540 }}
1541
1542 '''
1543     dump_details_template = '''\
1544 static cJSON *
1545 api_{n} (cJSON *o)
1546 {{
1547   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1548   int len;
1549   if (!o) return 0;
1550   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1551   if (!mp) {{
1552       fprintf(stderr, "Failed converting JSON to API\\n");
1553       return 0;
1554   }}
1555   mp->_vl_msg_id = msg_id;
1556   vl_api_{n}_t_endian(mp);
1557   vac_write((char *)mp, len);
1558   free(mp);
1559
1560   vat2_control_ping(123); // FIX CONTEXT
1561   cJSON *reply = cJSON_CreateArray();
1562
1563   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1564   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1565
1566   while (1) {{
1567     /* Read reply */
1568     char *p;
1569     int l;
1570     vac_read(&p, &l, 5); // XXX: Fix timeout
1571
1572     /* Message can be one of [_details, control_ping_reply
1573      * or unrelated event]
1574      */
1575     u16 reply_msg_id = ntohs(*((u16 *)p));
1576     if (reply_msg_id == ping_reply_msg_id) {{
1577         break;
1578     }}
1579
1580     if (reply_msg_id == details_msg_id) {{
1581         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1582         vl_api_{r}_t_endian(rmp);
1583         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1584     }}
1585   }}
1586   return reply;
1587 }}
1588
1589 '''
1590     gets_details_reply_template = '''\
1591 static cJSON *
1592 api_{n} (cJSON *o)
1593 {{
1594     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1595   int len = 0;
1596   if (!o) return 0;
1597   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1598   if (!mp) {{
1599     fprintf(stderr, "Failed converting JSON to API\\n");
1600     return 0;
1601   }}
1602   mp->_vl_msg_id = msg_id;
1603
1604   vl_api_{n}_t_endian(mp);
1605   vac_write((char *)mp, len);
1606   free(mp);
1607
1608   cJSON *reply = cJSON_CreateArray();
1609
1610   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1611   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1612
1613   while (1) {{
1614     /* Read reply */
1615     char *p;
1616     int l;
1617     vac_read(&p, &l, 5); // XXX: Fix timeout
1618
1619     /* Message can be one of [_details, control_ping_reply
1620      * or unrelated event]
1621      */
1622     u16 msg_id = ntohs(*((u16 *)p));
1623     if (msg_id == reply_msg_id) {{
1624         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1625         vl_api_{r}_t_endian(rmp);
1626         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1627         break;
1628     }}
1629
1630     if (msg_id == details_msg_id) {{
1631         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1632         vl_api_{d}_t_endian(rmp);
1633         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1634     }}
1635   }}
1636   return reply;
1637 }}
1638
1639 '''
1640
1641     if dump:
1642         if s.stream_message:
1643             write(gets_details_reply_template
1644                   .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1645                           R=s.reply.upper(), d=s.stream_message,
1646                           D=s.stream_message.upper()))
1647         else:
1648             write(dump_details_template.format(n=s.caller, r=s.reply,
1649                                                N=s.caller.upper(),
1650                                                R=s.reply.upper()))
1651     else:
1652         write(req_reply_template.format(n=s.caller, r=s.reply,
1653                                         N=s.caller.upper(),
1654                                         R=s.reply.upper()))
1655
1656
1657 def generate_c_test2_boilerplate(services, defines, module, stream):
1658     '''Generate code for VAT2 plugin.'''
1659     write = stream.write
1660
1661     define_hash = {d.name: d for d in defines}
1662     # replies = {}
1663
1664     hdr = '''\
1665 #include <vlibapi/api.h>
1666 #include <vlibmemory/api.h>
1667 #include <vppinfra/error.h>
1668 #include <vnet/ip/ip_format_fns.h>
1669 #include <vnet/ethernet/ethernet_format_fns.h>
1670
1671 #define vl_typedefs             /* define message structures */
1672 #include <vpp/api/vpe_all_api_h.h>
1673 #undef vl_typedefs
1674
1675 #include "{module}.api_enum.h"
1676 #include "{module}.api_types.h"
1677
1678 #define vl_endianfun            /* define message structures */
1679 #include "{module}.api.h"
1680 #undef vl_endianfun
1681
1682 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1683 #define vl_printfun
1684 #include "{module}.api.h"
1685 #undef vl_printfun
1686
1687 #include "{module}.api_tojson.h"
1688 #include "{module}.api_fromjson.h"
1689 #include <vpp-api/client/vppapiclient.h>
1690
1691 #include <vat2/vat2_helpers.h>
1692
1693 '''
1694
1695     write(hdr.format(module=module))
1696
1697     for s in services:
1698         if s.reply not in define_hash:
1699             continue
1700         c_test_api_service(s, s.stream, stream)
1701
1702     write('void vat2_register_function(char *, cJSON * (*)(cJSON *));\n')
1703     # write('__attribute__((constructor))')
1704     write('clib_error_t *\n')
1705     write('vat2_register_plugin (void) {\n')
1706     for s in services:
1707         write('   vat2_register_function("{n}", api_{n});\n'
1708               .format(n=s.caller))
1709     write('   return 0;\n')
1710     write('}\n')
1711
1712
1713 #
1714 # Plugin entry point
1715 #
1716 def run(args, apifilename, s):
1717     '''Main plugin entry point.'''
1718     stream = StringIO()
1719
1720     if not args.outputdir:
1721         sys.stderr.write('Missing --outputdir argument')
1722         return None
1723
1724     basename = os.path.basename(apifilename)
1725     filename, _ = os.path.splitext(basename)
1726     modulename = filename.replace('.', '_')
1727     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1728     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1729     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1730     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1731     filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1732                                      '_test2.c'))
1733     filename_c_tojson = (os.path.join(args.outputdir +
1734                                       '/' + basename + '_tojson.h'))
1735     filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1736                                         basename + '_fromjson.h'))
1737
1738     # Generate separate types file
1739     st = StringIO()
1740     generate_include_types(s, modulename, st)
1741     with open(filename_types, 'w') as fd:
1742         st.seek(0)
1743         shutil.copyfileobj(st, fd)
1744     st.close()
1745
1746     # Generate separate enum file
1747     st = StringIO()
1748     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1749     st.write('#define included_{}_api_enum_h\n'.format(modulename))
1750     generate_include_enum(s, modulename, st)
1751     generate_include_counters(s['Counters'], st)
1752     st.write('#endif\n')
1753     with open(filename_enum, 'w') as fd:
1754         st.seek(0)
1755         shutil.copyfileobj(st, fd)
1756     st.close()
1757
1758     # Generate separate C file
1759     st = StringIO()
1760     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1761                            s['file_crc'], modulename, st)
1762     with open(filename_c, 'w') as fd:
1763         st.seek(0)
1764         shutil.copyfileobj(st, fd)
1765     st.close()
1766
1767     # Generate separate C test file
1768     st = StringIO()
1769     plugin = bool('plugin' in apifilename)
1770     generate_c_test_boilerplate(s['Service'], s['Define'],
1771                                 s['file_crc'],
1772                                 modulename, plugin, st)
1773     with open(filename_c_test, 'w') as fd:
1774         st.seek(0)
1775         shutil.copyfileobj(st, fd)
1776     st.close()
1777
1778     # Fully autogenerated VATv2 C test file
1779     st = StringIO()
1780     generate_c_test2_boilerplate(s['Service'], s['Define'],
1781                                  modulename, st)
1782     with open(filename_c_test2, 'w') as fd:
1783         st.seek(0)
1784         shutil.copyfileobj(st, fd)
1785     st.close()                  #
1786
1787     # Generate separate JSON file
1788     st = StringIO()
1789     generate_tojson(s, modulename, st)
1790     with open(filename_c_tojson, 'w') as fd:
1791         st.seek(0)
1792         shutil.copyfileobj(st, fd)
1793     st.close()
1794     st = StringIO()
1795     generate_fromjson(s, modulename, st)
1796     with open(filename_c_fromjson, 'w') as fd:
1797         st.seek(0)
1798         shutil.copyfileobj(st, fd)
1799     st.close()
1800
1801     output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1802                                     input_filename=basename)
1803     output += generate_imports(s['Import'])
1804     output += msg_ids(s)
1805     output += msg_names(s)
1806     output += msg_name_crc_list(s, filename)
1807     output += typedefs(modulename)
1808     printfun_types(s['types'], stream, modulename)
1809     printfun(s['Define'], stream, modulename)
1810     output += stream.getvalue()
1811     stream.close()
1812     output += endianfun(s['types'] + s['Define'], modulename)
1813     output += version_tuple(s, basename)
1814     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1815                                         file_crc=s['file_crc'])
1816
1817     return output