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