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