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