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