vat2: add shared memory argument
[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     if plugin:
1482         write('clib_error_t * vat_plugin_register (vat_main_t *vam)\n')
1483     else:
1484         write('clib_error_t * vat_{}_plugin_register (vat_main_t *vam)\n'
1485               .format(module))
1486     write('{\n')
1487     write('   {n}_test_main_t * mainp = &{n}_test_main;\n'.format(n=module))
1488     write('   mainp->vat_main = vam;\n')
1489     write('   mainp->msg_id_base = vl_client_get_first_plugin_msg_id '
1490           '                       ("{n}_{crc:08x}");\n'
1491           .format(n=module, crc=file_crc))
1492     write('   if (mainp->msg_id_base == (u16) ~0)\n')
1493     write('      return clib_error_return (0, "{} plugin not loaded...");\n'
1494           .format(module))
1495     write('   setup_message_id_table (vam, mainp->msg_id_base);\n')
1496     write('#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n')
1497     write('    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n')
1498     write('#endif\n')
1499     write('   return 0;\n')
1500     write('}\n')
1501
1502
1503 def apifunc(func):
1504     '''Check if a method is generated already.'''
1505     def _f(module, d, processed, *args):
1506         if d.name in processed:
1507             return None
1508         processed[d.name] = True
1509         return func(module, d, *args)
1510     return _f
1511
1512
1513 def c_test_api_service(s, dump, stream):
1514     '''Generate JSON code for a service.'''
1515     write = stream.write
1516
1517     req_reply_template = '''\
1518 static cJSON *
1519 api_{n} (cJSON *o)
1520 {{
1521   vl_api_{n}_t *mp;
1522   int len;
1523   if (!o) return 0;
1524   mp = vl_api_{n}_t_fromjson(o, &len);
1525   if (!mp) {{
1526     fprintf(stderr, "Failed converting JSON to API\\n");
1527     return 0;
1528   }}
1529
1530   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1531   vl_api_{n}_t_endian(mp);
1532   vac_write((char *)mp, len);
1533   free(mp);
1534
1535   /* Read reply */
1536   char *p;
1537   int l;
1538   vac_read(&p, &l, 5); // XXX: Fix timeout
1539   if (p == 0 || l == 0) return 0;
1540     // XXX Will fail in case of event received. Do loop
1541   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1542     fprintf(stderr, "Mismatched reply\\n");
1543     return 0;
1544   }}
1545   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1546   vl_api_{r}_t_endian(rmp);
1547   return vl_api_{r}_t_tojson(rmp);
1548 }}
1549
1550 '''
1551     dump_details_template = '''\
1552 static cJSON *
1553 api_{n} (cJSON *o)
1554 {{
1555   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1556   int len;
1557   if (!o) return 0;
1558   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1559   if (!mp) {{
1560       fprintf(stderr, "Failed converting JSON to API\\n");
1561       return 0;
1562   }}
1563   mp->_vl_msg_id = msg_id;
1564   vl_api_{n}_t_endian(mp);
1565   vac_write((char *)mp, len);
1566   free(mp);
1567
1568   vat2_control_ping(123); // FIX CONTEXT
1569   cJSON *reply = cJSON_CreateArray();
1570
1571   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1572   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1573
1574   while (1) {{
1575     /* Read reply */
1576     char *p;
1577     int l;
1578     vac_read(&p, &l, 5); // XXX: Fix timeout
1579     if (p == 0 || l == 0) {{
1580       cJSON_free(reply);
1581       return 0;
1582     }}
1583
1584     /* Message can be one of [_details, control_ping_reply
1585      * or unrelated event]
1586      */
1587     u16 reply_msg_id = ntohs(*((u16 *)p));
1588     if (reply_msg_id == ping_reply_msg_id) {{
1589         break;
1590     }}
1591
1592     if (reply_msg_id == details_msg_id) {{
1593         if (l < sizeof(vl_api_{r}_t)) {{
1594             cJSON_free(reply);
1595             return 0;
1596         }}
1597         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1598         vl_api_{r}_t_endian(rmp);
1599         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1600     }}
1601   }}
1602   return reply;
1603 }}
1604
1605 '''
1606     gets_details_reply_template = '''\
1607 static cJSON *
1608 api_{n} (cJSON *o)
1609 {{
1610     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1611   int len = 0;
1612   if (!o) return 0;
1613   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1614   if (!mp) {{
1615     fprintf(stderr, "Failed converting JSON to API\\n");
1616     return 0;
1617   }}
1618   mp->_vl_msg_id = msg_id;
1619
1620   vl_api_{n}_t_endian(mp);
1621   vac_write((char *)mp, len);
1622   free(mp);
1623
1624   cJSON *reply = cJSON_CreateArray();
1625
1626   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1627   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1628
1629   while (1) {{
1630     /* Read reply */
1631     char *p;
1632     int l;
1633     vac_read(&p, &l, 5); // XXX: Fix timeout
1634
1635     /* Message can be one of [_details, control_ping_reply
1636      * or unrelated event]
1637      */
1638     u16 msg_id = ntohs(*((u16 *)p));
1639     if (msg_id == reply_msg_id) {{
1640         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1641         vl_api_{r}_t_endian(rmp);
1642         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1643         break;
1644     }}
1645
1646     if (msg_id == details_msg_id) {{
1647         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1648         vl_api_{d}_t_endian(rmp);
1649         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1650     }}
1651   }}
1652   return reply;
1653 }}
1654
1655 '''
1656
1657     if dump:
1658         if s.stream_message:
1659             write(gets_details_reply_template
1660                   .format(n=s.caller, r=s.reply, N=s.caller.upper(),
1661                           R=s.reply.upper(), d=s.stream_message,
1662                           D=s.stream_message.upper()))
1663         else:
1664             write(dump_details_template.format(n=s.caller, r=s.reply,
1665                                                N=s.caller.upper(),
1666                                                R=s.reply.upper()))
1667     else:
1668         write(req_reply_template.format(n=s.caller, r=s.reply,
1669                                         N=s.caller.upper(),
1670                                         R=s.reply.upper()))
1671
1672
1673 def generate_c_test2_boilerplate(services, defines, module, stream):
1674     '''Generate code for VAT2 plugin.'''
1675     write = stream.write
1676
1677     define_hash = {d.name: d for d in defines}
1678     # replies = {}
1679
1680     hdr = '''\
1681 #include <vlibapi/api.h>
1682 #include <vlibmemory/api.h>
1683 #include <vppinfra/error.h>
1684 #include <vnet/ip/ip_format_fns.h>
1685 #include <vnet/ethernet/ethernet_format_fns.h>
1686
1687 #define vl_typedefs             /* define message structures */
1688 #include <vlibmemory/vl_memory_api_h.h>
1689 #include <vpp/api/vpe_types.api.h>
1690 #include <vpp/api/vpe.api.h>
1691 #undef vl_typedefs
1692
1693 #include "{module}.api_enum.h"
1694 #include "{module}.api_types.h"
1695
1696 #define vl_endianfun            /* define message structures */
1697 #include "{module}.api.h"
1698 #undef vl_endianfun
1699
1700 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
1701 #define vl_printfun
1702 #include "{module}.api.h"
1703 #undef vl_printfun
1704
1705 #include "{module}.api_tojson.h"
1706 #include "{module}.api_fromjson.h"
1707 #include <vpp-api/client/vppapiclient.h>
1708
1709 #include <vat2/vat2_helpers.h>
1710
1711 '''
1712
1713     write(hdr.format(module=module))
1714
1715     for s in services:
1716         if s.reply not in define_hash:
1717             continue
1718         c_test_api_service(s, s.stream, stream)
1719
1720     write('void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *));\n')
1721     # write('__attribute__((constructor))')
1722     write('clib_error_t *\n')
1723     write('vat2_register_plugin (void) {\n')
1724     for s in services:
1725         write('   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson);\n'
1726               .format(n=s.caller))
1727     write('   return 0;\n')
1728     write('}\n')
1729
1730
1731 #
1732 # Plugin entry point
1733 #
1734 def run(args, apifilename, s):
1735     '''Main plugin entry point.'''
1736     stream = StringIO()
1737
1738     if not args.outputdir:
1739         sys.stderr.write('Missing --outputdir argument')
1740         return None
1741
1742     basename = os.path.basename(apifilename)
1743     filename, _ = os.path.splitext(basename)
1744     modulename = filename.replace('.', '_')
1745     filename_enum = os.path.join(args.outputdir + '/' + basename + '_enum.h')
1746     filename_types = os.path.join(args.outputdir + '/' + basename + '_types.h')
1747     filename_c = os.path.join(args.outputdir + '/' + basename + '.c')
1748     filename_c_test = os.path.join(args.outputdir + '/' + basename + '_test.c')
1749     filename_c_test2 = (os.path.join(args.outputdir + '/' + basename +
1750                                      '_test2.c'))
1751     filename_c_tojson = (os.path.join(args.outputdir +
1752                                       '/' + basename + '_tojson.h'))
1753     filename_c_fromjson = (os.path.join(args.outputdir + '/' +
1754                                         basename + '_fromjson.h'))
1755
1756     # Generate separate types file
1757     st = StringIO()
1758     generate_include_types(s, modulename, st)
1759     with open(filename_types, 'w') as fd:
1760         st.seek(0)
1761         shutil.copyfileobj(st, fd)
1762     st.close()
1763
1764     # Generate separate enum file
1765     st = StringIO()
1766     st.write('#ifndef included_{}_api_enum_h\n'.format(modulename))
1767     st.write('#define included_{}_api_enum_h\n'.format(modulename))
1768     generate_include_enum(s, modulename, st)
1769     generate_include_counters(s['Counters'], st)
1770     st.write('#endif\n')
1771     with open(filename_enum, 'w') as fd:
1772         st.seek(0)
1773         shutil.copyfileobj(st, fd)
1774     st.close()
1775
1776     # Generate separate C file
1777     st = StringIO()
1778     generate_c_boilerplate(s['Service'], s['Define'], s['Counters'],
1779                            s['file_crc'], modulename, st)
1780     with open(filename_c, 'w') as fd:
1781         st.seek(0)
1782         shutil.copyfileobj(st, fd)
1783     st.close()
1784
1785     # Generate separate C test file
1786     st = StringIO()
1787     plugin = bool('plugin' in apifilename)
1788     generate_c_test_boilerplate(s['Service'], s['Define'],
1789                                 s['file_crc'],
1790                                 modulename, plugin, st)
1791     with open(filename_c_test, 'w') as fd:
1792         st.seek(0)
1793         shutil.copyfileobj(st, fd)
1794     st.close()
1795
1796     # Fully autogenerated VATv2 C test file
1797     st = StringIO()
1798     generate_c_test2_boilerplate(s['Service'], s['Define'],
1799                                  modulename, st)
1800     with open(filename_c_test2, 'w') as fd:
1801         st.seek(0)
1802         shutil.copyfileobj(st, fd)
1803     st.close()                  #
1804
1805     # Generate separate JSON file
1806     st = StringIO()
1807     generate_tojson(s, modulename, st)
1808     with open(filename_c_tojson, 'w') as fd:
1809         st.seek(0)
1810         shutil.copyfileobj(st, fd)
1811     st.close()
1812     st = StringIO()
1813     generate_fromjson(s, modulename, st)
1814     with open(filename_c_fromjson, 'w') as fd:
1815         st.seek(0)
1816         shutil.copyfileobj(st, fd)
1817     st.close()
1818
1819     output = TOP_BOILERPLATE.format(datestring=DATESTRING,
1820                                     input_filename=basename)
1821     output += generate_imports(s['Import'])
1822     output += msg_ids(s)
1823     output += msg_names(s)
1824     output += msg_name_crc_list(s, filename)
1825     output += typedefs(modulename)
1826     printfun_types(s['types'], stream, modulename)
1827     printfun(s['Define'], stream, modulename)
1828     output += stream.getvalue()
1829     stream.close()
1830     output += endianfun(s['types'] + s['Define'], modulename)
1831     output += version_tuple(s, basename)
1832     output += BOTTOM_BOILERPLATE.format(input_filename=basename,
1833                                         file_crc=s['file_crc'])
1834
1835     return output