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