misc: move part of vpe apis to vlibmemory
[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}) goto error;
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 vlib_error_desc_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               '   .traced = 1,\n'
1348               '   .replay = 1,\n'
1349               '   .is_autoendian = {auto}}};\n'
1350               .format(n=s.caller, ID=s.caller.upper(),
1351                       auto=d.autoendian))
1352         write('   vl_msg_api_config (&c);\n')
1353         try:
1354             d = define_hash[s.reply]
1355             write('   c = (vl_msg_api_msg_config_t) '
1356                   '{{.id = VL_API_{ID} + msg_id_base,\n'
1357                   '  .name = "{n}",\n'
1358                   '  .handler = 0,\n'
1359                   '  .cleanup = vl_noop_handler,\n'
1360                   '  .endian = vl_api_{n}_t_endian,\n'
1361                   '  .print = vl_api_{n}_t_print,\n'
1362                   '  .is_autoendian = {auto}}};\n'
1363                   .format(n=s.reply, ID=s.reply.upper(),
1364                           auto=d.autoendian))
1365             write('   vl_msg_api_config (&c);\n')
1366         except KeyError:
1367             pass
1368
1369     write('   return msg_id_base;\n')
1370     write('}\n')
1371
1372     severity = {'error': 'VL_COUNTER_SEVERITY_ERROR',
1373                 'info': 'VL_COUNTER_SEVERITY_INFO',
1374                 'warn': 'VL_COUNTER_SEVERITY_WARN'}
1375
1376     for cnt in counters:
1377         csetname = cnt.name
1378         write('vlib_error_desc_t {}_error_counters[] = {{\n'.format(csetname))
1379         for c in cnt.block:
1380             write('  {\n')
1381             write('   .name = "{}",\n'.format(c['name']))
1382             write('   .desc = "{}",\n'.format(c['description']))
1383             write('   .severity = {},\n'.format(severity[c['severity']]))
1384             write('  },\n')
1385         write('};\n')
1386
1387
1388 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin,
1389                                 stream):
1390     '''Generate code for legacy style VAT. To be deleted.'''
1391     write = stream.write
1392
1393     define_hash = {d.name: d for d in defines}
1394
1395     hdr = '''\
1396 #define vl_endianfun            /* define message structures */
1397 #include "{module}.api.h"
1398 #undef vl_endianfun
1399
1400 /* instantiate all the print functions we know about */
1401 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1402 #define vl_printfun
1403 #include "{module}.api.h"
1404 #undef vl_printfun
1405
1406 '''
1407
1408     write(hdr.format(module=module))
1409     for s in services:
1410         try:
1411             d = define_hash[s.reply]
1412         except KeyError:
1413             continue
1414         if d.manual_print:
1415             write('/*\n'
1416                   ' * Manual definition requested for: \n'
1417                   ' * vl_api_{n}_t_handler()\n'
1418                   ' */\n'
1419                   .format(n=s.reply))
1420             continue
1421         if not define_hash[s.caller].autoreply:
1422             write('/* Generation not supported (vl_api_{n}_t_handler()) */\n'
1423                   .format(n=s.reply))
1424             continue
1425         write('#ifndef VL_API_{n}_T_HANDLER\n'.format(n=s.reply.upper()))
1426         write('static void\n')
1427         write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'
1428               .format(n=s.reply))
1429         write('   vat_main_t * vam = {}_test_main.vat_main;\n'.format(module))
1430         write('   i32 retval = ntohl(mp->retval);\n')
1431         write('   if (vam->async_mode) {\n')
1432         write('      vam->async_errors += (retval < 0);\n')
1433         write('   } else {\n')
1434         write('      vam->retval = retval;\n')
1435         write('      vam->result_ready = 1;\n')
1436         write('   }\n')
1437         write('}\n')
1438         write('#endif\n')
1439
1440         for e in s.events:
1441             if define_hash[e].manual_print:
1442                 continue
1443             write('static void\n')
1444             write('vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n'.format(n=e))
1445             write('    vl_print(0, "{n} event called:");\n'.format(n=e))
1446             write('    vl_api_{n}_t_print(mp, 0);\n'.format(n=e))
1447             write('}\n')
1448
1449     write('static void\n')
1450     write('setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n')
1451     for s in services:
1452         write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1453               '                           "{n}",\n'
1454               '                           vl_api_{n}_t_handler, '
1455               '                           vl_noop_handler,\n'
1456               '                           vl_api_{n}_t_endian, '
1457               '                           vl_api_{n}_t_print,\n'
1458               '                           sizeof(vl_api_{n}_t), 1);\n'
1459               .format(n=s.reply, ID=s.reply.upper()))
1460         write('   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'
1461               .format(n=s.caller))
1462         try:
1463             write('   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'
1464                   .format(n=s.caller,
1465                           help=define_hash[s.caller].options['vat_help']))
1466         except KeyError:
1467             pass
1468
1469         # Events
1470         for e in s.events:
1471             write('   vl_msg_api_set_handlers(VL_API_{ID} + msg_id_base, '
1472                   '                          "{n}",\n'
1473                   '                           vl_api_{n}_t_handler, '
1474                   '                           vl_noop_handler,\n'
1475                   '                           vl_api_{n}_t_endian, '
1476                   '                           vl_api_{n}_t_print,\n'
1477                   '                           sizeof(vl_api_{n}_t), 1);\n'
1478                   .format(n=e, ID=e.upper()))
1479
1480     write('}\n')
1481     write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1482     write('{\n')
1483     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1484     write('   mainp->vat_main = vam;\n')
1485     write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1486           '                       ("{n}_{crc:08x}");\n'
1487           .format(n=module, crc=file_crc))
1488     write('   if (mainp->msg_id_base == (u16) ~0)\n')
1489     write('      return clib_error_return (0, "{} plugin not loaded...");\n'
1490           .format(module))
1491     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
1492     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1493     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1494     write('#endif\n')
1495     write('   return 0;\n')
1496     write('}\n')
1497
1498
1499 def apifunc(func):
1500     '''Check if a method is generated already.'''
1501     def _f(module, d, processed, *args):
1502         if d.name in processed:
1503             return None
1504         processed[d.name] = True
1505         return func(module, d, *args)
1506     return _f
1507
1508
1509 def c_test_api_service(s, dump, stream):
1510     '''Generate JSON code for a service.'''
1511     write = stream.write
1512
1513     req_reply_template = '''\
1514 static cJSON *
1515 api_{n} (cJSON *o)
1516 {{
1517   vl_api_{n}_t *mp;
1518   int len;
1519   if (!o) return 0;
1520   mp = vl_api_{n}_t_fromjson(o, &len);
1521   if (!mp) {{
1522     fprintf(stderr, "Failed converting JSON to API\\n");
1523     return 0;
1524   }}
1525
1526   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1527   vl_api_{n}_t_endian(mp);
1528   vac_write((char *)mp, len);
1529   free(mp);
1530
1531   /* Read reply */
1532   char *p;
1533   int l;
1534   vac_read(&p, &l, 5); // XXX: Fix timeout
1535   if (p == 0 || l == 0) return 0;
1536     // XXX Will fail in case of event received. Do loop
1537   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1538     fprintf(stderr, "Mismatched reply\\n");
1539     return 0;
1540   }}
1541   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1542   vl_api_{r}_t_endian(rmp);
1543   return vl_api_{r}_t_tojson(rmp);
1544 }}
1545
1546 '''
1547     dump_details_template = '''\
1548 static cJSON *
1549 api_{n} (cJSON *o)
1550 {{
1551   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1552   int len;
1553   if (!o) return 0;
1554   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1555   if (!mp) {{
1556       fprintf(stderr, "Failed converting JSON to API\\n");
1557       return 0;
1558   }}
1559   mp->_vl_msg_id = msg_id;
1560   vl_api_{n}_t_endian(mp);
1561   vac_write((char *)mp, len);
1562   free(mp);
1563
1564   vat2_control_ping(123); // FIX CONTEXT
1565   cJSON *reply = cJSON_CreateArray();
1566
1567   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1568   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1569
1570   while (1) {{
1571     /* Read reply */
1572     char *p;
1573     int l;
1574     vac_read(&p, &l, 5); // XXX: Fix timeout
1575     if (p == 0 || l == 0) {{
1576       cJSON_free(reply);
1577       return 0;
1578     }}
1579
1580     /* Message can be one of [_details, control_ping_reply
1581      * or unrelated event]
1582      */
1583     u16 reply_msg_id = ntohs(*((u16 *)p));
1584     if (reply_msg_id == ping_reply_msg_id) {{
1585         break;
1586     }}
1587
1588     if (reply_msg_id == details_msg_id) {{
1589         if (l < sizeof(vl_api_{r}_t)) {{
1590             cJSON_free(reply);
1591             return 0;
1592         }}
1593         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1594         vl_api_{r}_t_endian(rmp);
1595         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1596     }}
1597   }}
1598   return reply;
1599 }}
1600
1601 '''
1602     gets_details_reply_template = '''\
1603 static cJSON *
1604 api_{n} (cJSON *o)
1605 {{
1606     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1607   int len = 0;
1608   if (!o) return 0;
1609   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1610   if (!mp) {{
1611     fprintf(stderr, "Failed converting JSON to API\\n");
1612     return 0;
1613   }}
1614   mp->_vl_msg_id = msg_id;
1615
1616   vl_api_{n}_t_endian(mp);
1617   vac_write((char *)mp, len);
1618   free(mp);
1619
1620   cJSON *reply = cJSON_CreateArray();
1621
1622   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1623   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1624
1625   while (1) {{
1626     /* Read reply */
1627     char *p;
1628     int l;
1629     vac_read(&p, &l, 5); // XXX: Fix timeout
1630
1631     /* Message can be one of [_details, control_ping_reply
1632      * or unrelated event]
1633      */
1634     u16 msg_id = ntohs(*((u16 *)p));
1635     if (msg_id == reply_msg_id) {{
1636         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1637         vl_api_{r}_t_endian(rmp);
1638         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1639         break;
1640     }}
1641
1642     if (msg_id == details_msg_id) {{
1643         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1644         vl_api_{d}_t_endian(rmp);
1645         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1646     }}
1647   }}
1648   return reply;
1649 }}
1650
1651 '''
1652
1653     if dump:
1654         if s.stream_message:
1655             write(gets_details_reply_template
1656                   .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1657                           R=s.reply.upper(), d=s.stream_message,
1658                           D=s.stream_message.upper()))
1659         else:
1660             write(dump_details_template.format(n=s.caller, r=s.reply,
1661                                                N=s.caller.upper(),
1662                                                R=s.reply.upper()))
1663     else:
1664         write(req_reply_template.format(n=s.caller, r=s.reply,
1665                                         N=s.caller.upper(),
1666                                         R=s.reply.upper()))
1667
1668
1669 def generate_c_test2_boilerplate(services, defines, module, stream):
1670     '''Generate code for VAT2 plugin.'''
1671     write = stream.write
1672
1673     define_hash = {d.name: d for d in defines}
1674     # replies = {}
1675
1676     hdr = '''\
1677 #include <vlibapi/api.h>
1678 #include <vlibmemory/api.h>
1679 #include <vppinfra/error.h>
1680 #include <vnet/ip/ip_format_fns.h>
1681 #include <vnet/ethernet/ethernet_format_fns.h>
1682
1683 #define vl_typedefs             /* define message structures */
1684 #include <vlibmemory/vl_memory_api_h.h>
1685 #include <vpp/api/vpe_types.api.h>
1686 #include <vpp/api/vpe.api.h>
1687 #undef vl_typedefs
1688
1689 #include "{module}.api_enum.h"
1690 #include "{module}.api_types.h"
1691
1692 #define vl_endianfun            /* define message structures */
1693 #include "{module}.api.h"
1694 #undef vl_endianfun
1695
1696 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1697 #define vl_printfun
1698 #include "{module}.api.h"
1699 #undef vl_printfun
1700
1701 #include "{module}.api_tojson.h"
1702 #include "{module}.api_fromjson.h"
1703 #include <vpp-api/client/vppapiclient.h>
1704
1705 #include <vat2/vat2_helpers.h>
1706
1707 '''
1708
1709     write(hdr.format(module=module))
1710
1711     for s in services:
1712         if s.reply not in define_hash:
1713             continue
1714         c_test_api_service(s, s.stream, stream)
1715
1716     write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *));\n')
1717     # write('__attribute__((constructor))')
1718     write('clib_error_t *\n')
1719     write('vat2_register_plugin (void) {\n')
1720     for s in services:
1721         write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson);\n'
1722               .format(n=s.caller))
1723     write('   return 0;\n')
1724     write('}\n')
1725
1726
1727 #
1728 # Plugin entry point
1729 #
1730 def run(args, apifilename, s):
1731     '''Main plugin entry point.'''
1732     stream = StringIO()
1733
1734     if not args.outputdir:
1735         sys.stderr.write('Missing --outputdir argument')
1736         return None
1737
1738     basename = os.path.basename(apifilename)
1739     filename, _ = os.path.splitext(basename)
1740     modulename = filename.replace('.', '_')
1741     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1742     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1743     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1744     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1745     filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1746                                      '_test2.c'))
1747     filename_c_tojson = (os.path.join(args.outputdir +
1748                                       '/' + basename + '_tojson.h'))
1749     filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1750                                         basename + '_fromjson.h'))
1751
1752     # Generate separate types file
1753     st = StringIO()
1754     generate_include_types(s, modulename, st)
1755     with open(filename_types, 'w') as fd:
1756         st.seek(0)
1757         shutil.copyfileobj(st, fd)
1758     st.close()
1759
1760     # Generate separate enum file
1761     st = StringIO()
1762     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1763     st.write('#define included_{}_api_enum_h\n'.format(modulename))
1764     generate_include_enum(s, modulename, st)
1765     generate_include_counters(s['Counters'], st)
1766     st.write('#endif\n')
1767     with open(filename_enum, 'w') as fd:
1768         st.seek(0)
1769         shutil.copyfileobj(st, fd)
1770     st.close()
1771
1772     # Generate separate C file
1773     st = StringIO()
1774     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1775                            s['file_crc'], modulename, st)
1776     with open(filename_c, 'w') as fd:
1777         st.seek(0)
1778         shutil.copyfileobj(st, fd)
1779     st.close()
1780
1781     # Generate separate C test file
1782     st = StringIO()
1783     plugin = bool('plugin' in apifilename)
1784     generate_c_test_boilerplate(s['Service'], s['Define'],
1785                                 s['file_crc'],
1786                                 modulename, plugin, st)
1787     with open(filename_c_test, 'w') as fd:
1788         st.seek(0)
1789         shutil.copyfileobj(st, fd)
1790     st.close()
1791
1792     # Fully autogenerated VATv2 C test file
1793     st = StringIO()
1794     generate_c_test2_boilerplate(s['Service'], s['Define'],
1795                                  modulename, st)
1796     with open(filename_c_test2, 'w') as fd:
1797         st.seek(0)
1798         shutil.copyfileobj(st, fd)
1799     st.close()                  #
1800
1801     # Generate separate JSON file
1802     st = StringIO()
1803     generate_tojson(s, modulename, st)
1804     with open(filename_c_tojson, 'w') as fd:
1805         st.seek(0)
1806         shutil.copyfileobj(st, fd)
1807     st.close()
1808     st = StringIO()
1809     generate_fromjson(s, modulename, st)
1810     with open(filename_c_fromjson, 'w') as fd:
1811         st.seek(0)
1812         shutil.copyfileobj(st, fd)
1813     st.close()
1814
1815     output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1816                                     input_filename=basename)
1817     output += generate_imports(s['Import'])
1818     output += msg_ids(s)
1819     output += msg_names(s)
1820     output += msg_name_crc_list(s, filename)
1821     output += typedefs(modulename)
1822     printfun_types(s['types'], stream, modulename)
1823     printfun(s['Define'], stream, modulename)
1824     output += stream.getvalue()
1825     stream.close()
1826     output += endianfun(s['types'] + s['Define'], modulename)
1827     output += version_tuple(s, basename)
1828     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1829                                         file_crc=s['file_crc'])
1830
1831     return output