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