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