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