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