vppapigen: fix crash with autoendian arrays
[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  * Note: VL_API_MAX_ARRAY_SIZE is set to an arbitrarily large limit.
757  *
758  * However, any message with a ~2 billion element array is likely to break the
759  * api handling long before this limit causes array element endian issues.
760  *
761  * Applications should be written to create reasonable api messages.
762  */
763 #define VL_API_MAX_ARRAY_SIZE 0x7fffffff
764
765 """
766
767 BOTTOM_BOILERPLATE = """\
768 /****** API CRC (whole file) *****/
769
770 #ifdef vl_api_version
771 vl_api_version({input_filename}, {file_crc:#08x})
772
773 #endif
774 """
775
776
777 def msg_ids(s):
778     """Generate macro to map API message id to handler"""
779     output = """\
780
781 /****** Message ID / handler enum ******/
782
783 #ifdef vl_msg_id
784 """
785
786     for t in s["Define"]:
787         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % (
788             t.name.upper(),
789             t.name,
790         )
791     output += "#endif"
792
793     return output
794
795
796 def msg_names(s):
797     """Generate calls to name mapping macro"""
798     output = """\
799
800 /****** Message names ******/
801
802 #ifdef vl_msg_name
803 """
804
805     for t in s["Define"]:
806         dont_trace = 0 if t.dont_trace else 1
807         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
808     output += "#endif"
809
810     return output
811
812
813 def msg_name_crc_list(s, suffix):
814     """Generate list of names to CRC mappings"""
815     output = """\
816
817 /****** Message name, crc list ******/
818
819 #ifdef vl_msg_name_crc_list
820 """
821     output += "#define foreach_vl_msg_name_crc_%s " % suffix
822
823     for t in s["Define"]:
824         output += "\\\n_(VL_API_%s, %s, %08x) " % (t.name.upper(), t.name, t.crc)
825     output += "\n#endif"
826
827     return output
828
829
830 def api2c(fieldtype):
831     """Map between API type names and internal VPP type names"""
832     mappingtable = {
833         "string": "vl_api_string_t",
834     }
835     if fieldtype in mappingtable:
836         return mappingtable[fieldtype]
837     return fieldtype
838
839
840 def typedefs(filename):
841     """Include in the main files to the types file"""
842     output = """\
843
844 /****** Typedefs ******/
845
846 #ifdef vl_typedefs
847 #include "{include}.api_types.h"
848 #endif
849 """.format(
850         include=filename
851     )
852     return output
853
854
855 FORMAT_STRINGS = {
856     "u8": "%u",
857     "bool": "%u",
858     "i8": "%d",
859     "u16": "%u",
860     "i16": "%d",
861     "u32": "%u",
862     "i32": "%ld",
863     "u64": "%llu",
864     "i64": "%lld",
865     "f64": "%.2f",
866 }
867
868
869 class Printfun:
870     """Functions for pretty printing VPP API messages"""
871
872     _dispatch = {}
873     noprint_fields = {"_vl_msg_id": None, "client_index": None, "context": None}
874
875     def __init__(self, stream):
876         self.stream = stream
877
878     @staticmethod
879     def print_string(o, stream):
880         """Pretty print a vl_api_string_t"""
881         write = stream.write
882         if o.modern_vla:
883             write("    if (vl_api_string_len(&a->{f}) > 0) {{\n".format(f=o.fieldname))
884             write(
885                 '        s = format(s, "\\n%U{f}: %U", '
886                 "format_white_space, indent, "
887                 "vl_api_format_string, (&a->{f}));\n".format(f=o.fieldname)
888             )
889             write("    } else {\n")
890             write(
891                 '        s = format(s, "\\n%U{f}:", '
892                 "format_white_space, indent);\n".format(f=o.fieldname)
893             )
894             write("    }\n")
895         else:
896             write(
897                 '    s = format(s, "\\n%U{f}: %s", '
898                 "format_white_space, indent, a->{f});\n".format(f=o.fieldname)
899             )
900
901     def print_field(self, o, stream):
902         """Pretty print API field"""
903         write = stream.write
904         if o.fieldname in self.noprint_fields:
905             return
906         if o.fieldtype in FORMAT_STRINGS:
907             f = FORMAT_STRINGS[o.fieldtype]
908             write(
909                 '    s = format(s, "\\n%U{n}: {f}", '
910                 "format_white_space, indent, a->{n});\n".format(n=o.fieldname, f=f)
911             )
912         else:
913             write(
914                 '    s = format(s, "\\n%U{n}: %U", '
915                 "format_white_space, indent, "
916                 "format_{t}, &a->{n}, indent);\n".format(n=o.fieldname, t=o.fieldtype)
917             )
918
919     _dispatch["Field"] = print_field
920
921     def print_array(self, o, stream):
922         """Pretty print API array"""
923         write = stream.write
924
925         forloop = """\
926     for (i = 0; i < {lfield}; i++) {{
927         s = format(s, "\\n%U{n}: %U",
928                    format_white_space, indent, format_{t}, &a->{n}[i], indent);
929     }}
930 """
931
932         forloop_format = """\
933     for (i = 0; i < {lfield}; i++) {{
934         s = format(s, "\\n%U{n}: {t}",
935                    format_white_space, indent, a->{n}[i]);
936     }}
937 """
938
939         if o.fieldtype == "string":
940             self.print_string(o, stream)
941             return
942
943         if o.fieldtype == "u8":
944             if o.lengthfield:
945                 write(
946                     '    s = format(s, "\\n%U{n}: %U", format_white_space, '
947                     "indent, format_hex_bytes, a->{n}, a->{lfield});\n".format(
948                         n=o.fieldname, lfield=o.lengthfield
949                     )
950                 )
951             else:
952                 write(
953                     '    s = format(s, "\\n%U{n}: %U", format_white_space, '
954                     "indent, format_hex_bytes, a, {lfield});\n".format(
955                         n=o.fieldname, lfield=o.length
956                     )
957                 )
958             return
959
960         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
961         if o.fieldtype in FORMAT_STRINGS:
962             write(
963                 forloop_format.format(
964                     lfield=lfield, t=FORMAT_STRINGS[o.fieldtype], n=o.fieldname
965                 )
966             )
967         else:
968             write(forloop.format(lfield=lfield, t=o.fieldtype, n=o.fieldname))
969
970     _dispatch["Array"] = print_array
971
972     @staticmethod
973     def print_alias(k, v, stream):
974         """Pretty print type alias"""
975         write = stream.write
976         if "length" in v.alias and v.alias["length"] and v.alias["type"] == "u8":
977             write(
978                 '    return format(s, "%U", format_hex_bytes, a, {});\n'.format(
979                     v.alias["length"]
980                 )
981             )
982         elif v.alias["type"] in FORMAT_STRINGS:
983             write(
984                 '    return format(s, "{}", *a);\n'.format(
985                     FORMAT_STRINGS[v.alias["type"]]
986                 )
987             )
988         else:
989             write('    return format(s, "{} (print not implemented)");\n'.format(k))
990
991     @staticmethod
992     def print_enum(o, stream):
993         """Pretty print API enum"""
994         write = stream.write
995         write("    switch(*a) {\n")
996         for b in o:
997             write("    case %s:\n" % b[1])
998             write('        return format(s, "{}");\n'.format(b[0]))
999         write("    }\n")
1000
1001     _dispatch["Enum"] = print_enum
1002     _dispatch["EnumFlag"] = print_enum
1003
1004     def print_obj(self, o, stream):
1005         """Entry point"""
1006         write = stream.write
1007
1008         if o.type in self._dispatch:
1009             self._dispatch[o.type](self, o, stream)
1010         else:
1011             write(
1012                 '    s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1013                     o.type, o.fieldtype, o.fieldname
1014                 )
1015             )
1016
1017
1018 def printfun(objs, stream, modulename):
1019     """Main entry point for pretty print function generation"""
1020     write = stream.write
1021
1022     h = """\
1023 /****** Print functions *****/
1024 #ifdef vl_printfun
1025 #ifndef included_{module}_printfun
1026 #define included_{module}_printfun
1027
1028 #ifdef LP64
1029 #define _uword_fmt \"%lld\"
1030 #define _uword_cast (long long)
1031 #else
1032 #define _uword_fmt \"%ld\"
1033 #define _uword_cast long
1034 #endif
1035
1036 #include "{module}.api_tojson.h"
1037 #include "{module}.api_fromjson.h"
1038
1039 """
1040
1041     signature = """\
1042 static inline u8 *vl_api_{name}_t_format (u8 *s,  va_list *args)
1043 {{
1044     __attribute__((unused)) vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1045     u32 indent __attribute__((unused)) = 2;
1046     int i __attribute__((unused));
1047 """
1048
1049     h = h.format(module=modulename)
1050     write(h)
1051
1052     pp = Printfun(stream)
1053     for t in objs:
1054         if t.manual_print:
1055             write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1056             continue
1057         write(signature.format(name=t.name, suffix=""))
1058         write("    /* Message definition: vl_api_{}_t: */\n".format(t.name))
1059         write('    s = format(s, "vl_api_%s_t:");\n' % t.name)
1060         for o in t.block:
1061             pp.print_obj(o, stream)
1062         write("    return s;\n")
1063         write("}\n\n")
1064
1065     write("\n#endif")
1066     write("\n#endif /* vl_printfun */\n")
1067
1068     return ""
1069
1070
1071 def printfun_types(objs, stream, modulename):
1072     """Pretty print API types"""
1073     write = stream.write
1074     pp = Printfun(stream)
1075
1076     h = """\
1077 /****** Print functions *****/
1078 #ifdef vl_printfun
1079 #ifndef included_{module}_printfun_types
1080 #define included_{module}_printfun_types
1081
1082 """
1083     h = h.format(module=modulename)
1084     write(h)
1085
1086     signature = """\
1087 static inline u8 *format_vl_api_{name}_t (u8 *s, va_list * args)
1088 {{
1089     vl_api_{name}_t *a = va_arg (*args, vl_api_{name}_t *);
1090     u32 indent __attribute__((unused)) = va_arg (*args, u32);
1091     int i __attribute__((unused));
1092     indent += 2;
1093 """
1094
1095     for t in objs:
1096         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1097             write(signature.format(name=t.name))
1098             pp.print_enum(t.block, stream)
1099             write("    return s;\n")
1100             write("}\n\n")
1101             continue
1102
1103         if t.manual_print:
1104             write("/***** manual: vl_api_%s_t_format *****/\n\n" % t.name)
1105             continue
1106
1107         if t.__class__.__name__ == "Using":
1108             write(signature.format(name=t.name))
1109             pp.print_alias(t.name, t, stream)
1110             write("}\n\n")
1111             continue
1112
1113         write(signature.format(name=t.name))
1114         for o in t.block:
1115             pp.print_obj(o, stream)
1116
1117         write("    return s;\n")
1118         write("}\n\n")
1119
1120     write("\n#endif")
1121     write("\n#endif /* vl_printfun_types */\n")
1122
1123
1124 def generate_imports(imports):
1125     """Add #include matching the API import statements"""
1126     output = "/* Imported API files */\n"
1127     output += "#ifndef vl_api_version\n"
1128
1129     for i in imports:
1130         s = i.filename.replace("plugins/", "")
1131         output += "#include <{}.h>\n".format(s)
1132     output += "#endif\n"
1133     return output
1134
1135
1136 ENDIAN_STRINGS = {
1137     "u16": "clib_net_to_host_u16",
1138     "u32": "clib_net_to_host_u32",
1139     "u64": "clib_net_to_host_u64",
1140     "i16": "clib_net_to_host_i16",
1141     "i32": "clib_net_to_host_i32",
1142     "i64": "clib_net_to_host_i64",
1143     "f64": "clib_net_to_host_f64",
1144 }
1145
1146
1147 def get_endian_string(o, type):
1148     """Return proper endian string conversion function"""
1149     try:
1150         if o.to_network:
1151             return ENDIAN_STRINGS[type].replace("net_to_host", "host_to_net")
1152     except:
1153         pass
1154     return ENDIAN_STRINGS[type]
1155
1156
1157 def endianfun_array(o):
1158     """Generate endian functions for arrays"""
1159     forloop = """\
1160     {comment}
1161     ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE);
1162     for (i = 0; i < {length}; i++) {{
1163         a->{name}[i] = {format}(a->{name}[i]);
1164     }}
1165 """
1166
1167     forloop_format = """\
1168     for (i = 0; i < {length}; i++) {{
1169         {type}_endian(&a->{name}[i]);
1170     }}
1171 """
1172
1173     to_network_comment = ""
1174     try:
1175         if o.to_network:
1176             to_network_comment = """/*
1177      * Array fields processed first to handle variable length arrays and size
1178      * field endian conversion in the proper order for to-network messages.
1179      * Message fields have been sorted by type in the code generator, thus fields
1180      * in this generated code may be converted in a different order than specified
1181      * in the *.api file.
1182      */"""
1183     except:
1184         pass
1185
1186     output = ""
1187     if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
1188         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1189     else:
1190         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
1191         if o.fieldtype in ENDIAN_STRINGS:
1192             output += forloop.format(
1193                 comment=to_network_comment,
1194                 length=lfield,
1195                 format=get_endian_string(o, o.fieldtype),
1196                 name=o.fieldname,
1197             )
1198         else:
1199             output += forloop_format.format(
1200                 length=lfield, type=o.fieldtype, name=o.fieldname
1201             )
1202     return output
1203
1204
1205 NO_ENDIAN_CONVERSION = {"client_index": None}
1206
1207
1208 def endianfun_obj(o):
1209     """Generate endian conversion function for type"""
1210     output = ""
1211     if o.type == "Array":
1212         return endianfun_array(o)
1213     if o.type != "Field":
1214         output += '    s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1215             o.type, o.fieldtype, o.fieldname
1216         )
1217         return output
1218     if o.fieldname in NO_ENDIAN_CONVERSION:
1219         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1220         return output
1221     if o.fieldtype in ENDIAN_STRINGS:
1222         output += "    a->{name} = {format}(a->{name});\n".format(
1223             name=o.fieldname, format=get_endian_string(o, o.fieldtype)
1224         )
1225     elif o.fieldtype.startswith("vl_api_"):
1226         output += "    {type}_endian(&a->{name});\n".format(
1227             type=o.fieldtype, name=o.fieldname
1228         )
1229     else:
1230         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1231
1232     return output
1233
1234
1235 def endianfun(objs, modulename):
1236     """Main entry point for endian function generation"""
1237     output = """\
1238
1239 /****** Endian swap functions *****/\n\
1240 #ifdef vl_endianfun
1241 #ifndef included_{module}_endianfun
1242 #define included_{module}_endianfun
1243
1244 #undef clib_net_to_host_uword
1245 #undef clib_host_to_net_uword
1246 #ifdef LP64
1247 #define clib_net_to_host_uword clib_net_to_host_u64
1248 #define clib_host_to_net_uword clib_host_to_net_u64
1249 #else
1250 #define clib_net_to_host_uword clib_net_to_host_u32
1251 #define clib_host_to_net_uword clib_host_to_net_u32
1252 #endif
1253
1254 """
1255     output = output.format(module=modulename)
1256
1257     signature = """\
1258 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a)
1259 {{
1260     int i __attribute__((unused));
1261 """
1262
1263     for t in objs:
1264         # Outbound (to network) messages are identified by message nomenclature
1265         # i.e. message names ending with these suffixes are 'to network'
1266         if t.name.endswith("_reply") or t.name.endswith("_details"):
1267             t.to_network = True
1268         else:
1269             t.to_network = False
1270
1271         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1272             output += signature.format(name=t.name)
1273             if t.enumtype in ENDIAN_STRINGS:
1274                 output += "    *a = {}(*a);\n".format(get_endian_string(t, t.enumtype))
1275             else:
1276                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1277                     name=t.name
1278                 )
1279
1280             output += "}\n\n"
1281             continue
1282
1283         if t.manual_endian:
1284             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1285             continue
1286
1287         if t.__class__.__name__ == "Using":
1288             output += signature.format(name=t.name)
1289             if "length" in t.alias and t.alias["length"] and t.alias["type"] == "u8":
1290                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1291                     name=t.name
1292                 )
1293             elif t.alias["type"] in FORMAT_STRINGS:
1294                 output += "    *a = {}(*a);\n".format(
1295                     get_endian_string(t, t.alias["type"])
1296                 )
1297             else:
1298                 output += "    /* Not Implemented yet {} */".format(t.name)
1299             output += "}\n\n"
1300             continue
1301
1302         output += signature.format(name=t.name)
1303
1304         # For outbound (to network) messages:
1305         #   some arrays have dynamic length -- iterate over
1306         #   them before changing endianness for the length field
1307         #   by making the Array types show up first
1308         if t.to_network:
1309             t.block.sort(key=lambda x: x.type)
1310
1311         for o in t.block:
1312             o.to_network = t.to_network
1313             output += endianfun_obj(o)
1314         output += "}\n\n"
1315
1316     output += "\n#endif"
1317     output += "\n#endif /* vl_endianfun */\n\n"
1318
1319     return output
1320
1321
1322 def calc_size_fun(objs, modulename):
1323     """Main entry point for calculate size function generation"""
1324     output = """\
1325
1326 /****** Calculate size functions *****/\n\
1327 #ifdef vl_calcsizefun
1328 #ifndef included_{module}_calcsizefun
1329 #define included_{module}_calcsizefun
1330
1331 """
1332     output = output.format(module=modulename)
1333
1334     signature = """\
1335 /* calculate message size of message in network byte order */
1336 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1337 {{
1338 """
1339
1340     for o in objs:
1341         tname = o.__class__.__name__
1342
1343         output += signature.format(name=o.name)
1344         output += f"      return sizeof(*a)"
1345         if tname == "Using":
1346             if "length" in o.alias:
1347                 try:
1348                     tmp = int(o.alias["length"])
1349                     if tmp == 0:
1350                         raise (f"Unexpected length '0' for alias {o}")
1351                 except:
1352                     # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1353                     print("culprit:")
1354                     print(o)
1355                     print(dir(o.alias))
1356                     print(o.alias)
1357                     raise
1358         elif tname == "Enum" or tname == "EnumFlag":
1359             pass
1360         else:
1361             for b in o.block:
1362                 if b.type == "Option":
1363                     continue
1364                 elif b.type == "Field":
1365                     if b.fieldtype.startswith("vl_api_"):
1366                         output += f" - sizeof(a->{b.fieldname})"
1367                         output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1368                 elif b.type == "Array":
1369                     if b.lengthfield:
1370                         m = list(
1371                             filter(lambda x: x.fieldname == b.lengthfield, o.block)
1372                         )
1373                         if len(m) != 1:
1374                             raise Exception(
1375                                 f"Expected 1 match for field '{b.lengthfield}', got '{m}'"
1376                             )
1377                         lf = m[0]
1378                         if lf.fieldtype in ENDIAN_STRINGS:
1379                             output += f" + {get_endian_string(b, lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1380                         elif lf.fieldtype == "u8":
1381                             output += (
1382                                 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1383                             )
1384                         else:
1385                             raise Exception(
1386                                 f"Don't know how to endian swap {lf.fieldtype}"
1387                             )
1388                     else:
1389                         # Fixed length strings decay to nul terminated u8
1390                         if b.fieldtype == "string":
1391                             if b.modern_vla:
1392                                 output += f" + vl_api_string_len(&a->{b.fieldname})"
1393
1394         output += ";\n"
1395         output += "}\n\n"
1396     output += "\n#endif"
1397     output += "\n#endif /* vl_calcsizefun */\n\n"
1398
1399     return output
1400
1401
1402 def version_tuple(s, module):
1403     """Generate semantic version string"""
1404     output = """\
1405 /****** Version tuple *****/
1406
1407 #ifdef vl_api_version_tuple
1408
1409 """
1410     if "version" in s["Option"]:
1411         v = s["Option"]["version"]
1412         (major, minor, patch) = v.split(".")
1413         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % (
1414             module,
1415             major,
1416             minor,
1417             patch,
1418         )
1419
1420     output += "\n#endif /* vl_api_version_tuple */\n\n"
1421
1422     return output
1423
1424
1425 def generate_include_enum(s, module, stream):
1426     """Generate <name>.api_enum.h"""
1427     write = stream.write
1428
1429     if "Define" in s:
1430         write("typedef enum {\n")
1431         for t in s["Define"]:
1432             write("   VL_API_{},\n".format(t.name.upper()))
1433         write("   VL_MSG_{}_LAST\n".format(module.upper()))
1434         write("}} vl_api_{}_enum_t;\n".format(module))
1435
1436
1437 def generate_include_counters(s, stream):
1438     """Include file for the counter data model types."""
1439     write = stream.write
1440
1441     for counters in s:
1442         csetname = counters.name
1443         write("typedef enum {\n")
1444         for c in counters.block:
1445             write("   {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
1446         write("   {}_N_ERROR\n".format(csetname.upper()))
1447         write("}} vl_counter_{}_enum_t;\n".format(csetname))
1448
1449         write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
1450
1451
1452 def generate_include_types(s, module, stream):
1453     """Generate separate API _types file."""
1454     write = stream.write
1455
1456     write("#ifndef included_{module}_api_types_h\n".format(module=module))
1457     write("#define included_{module}_api_types_h\n".format(module=module))
1458
1459     if "version" in s["Option"]:
1460         v = s["Option"]["version"]
1461         (major, minor, patch) = v.split(".")
1462         write(
1463             "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
1464                 m=module.upper(), v=major
1465             )
1466         )
1467         write(
1468             "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
1469                 m=module.upper(), v=minor
1470             )
1471         )
1472         write(
1473             "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
1474                 m=module.upper(), v=patch
1475             )
1476         )
1477
1478     if "Import" in s:
1479         write("/* Imported API files */\n")
1480         for i in s["Import"]:
1481             filename = i.filename.replace("plugins/", "")
1482             write("#include <{}_types.h>\n".format(filename))
1483
1484     for o in itertools.chain(s["types"], s["Define"]):
1485         tname = o.__class__.__name__
1486         if tname == "Using":
1487             if "length" in o.alias:
1488                 write(
1489                     "typedef %s vl_api_%s_t[%s];\n"
1490                     % (o.alias["type"], o.name, o.alias["length"])
1491                 )
1492             else:
1493                 write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
1494         elif tname == "Enum" or tname == "EnumFlag":
1495             if o.enumtype == "u32":
1496                 write("typedef enum {\n")
1497             else:
1498                 write("typedef enum __attribute__((packed)) {\n")
1499
1500             for b in o.block:
1501                 write("    %s = %s,\n" % (b[0], b[1]))
1502             write("} vl_api_%s_t;\n" % o.name)
1503             if o.enumtype != "u32":
1504                 size1 = "sizeof(vl_api_%s_t)" % o.name
1505                 size2 = "sizeof(%s)" % o.enumtype
1506                 err_str = "size of API enum %s is wrong" % o.name
1507                 write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
1508         else:
1509             if tname == "Union":
1510                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
1511             else:
1512                 write(
1513                     ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
1514                 )
1515             for b in o.block:
1516                 if b.type == "Option":
1517                     continue
1518                 if b.type == "Field":
1519                     write("    %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
1520                 elif b.type == "Array":
1521                     if b.lengthfield:
1522                         write("    %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
1523                     else:
1524                         # Fixed length strings decay to nul terminated u8
1525                         if b.fieldtype == "string":
1526                             if b.modern_vla:
1527                                 write(
1528                                     "    {} {};\n".format(
1529                                         api2c(b.fieldtype), b.fieldname
1530                                     )
1531                                 )
1532                             else:
1533                                 write("    u8 {}[{}];\n".format(b.fieldname, b.length))
1534                         else:
1535                             write(
1536                                 "    %s %s[%s];\n"
1537                                 % (api2c(b.fieldtype), b.fieldname, b.length)
1538                             )
1539                 else:
1540                     raise ValueError(
1541                         "Error in processing type {} for {}".format(b, o.name)
1542                     )
1543
1544             write("} vl_api_%s_t;\n" % o.name)
1545             write(
1546                 f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
1547             )
1548
1549     for t in s["Define"]:
1550         write(
1551             '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
1552                 n=t.name, ID=t.name.upper(), crc=t.crc
1553             )
1554         )
1555
1556     write("\n#endif\n")
1557
1558
1559 def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
1560     """VPP side plugin."""
1561     write = stream.write
1562     define_hash = {d.name: d for d in defines}
1563
1564     hdr = """\
1565 #define vl_endianfun            /* define message structures */
1566 #include "{module}.api.h"
1567 #undef vl_endianfun
1568
1569 #define vl_calcsizefun
1570 #include "{module}.api.h"
1571 #undef vl_calsizefun
1572
1573 /* instantiate all the print functions we know about */
1574 #define vl_printfun
1575 #include "{module}.api.h"
1576 #undef vl_printfun
1577
1578 """
1579
1580     write(hdr.format(module=module))
1581     if len(defines) > 0:
1582         write("static u16\n")
1583         write("setup_message_id_table (void) {\n")
1584         write("   api_main_t *am = my_api_main;\n")
1585         write("   vl_msg_api_msg_config_t c;\n")
1586         write(
1587             '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1588             "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1589         )
1590
1591     for d in defines:
1592         write(
1593             '   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1594             "                                VL_API_{ID} + msg_id_base);\n".format(
1595                 n=d.name, ID=d.name.upper(), crc=d.crc
1596             )
1597         )
1598     for s in services:
1599         d = define_hash[s.caller]
1600         write(
1601             "   c = (vl_msg_api_msg_config_t) "
1602             " {{.id = VL_API_{ID} + msg_id_base,\n"
1603             '   .name = "{n}",\n'
1604             "   .handler = vl_api_{n}_t_handler,\n"
1605             "   .endian = vl_api_{n}_t_endian,\n"
1606             "   .format_fn = vl_api_{n}_t_format,\n"
1607             "   .traced = 1,\n"
1608             "   .replay = 1,\n"
1609             "   .tojson = vl_api_{n}_t_tojson,\n"
1610             "   .fromjson = vl_api_{n}_t_fromjson,\n"
1611             "   .calc_size = vl_api_{n}_t_calc_size,\n"
1612             "   .is_autoendian = {auto}}};\n".format(
1613                 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1614             )
1615         )
1616         write("   vl_msg_api_config (&c);\n")
1617         try:
1618             d = define_hash[s.reply]
1619             write(
1620                 "   c = (vl_msg_api_msg_config_t) "
1621                 "{{.id = VL_API_{ID} + msg_id_base,\n"
1622                 '  .name = "{n}",\n'
1623                 "  .handler = 0,\n"
1624                 "  .endian = vl_api_{n}_t_endian,\n"
1625                 "  .format_fn = vl_api_{n}_t_format,\n"
1626                 "  .traced = 1,\n"
1627                 "  .replay = 1,\n"
1628                 "  .tojson = vl_api_{n}_t_tojson,\n"
1629                 "  .fromjson = vl_api_{n}_t_fromjson,\n"
1630                 "  .calc_size = vl_api_{n}_t_calc_size,\n"
1631                 "  .is_autoendian = {auto}}};\n".format(
1632                     n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1633                 )
1634             )
1635             write("   vl_msg_api_config (&c);\n")
1636         except KeyError:
1637             pass
1638
1639         try:
1640             if s.stream:
1641                 d = define_hash[s.stream_message]
1642                 write(
1643                     "   c = (vl_msg_api_msg_config_t) "
1644                     "{{.id = VL_API_{ID} + msg_id_base,\n"
1645                     '  .name = "{n}",\n'
1646                     "  .handler = 0,\n"
1647                     "  .endian = vl_api_{n}_t_endian,\n"
1648                     "  .format_fn = vl_api_{n}_t_format,\n"
1649                     "  .traced = 1,\n"
1650                     "  .replay = 1,\n"
1651                     "  .tojson = vl_api_{n}_t_tojson,\n"
1652                     "  .fromjson = vl_api_{n}_t_fromjson,\n"
1653                     "  .calc_size = vl_api_{n}_t_calc_size,\n"
1654                     "  .is_autoendian = {auto}}};\n".format(
1655                         n=s.stream_message,
1656                         ID=s.stream_message.upper(),
1657                         auto=d.autoendian,
1658                     )
1659                 )
1660                 write("   vl_msg_api_config (&c);\n")
1661         except KeyError:
1662             pass
1663     if len(defines) > 0:
1664         write("   return msg_id_base;\n")
1665         write("}\n")
1666
1667     severity = {
1668         "error": "VL_COUNTER_SEVERITY_ERROR",
1669         "info": "VL_COUNTER_SEVERITY_INFO",
1670         "warn": "VL_COUNTER_SEVERITY_WARN",
1671     }
1672
1673     for cnt in counters:
1674         csetname = cnt.name
1675         write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1676         for c in cnt.block:
1677             write("  {\n")
1678             write('   .name = "{}",\n'.format(c["name"]))
1679             write('   .desc = "{}",\n'.format(c["description"]))
1680             write("   .severity = {},\n".format(severity[c["severity"]]))
1681             write("  },\n")
1682         write("};\n")
1683
1684
1685 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1686     """Generate code for legacy style VAT. To be deleted."""
1687     write = stream.write
1688
1689     define_hash = {d.name: d for d in defines}
1690
1691     hdr = """\
1692 #define vl_endianfun            /* define message structures */
1693 #include "{module}.api.h"
1694 #undef vl_endianfun
1695
1696 #define vl_calcsizefun
1697 #include "{module}.api.h"
1698 #undef vl_calsizefun
1699
1700 /* instantiate all the print functions we know about */
1701 #define vl_printfun
1702 #include "{module}.api.h"
1703 #undef vl_printfun
1704
1705 """
1706
1707     write(hdr.format(module=module))
1708     for s in services:
1709         try:
1710             d = define_hash[s.reply]
1711         except KeyError:
1712             continue
1713         if d.manual_print:
1714             write(
1715                 "/*\n"
1716                 " * Manual definition requested for: \n"
1717                 " * vl_api_{n}_t_handler()\n"
1718                 " */\n".format(n=s.reply)
1719             )
1720             continue
1721         if not define_hash[s.caller].autoreply:
1722             write(
1723                 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1724                     n=s.reply
1725                 )
1726             )
1727             continue
1728         write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1729         write("static void\n")
1730         write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1731         write("   vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1732         write("   i32 retval = ntohl(mp->retval);\n")
1733         write("   if (vam->async_mode) {\n")
1734         write("      vam->async_errors += (retval < 0);\n")
1735         write("   } else {\n")
1736         write("      vam->retval = retval;\n")
1737         write("      vam->result_ready = 1;\n")
1738         write("   }\n")
1739         write("}\n")
1740         write("#endif\n")
1741
1742         for e in s.events:
1743             if define_hash[e].manual_print:
1744                 continue
1745             write("static void\n")
1746             write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1747             write('    vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
1748             write(
1749                 '    vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
1750             )
1751             write("}\n")
1752
1753     write("static void\n")
1754     write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1755     for s in services:
1756         write(
1757             "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1758             "    .id = VL_API_{ID} + msg_id_base,\n"
1759             '    .name = "{n}",\n'
1760             "    .handler = vl_api_{n}_t_handler,\n"
1761             "    .endian = vl_api_{n}_t_endian,\n"
1762             "    .format_fn = vl_api_{n}_t_format,\n"
1763             "    .size = sizeof(vl_api_{n}_t),\n"
1764             "    .traced = 1,\n"
1765             "    .tojson = vl_api_{n}_t_tojson,\n"
1766             "    .fromjson = vl_api_{n}_t_fromjson,\n"
1767             "    .calc_size = vl_api_{n}_t_calc_size,\n"
1768             "   }});".format(n=s.reply, ID=s.reply.upper())
1769         )
1770         write(
1771             '   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1772                 n=s.caller
1773             )
1774         )
1775         try:
1776             write(
1777                 '   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1778                     n=s.caller, help=define_hash[s.caller].options["vat_help"]
1779                 )
1780             )
1781         except KeyError:
1782             pass
1783
1784         # Events
1785         for e in s.events:
1786             write(
1787                 "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1788                 "    .id = VL_API_{ID} + msg_id_base,\n"
1789                 '    .name = "{n}",\n'
1790                 "    .handler = vl_api_{n}_t_handler,\n"
1791                 "    .endian = vl_api_{n}_t_endian,\n"
1792                 "    .format_fn = vl_api_{n}_t_format,\n"
1793                 "    .size = sizeof(vl_api_{n}_t),\n"
1794                 "    .traced = 1,\n"
1795                 "    .tojson = vl_api_{n}_t_tojson,\n"
1796                 "    .fromjson = vl_api_{n}_t_fromjson,\n"
1797                 "    .calc_size = vl_api_{n}_t_calc_size,\n"
1798                 "   }});".format(n=e, ID=e.upper())
1799             )
1800
1801     write("}\n")
1802     write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1803     write("{\n")
1804     write("   {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1805     write("   mainp->vat_main = vam;\n")
1806     write(
1807         "   mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1808         '                       ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1809     )
1810     write("   if (mainp->msg_id_base == (u16) ~0)\n")
1811     write(
1812         '      return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1813             module
1814         )
1815     )
1816     write("   setup_message_id_table (vam, mainp->msg_id_base);\n")
1817     write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1818     write("    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1819     write("#endif\n")
1820     write("   return 0;\n")
1821     write("}\n")
1822
1823
1824 def apifunc(func):
1825     """Check if a method is generated already."""
1826
1827     def _f(module, d, processed, *args):
1828         if d.name in processed:
1829             return None
1830         processed[d.name] = True
1831         return func(module, d, *args)
1832
1833     return _f
1834
1835
1836 def c_test_api_service(s, dump, stream):
1837     """Generate JSON code for a service."""
1838     write = stream.write
1839
1840     req_reply_template = """\
1841 static cJSON *
1842 api_{n} (cJSON *o)
1843 {{
1844   vl_api_{n}_t *mp;
1845   int len;
1846   if (!o) return 0;
1847   mp = vl_api_{n}_t_fromjson(o, &len);
1848   if (!mp) {{
1849     fprintf(stderr, "Failed converting JSON to API\\n");
1850     return 0;
1851   }}
1852
1853   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1854   vl_api_{n}_t_endian(mp);
1855   vac_write((char *)mp, len);
1856   cJSON_free(mp);
1857
1858   /* Read reply */
1859   char *p;
1860   int l;
1861   vac_read(&p, &l, 5); // XXX: Fix timeout
1862   if (p == 0 || l == 0) return 0;
1863     // XXX Will fail in case of event received. Do loop
1864   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1865     fprintf(stderr, "Mismatched reply\\n");
1866     return 0;
1867   }}
1868   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1869   vl_api_{r}_t_endian(rmp);
1870   return vl_api_{r}_t_tojson(rmp);
1871 }}
1872
1873 """
1874     dump_details_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;
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   vl_api_{n}_t_endian(mp);
1888   vac_write((char *)mp, len);
1889   cJSON_free(mp);
1890
1891   vat2_control_ping(123); // FIX CONTEXT
1892   cJSON *reply = cJSON_CreateArray();
1893
1894   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1895   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1896
1897   while (1) {{
1898     /* Read reply */
1899     char *p;
1900     int l;
1901     vac_read(&p, &l, 5); // XXX: Fix timeout
1902     if (p == 0 || l == 0) {{
1903       cJSON_free(reply);
1904       return 0;
1905     }}
1906
1907     /* Message can be one of [_details, control_ping_reply
1908      * or unrelated event]
1909      */
1910     u16 reply_msg_id = ntohs(*((u16 *)p));
1911     if (reply_msg_id == ping_reply_msg_id) {{
1912         break;
1913     }}
1914
1915     if (reply_msg_id == details_msg_id) {{
1916         if (l < sizeof(vl_api_{r}_t)) {{
1917             cJSON_free(reply);
1918             return 0;
1919         }}
1920         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1921         vl_api_{r}_t_endian(rmp);
1922         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1923     }}
1924   }}
1925   return reply;
1926 }}
1927
1928 """
1929     gets_details_reply_template = """\
1930 static cJSON *
1931 api_{n} (cJSON *o)
1932 {{
1933     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1934   int len = 0;
1935   if (!o) return 0;
1936   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1937   if (!mp) {{
1938     fprintf(stderr, "Failed converting JSON to API\\n");
1939     return 0;
1940   }}
1941   mp->_vl_msg_id = msg_id;
1942
1943   vl_api_{n}_t_endian(mp);
1944   vac_write((char *)mp, len);
1945   cJSON_free(mp);
1946
1947   cJSON *reply = cJSON_CreateArray();
1948
1949   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1950   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1951
1952   while (1) {{
1953     /* Read reply */
1954     char *p;
1955     int l;
1956     vac_read(&p, &l, 5); // XXX: Fix timeout
1957
1958     /* Message can be one of [_details, control_ping_reply
1959      * or unrelated event]
1960      */
1961     u16 msg_id = ntohs(*((u16 *)p));
1962     if (msg_id == reply_msg_id) {{
1963         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1964         vl_api_{r}_t_endian(rmp);
1965         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1966         break;
1967     }}
1968
1969     if (msg_id == details_msg_id) {{
1970         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1971         vl_api_{d}_t_endian(rmp);
1972         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1973     }}
1974   }}
1975   return reply;
1976 }}
1977
1978 """
1979
1980     if dump:
1981         if s.stream_message:
1982             write(
1983                 gets_details_reply_template.format(
1984                     n=s.caller,
1985                     r=s.reply,
1986                     N=s.caller.upper(),
1987                     R=s.reply.upper(),
1988                     d=s.stream_message,
1989                     D=s.stream_message.upper(),
1990                 )
1991             )
1992         else:
1993             write(
1994                 dump_details_template.format(
1995                     n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1996                 )
1997             )
1998     else:
1999         write(
2000             req_reply_template.format(
2001                 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
2002             )
2003         )
2004
2005
2006 def generate_c_test2_boilerplate(services, defines, module, stream):
2007     """Generate code for VAT2 plugin."""
2008     write = stream.write
2009
2010     define_hash = {d.name: d for d in defines}
2011     # replies = {}
2012
2013     hdr = """\
2014 #include <vlibapi/api.h>
2015 #include <vlibmemory/api.h>
2016 #include <vppinfra/error.h>
2017 #include <vnet/ip/ip_format_fns.h>
2018 #include <vnet/ethernet/ethernet_format_fns.h>
2019
2020 #define vl_typedefs             /* define message structures */
2021 #include <vlibmemory/vl_memory_api_h.h>
2022 #include <vlibmemory/vlib.api_types.h>
2023 #include <vlibmemory/vlib.api.h>
2024 #undef vl_typedefs
2025
2026 #include "{module}.api_enum.h"
2027 #include "{module}.api_types.h"
2028
2029 #define vl_endianfun            /* define message structures */
2030 #include "{module}.api.h"
2031 #undef vl_endianfun
2032
2033 #define vl_calcsizefun
2034 #include "{module}.api.h"
2035 #undef vl_calsizefun
2036
2037 #define vl_printfun
2038 #include "{module}.api.h"
2039 #undef vl_printfun
2040
2041 #include "{module}.api_tojson.h"
2042 #include "{module}.api_fromjson.h"
2043 #include <vpp-api/client/vppapiclient.h>
2044
2045 #include <vat2/vat2_helpers.h>
2046
2047 """
2048
2049     write(hdr.format(module=module))
2050
2051     for s in services:
2052         if s.reply not in define_hash:
2053             continue
2054         c_test_api_service(s, s.stream, stream)
2055
2056     write(
2057         "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
2058     )
2059     # write('__attribute__((constructor))')
2060     write("clib_error_t *\n")
2061     write("vat2_register_plugin (void) {\n")
2062     for s in services:
2063         if s.reply not in define_hash:
2064             continue
2065         crc = define_hash[s.caller].crc
2066         write(
2067             '   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
2068                 n=s.caller, crc=crc
2069             )
2070         )
2071     write("   return 0;\n")
2072     write("}\n")
2073
2074
2075 #
2076 # Plugin entry point
2077 #
2078 def run(output_dir, apifilename, s):
2079     """Main plugin entry point."""
2080     stream = StringIO()
2081
2082     if not output_dir:
2083         sys.stderr.write("Missing --outputdir argument")
2084         return None
2085
2086     basename = os.path.basename(apifilename)
2087     filename, _ = os.path.splitext(basename)
2088     modulename = filename.replace(".", "_")
2089     filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2090     filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2091     filename_c = os.path.join(output_dir + "/" + basename + ".c")
2092     filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2093     filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2094     filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2095     filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2096
2097     # Generate separate types file
2098     st = StringIO()
2099     generate_include_types(s, modulename, st)
2100     with open(filename_types, "w") as fd:
2101         st.seek(0)
2102         shutil.copyfileobj(st, fd)
2103     st.close()
2104
2105     # Generate separate enum file
2106     st = StringIO()
2107     st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2108     st.write("#define included_{}_api_enum_h\n".format(modulename))
2109     generate_include_enum(s, modulename, st)
2110     generate_include_counters(s["Counters"], st)
2111     st.write("#endif\n")
2112     with open(filename_enum, "w") as fd:
2113         st.seek(0)
2114         shutil.copyfileobj(st, fd)
2115     st.close()
2116
2117     # Generate separate C file
2118     st = StringIO()
2119     generate_c_boilerplate(
2120         s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2121     )
2122     with open(filename_c, "w") as fd:
2123         st.seek(0)
2124         shutil.copyfileobj(st, fd)
2125     st.close()
2126
2127     # Generate separate C test file
2128     st = StringIO()
2129     plugin = bool("plugin" in apifilename)
2130     generate_c_test_boilerplate(
2131         s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2132     )
2133     with open(filename_c_test, "w") as fd:
2134         st.seek(0)
2135         shutil.copyfileobj(st, fd)
2136     st.close()
2137
2138     # Fully autogenerated VATv2 C test file
2139     st = StringIO()
2140     generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2141     with open(filename_c_test2, "w") as fd:
2142         st.seek(0)
2143         shutil.copyfileobj(st, fd)
2144     st.close()  #
2145
2146     # Generate separate JSON file
2147     st = StringIO()
2148     generate_tojson(s, modulename, st)
2149     with open(filename_c_tojson, "w") as fd:
2150         st.seek(0)
2151         shutil.copyfileobj(st, fd)
2152     st.close()
2153     st = StringIO()
2154     generate_fromjson(s, modulename, st)
2155     with open(filename_c_fromjson, "w") as fd:
2156         st.seek(0)
2157         shutil.copyfileobj(st, fd)
2158     st.close()
2159
2160     output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2161     output += generate_imports(s["Import"])
2162     output += msg_ids(s)
2163     output += msg_names(s)
2164     output += msg_name_crc_list(s, filename)
2165     output += typedefs(modulename)
2166     printfun_types(s["types"], stream, modulename)
2167     printfun(s["Define"], stream, modulename)
2168     output += stream.getvalue()
2169     stream.close()
2170     output += endianfun(s["types"] + s["Define"], modulename)
2171     output += calc_size_fun(s["types"] + s["Define"], modulename)
2172     output += version_tuple(s, basename)
2173     output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])
2174
2175     return output