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