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