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