vlib: prevent some signals from being executed on workers
[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);\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);
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));\n".format(
465                         msgvar=msgvar, msgsize=msgsize, realloc=realloc
466                     )
467                 )
468                 write(
469                     "    clib_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, fieldtype):
1147     """Return proper endian string conversion function"""
1148     return ENDIAN_STRINGS[fieldtype]
1149
1150
1151 def endianfun_array(o):
1152     """Generate endian functions for arrays"""
1153     forloop = """\
1154     ASSERT((u32){length} <= (u32)VL_API_MAX_ARRAY_SIZE);
1155     for (i = 0; i < {length}; i++) {{
1156         a->{name}[i] = {format}(a->{name}[i]);
1157     }}
1158 """
1159
1160     forloop_format = """\
1161     for (i = 0; i < {length}; i++) {{
1162         {type}_endian(&a->{name}[i], to_net);
1163     }}
1164 """
1165
1166     output = ""
1167     if o.fieldtype == "u8" or o.fieldtype == "string" or o.fieldtype == "bool":
1168         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1169     else:
1170         lfield = "a->" + o.lengthfield if o.lengthfield else o.length
1171         if o.lengthfield:
1172             output += (
1173                 f"    u32 count = to_net ? clib_host_to_net_u32(a->{o.lengthfield}) : "
1174                 f"a->{o.lengthfield};\n"
1175             )
1176             lfield = "count"
1177         else:
1178             lfield = o.length
1179
1180         if o.fieldtype in ENDIAN_STRINGS:
1181             output += forloop.format(
1182                 length=lfield,
1183                 format=get_endian_string(o, o.fieldtype),
1184                 name=o.fieldname,
1185             )
1186         else:
1187             output += forloop_format.format(
1188                 length=lfield, type=o.fieldtype, name=o.fieldname
1189             )
1190     return output
1191
1192
1193 NO_ENDIAN_CONVERSION = {"client_index": None}
1194
1195
1196 def endianfun_obj(o):
1197     """Generate endian conversion function for type"""
1198     output = ""
1199     if o.type == "Array":
1200         return endianfun_array(o)
1201     if o.type != "Field":
1202         output += '    s = format(s, "\\n{} {} {} (print not implemented");\n'.format(
1203             o.type, o.fieldtype, o.fieldname
1204         )
1205         return output
1206     if o.fieldname in NO_ENDIAN_CONVERSION:
1207         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1208         return output
1209     if o.fieldtype in ENDIAN_STRINGS:
1210         output += "    a->{name} = {format}(a->{name});\n".format(
1211             name=o.fieldname, format=get_endian_string(o, o.fieldtype)
1212         )
1213     elif o.fieldtype.startswith("vl_api_"):
1214         output += "    {type}_endian(&a->{name}, to_net);\n".format(
1215             type=o.fieldtype, name=o.fieldname
1216         )
1217     else:
1218         output += "    /* a->{n} = a->{n} (no-op) */\n".format(n=o.fieldname)
1219
1220     return output
1221
1222
1223 def endianfun(objs, modulename):
1224     """Main entry point for endian function generation"""
1225     output = """\
1226
1227 /****** Endian swap functions *****/\n\
1228 #ifdef vl_endianfun
1229 #ifndef included_{module}_endianfun
1230 #define included_{module}_endianfun
1231
1232 #undef clib_net_to_host_uword
1233 #undef clib_host_to_net_uword
1234 #ifdef LP64
1235 #define clib_net_to_host_uword clib_net_to_host_u64
1236 #define clib_host_to_net_uword clib_host_to_net_u64
1237 #else
1238 #define clib_net_to_host_uword clib_net_to_host_u32
1239 #define clib_host_to_net_uword clib_host_to_net_u32
1240 #endif
1241
1242 """
1243     output = output.format(module=modulename)
1244
1245     signature = """\
1246 static inline void vl_api_{name}_t_endian (vl_api_{name}_t *a, bool to_net)
1247 {{
1248     int i __attribute__((unused));
1249 """
1250
1251     for t in objs:
1252         if t.__class__.__name__ == "Enum" or t.__class__.__name__ == "EnumFlag":
1253             output += signature.format(name=t.name)
1254             if t.enumtype in ENDIAN_STRINGS:
1255                 output += "    *a = {}(*a);\n".format(get_endian_string(t, t.enumtype))
1256             else:
1257                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1258                     name=t.name
1259                 )
1260
1261             output += "}\n\n"
1262             continue
1263
1264         if t.manual_endian:
1265             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
1266             continue
1267
1268         if t.__class__.__name__ == "Using":
1269             output += signature.format(name=t.name)
1270             if "length" in t.alias and t.alias["length"] and t.alias["type"] == "u8":
1271                 output += "    /* a->{name} = a->{name} (no-op) */\n".format(
1272                     name=t.name
1273                 )
1274             elif t.alias["type"] in FORMAT_STRINGS:
1275                 output += "    *a = {}(*a);\n".format(
1276                     get_endian_string(t, t.alias["type"])
1277                 )
1278             else:
1279                 output += "    /* Not Implemented yet {} */".format(t.name)
1280             output += "}\n\n"
1281             continue
1282
1283         output += signature.format(name=t.name)
1284
1285         for o in t.block:
1286             output += endianfun_obj(o)
1287         output += "}\n\n"
1288
1289     output += "\n#endif"
1290     output += "\n#endif /* vl_endianfun */\n\n"
1291
1292     return output
1293
1294
1295 def calc_size_fun(objs, modulename):
1296     """Main entry point for calculate size function generation"""
1297     output = """\
1298
1299 /****** Calculate size functions *****/\n\
1300 #ifdef vl_calcsizefun
1301 #ifndef included_{module}_calcsizefun
1302 #define included_{module}_calcsizefun
1303
1304 """
1305     output = output.format(module=modulename)
1306
1307     signature = """\
1308 /* calculate message size of message in network byte order */
1309 static inline uword vl_api_{name}_t_calc_size (vl_api_{name}_t *a)
1310 {{
1311 """
1312
1313     for o in objs:
1314         tname = o.__class__.__name__
1315
1316         output += signature.format(name=o.name)
1317         output += f"      return sizeof(*a)"
1318         if tname == "Using":
1319             if "length" in o.alias:
1320                 try:
1321                     tmp = int(o.alias["length"])
1322                     if tmp == 0:
1323                         raise (f"Unexpected length '0' for alias {o}")
1324                 except:
1325                     # output += f" + vl_api_{o.alias.name}_t_calc_size({o.name})"
1326                     print("culprit:")
1327                     print(o)
1328                     print(dir(o.alias))
1329                     print(o.alias)
1330                     raise
1331         elif tname == "Enum" or tname == "EnumFlag":
1332             pass
1333         else:
1334             for b in o.block:
1335                 if b.type == "Option":
1336                     continue
1337                 elif b.type == "Field":
1338                     if b.fieldtype.startswith("vl_api_"):
1339                         output += f" - sizeof(a->{b.fieldname})"
1340                         output += f" + {b.fieldtype}_calc_size(&a->{b.fieldname})"
1341                 elif b.type == "Array":
1342                     if b.lengthfield:
1343                         m = list(
1344                             filter(lambda x: x.fieldname == b.lengthfield, o.block)
1345                         )
1346                         if len(m) != 1:
1347                             raise Exception(
1348                                 f"Expected 1 match for field '{b.lengthfield}', got '{m}'"
1349                             )
1350                         lf = m[0]
1351                         if lf.fieldtype in ENDIAN_STRINGS:
1352                             output += f" + {get_endian_string(b, lf.fieldtype)}(a->{b.lengthfield}) * sizeof(a->{b.fieldname}[0])"
1353                         elif lf.fieldtype == "u8":
1354                             output += (
1355                                 f" + a->{b.lengthfield} * sizeof(a->{b.fieldname}[0])"
1356                             )
1357                         else:
1358                             raise Exception(
1359                                 f"Don't know how to endian swap {lf.fieldtype}"
1360                             )
1361                     else:
1362                         # Fixed length strings decay to nul terminated u8
1363                         if b.fieldtype == "string":
1364                             if b.modern_vla:
1365                                 output += f" + vl_api_string_len(&a->{b.fieldname})"
1366
1367         output += ";\n"
1368         output += "}\n\n"
1369     output += "\n#endif"
1370     output += "\n#endif /* vl_calcsizefun */\n\n"
1371
1372     return output
1373
1374
1375 def version_tuple(s, module):
1376     """Generate semantic version string"""
1377     output = """\
1378 /****** Version tuple *****/
1379
1380 #ifdef vl_api_version_tuple
1381
1382 """
1383     if "version" in s["Option"]:
1384         v = s["Option"]["version"]
1385         (major, minor, patch) = v.split(".")
1386         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % (
1387             module,
1388             major,
1389             minor,
1390             patch,
1391         )
1392
1393     output += "\n#endif /* vl_api_version_tuple */\n\n"
1394
1395     return output
1396
1397
1398 def generate_include_enum(s, module, stream):
1399     """Generate <name>.api_enum.h"""
1400     write = stream.write
1401
1402     if "Define" in s:
1403         write("typedef enum {\n")
1404         for t in s["Define"]:
1405             write("   VL_API_{},\n".format(t.name.upper()))
1406         write("   VL_MSG_{}_LAST\n".format(module.upper()))
1407         write("}} vl_api_{}_enum_t;\n".format(module))
1408
1409
1410 def generate_include_counters(s, stream):
1411     """Include file for the counter data model types."""
1412     write = stream.write
1413
1414     for counters in s:
1415         csetname = counters.name
1416         write("typedef enum {\n")
1417         for c in counters.block:
1418             write("   {}_ERROR_{},\n".format(csetname.upper(), c["name"].upper()))
1419         write("   {}_N_ERROR\n".format(csetname.upper()))
1420         write("}} vl_counter_{}_enum_t;\n".format(csetname))
1421
1422         write("extern vlib_error_desc_t {}_error_counters[];\n".format(csetname))
1423
1424
1425 def generate_include_types(s, module, stream):
1426     """Generate separate API _types file."""
1427     write = stream.write
1428
1429     write("#ifndef included_{module}_api_types_h\n".format(module=module))
1430     write("#define included_{module}_api_types_h\n".format(module=module))
1431
1432     if "version" in s["Option"]:
1433         v = s["Option"]["version"]
1434         (major, minor, patch) = v.split(".")
1435         write(
1436             "#define VL_API_{m}_API_VERSION_MAJOR {v}\n".format(
1437                 m=module.upper(), v=major
1438             )
1439         )
1440         write(
1441             "#define VL_API_{m}_API_VERSION_MINOR {v}\n".format(
1442                 m=module.upper(), v=minor
1443             )
1444         )
1445         write(
1446             "#define VL_API_{m}_API_VERSION_PATCH {v}\n".format(
1447                 m=module.upper(), v=patch
1448             )
1449         )
1450
1451     if "Import" in s:
1452         write("/* Imported API files */\n")
1453         for i in s["Import"]:
1454             filename = i.filename.replace("plugins/", "")
1455             write("#include <{}_types.h>\n".format(filename))
1456
1457     for o in itertools.chain(s["types"], s["Define"]):
1458         tname = o.__class__.__name__
1459         if tname == "Using":
1460             if "length" in o.alias:
1461                 write(
1462                     "typedef %s vl_api_%s_t[%s];\n"
1463                     % (o.alias["type"], o.name, o.alias["length"])
1464                 )
1465             else:
1466                 write("typedef %s vl_api_%s_t;\n" % (o.alias["type"], o.name))
1467         elif tname == "Enum" or tname == "EnumFlag":
1468             if o.enumtype == "u32":
1469                 write("typedef enum {\n")
1470             else:
1471                 write("typedef enum __attribute__((packed)) {\n")
1472
1473             for b in o.block:
1474                 write("    %s = %s,\n" % (b[0], b[1]))
1475             write("} vl_api_%s_t;\n" % o.name)
1476             if o.enumtype != "u32":
1477                 size1 = "sizeof(vl_api_%s_t)" % o.name
1478                 size2 = "sizeof(%s)" % o.enumtype
1479                 err_str = "size of API enum %s is wrong" % o.name
1480                 write('STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str))
1481         else:
1482             if tname == "Union":
1483                 write("typedef union __attribute__ ((packed)) _vl_api_%s {\n" % o.name)
1484             else:
1485                 write(
1486                     ("typedef struct __attribute__ ((packed)) _vl_api_%s {\n") % o.name
1487                 )
1488             for b in o.block:
1489                 if b.type == "Option":
1490                     continue
1491                 if b.type == "Field":
1492                     write("    %s %s;\n" % (api2c(b.fieldtype), b.fieldname))
1493                 elif b.type == "Array":
1494                     if b.lengthfield:
1495                         write("    %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname))
1496                     else:
1497                         # Fixed length strings decay to nul terminated u8
1498                         if b.fieldtype == "string":
1499                             if b.modern_vla:
1500                                 write(
1501                                     "    {} {};\n".format(
1502                                         api2c(b.fieldtype), b.fieldname
1503                                     )
1504                                 )
1505                             else:
1506                                 write("    u8 {}[{}];\n".format(b.fieldname, b.length))
1507                         else:
1508                             write(
1509                                 "    %s %s[%s];\n"
1510                                 % (api2c(b.fieldtype), b.fieldname, b.length)
1511                             )
1512                 else:
1513                     raise ValueError(
1514                         "Error in processing type {} for {}".format(b, o.name)
1515                     )
1516
1517             write("} vl_api_%s_t;\n" % o.name)
1518             write(
1519                 f"#define VL_API_{o.name.upper()}_IS_CONSTANT_SIZE ({0 if o.vla else 1})\n\n"
1520             )
1521
1522     for t in s["Define"]:
1523         write(
1524             '#define VL_API_{ID}_CRC "{n}_{crc:08x}"\n'.format(
1525                 n=t.name, ID=t.name.upper(), crc=t.crc
1526             )
1527         )
1528
1529     write("\n#endif\n")
1530
1531
1532 def generate_c_boilerplate(services, defines, counters, file_crc, module, stream):
1533     """VPP side plugin."""
1534     write = stream.write
1535     define_hash = {d.name: d for d in defines}
1536
1537     hdr = """\
1538 #define vl_endianfun            /* define message structures */
1539 #include "{module}.api.h"
1540 #undef vl_endianfun
1541
1542 #define vl_calcsizefun
1543 #include "{module}.api.h"
1544 #undef vl_calsizefun
1545
1546 /* instantiate all the print functions we know about */
1547 #define vl_printfun
1548 #include "{module}.api.h"
1549 #undef vl_printfun
1550
1551 #include "{module}.api_json.h"
1552 """
1553
1554     write(hdr.format(module=module))
1555     if len(defines) > 0:
1556         write("static u16\n")
1557         write("setup_message_id_table (void) {\n")
1558         write("   api_main_t *am = my_api_main;\n")
1559         write("   vl_msg_api_msg_config_t c;\n")
1560         write(
1561             '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1562             "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1563         )
1564         write(f"   vec_add1(am->json_api_repr, (u8 *)json_api_repr_{module});\n")
1565
1566     for d in defines:
1567         write(
1568             '   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1569             "                                VL_API_{ID} + msg_id_base);\n".format(
1570                 n=d.name, ID=d.name.upper(), crc=d.crc
1571             )
1572         )
1573     for s in services:
1574         d = define_hash[s.caller]
1575         write(
1576             "   c = (vl_msg_api_msg_config_t) "
1577             " {{.id = VL_API_{ID} + msg_id_base,\n"
1578             '   .name = "{n}",\n'
1579             "   .handler = vl_api_{n}_t_handler,\n"
1580             "   .endian = vl_api_{n}_t_endian,\n"
1581             "   .format_fn = vl_api_{n}_t_format,\n"
1582             "   .traced = 1,\n"
1583             "   .replay = 1,\n"
1584             "   .tojson = vl_api_{n}_t_tojson,\n"
1585             "   .fromjson = vl_api_{n}_t_fromjson,\n"
1586             "   .calc_size = vl_api_{n}_t_calc_size,\n"
1587             "   .is_autoendian = {auto}}};\n".format(
1588                 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1589             )
1590         )
1591         write("   vl_msg_api_config (&c);\n")
1592         try:
1593             d = define_hash[s.reply]
1594             write(
1595                 "   c = (vl_msg_api_msg_config_t) "
1596                 "{{.id = VL_API_{ID} + msg_id_base,\n"
1597                 '  .name = "{n}",\n'
1598                 "  .handler = 0,\n"
1599                 "  .endian = vl_api_{n}_t_endian,\n"
1600                 "  .format_fn = vl_api_{n}_t_format,\n"
1601                 "  .traced = 1,\n"
1602                 "  .replay = 1,\n"
1603                 "  .tojson = vl_api_{n}_t_tojson,\n"
1604                 "  .fromjson = vl_api_{n}_t_fromjson,\n"
1605                 "  .calc_size = vl_api_{n}_t_calc_size,\n"
1606                 "  .is_autoendian = {auto}}};\n".format(
1607                     n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1608                 )
1609             )
1610             write("   vl_msg_api_config (&c);\n")
1611         except KeyError:
1612             pass
1613
1614         try:
1615             if s.stream:
1616                 d = define_hash[s.stream_message]
1617                 write(
1618                     "   c = (vl_msg_api_msg_config_t) "
1619                     "{{.id = VL_API_{ID} + msg_id_base,\n"
1620                     '  .name = "{n}",\n'
1621                     "  .handler = 0,\n"
1622                     "  .endian = vl_api_{n}_t_endian,\n"
1623                     "  .format_fn = vl_api_{n}_t_format,\n"
1624                     "  .traced = 1,\n"
1625                     "  .replay = 1,\n"
1626                     "  .tojson = vl_api_{n}_t_tojson,\n"
1627                     "  .fromjson = vl_api_{n}_t_fromjson,\n"
1628                     "  .calc_size = vl_api_{n}_t_calc_size,\n"
1629                     "  .is_autoendian = {auto}}};\n".format(
1630                         n=s.stream_message,
1631                         ID=s.stream_message.upper(),
1632                         auto=d.autoendian,
1633                     )
1634                 )
1635                 write("   vl_msg_api_config (&c);\n")
1636         except KeyError:
1637             pass
1638     if len(defines) > 0:
1639         write("   return msg_id_base;\n")
1640         write("}\n")
1641
1642     severity = {
1643         "error": "VL_COUNTER_SEVERITY_ERROR",
1644         "info": "VL_COUNTER_SEVERITY_INFO",
1645         "warn": "VL_COUNTER_SEVERITY_WARN",
1646     }
1647
1648     for cnt in counters:
1649         csetname = cnt.name
1650         write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1651         for c in cnt.block:
1652             write("  {\n")
1653             write('   .name = "{}",\n'.format(c["name"]))
1654             write('   .desc = "{}",\n'.format(c["description"]))
1655             write("   .severity = {},\n".format(severity[c["severity"]]))
1656             write("  },\n")
1657         write("};\n")
1658
1659
1660 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1661     """Generate code for legacy style VAT. To be deleted."""
1662     write = stream.write
1663
1664     define_hash = {d.name: d for d in defines}
1665
1666     hdr = """\
1667 #define vl_endianfun            /* define message structures */
1668 #include "{module}.api.h"
1669 #undef vl_endianfun
1670
1671 #define vl_calcsizefun
1672 #include "{module}.api.h"
1673 #undef vl_calsizefun
1674
1675 /* instantiate all the print functions we know about */
1676 #define vl_printfun
1677 #include "{module}.api.h"
1678 #undef vl_printfun
1679
1680 """
1681
1682     write(hdr.format(module=module))
1683     for s in services:
1684         try:
1685             d = define_hash[s.reply]
1686         except KeyError:
1687             continue
1688         if d.manual_print:
1689             write(
1690                 "/*\n"
1691                 " * Manual definition requested for: \n"
1692                 " * vl_api_{n}_t_handler()\n"
1693                 " */\n".format(n=s.reply)
1694             )
1695             continue
1696         if not define_hash[s.caller].autoreply:
1697             write(
1698                 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1699                     n=s.reply
1700                 )
1701             )
1702             continue
1703         write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1704         write("static void\n")
1705         write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1706         write("   vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1707         write("   i32 retval = ntohl(mp->retval);\n")
1708         write("   if (vam->async_mode) {\n")
1709         write("      vam->async_errors += (retval < 0);\n")
1710         write("   } else {\n")
1711         write("      vam->retval = retval;\n")
1712         write("      vam->result_ready = 1;\n")
1713         write("   }\n")
1714         write("}\n")
1715         write("#endif\n")
1716
1717         for e in s.events:
1718             if define_hash[e].manual_print:
1719                 continue
1720             write("static void\n")
1721             write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1722             write('    vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
1723             write(
1724                 '    vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
1725             )
1726             write("}\n")
1727
1728     write("static void\n")
1729     write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1730     for s in services:
1731         write(
1732             "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1733             "    .id = VL_API_{ID} + msg_id_base,\n"
1734             '    .name = "{n}",\n'
1735             "    .handler = vl_api_{n}_t_handler,\n"
1736             "    .endian = vl_api_{n}_t_endian,\n"
1737             "    .format_fn = vl_api_{n}_t_format,\n"
1738             "    .size = sizeof(vl_api_{n}_t),\n"
1739             "    .traced = 1,\n"
1740             "    .tojson = vl_api_{n}_t_tojson,\n"
1741             "    .fromjson = vl_api_{n}_t_fromjson,\n"
1742             "    .calc_size = vl_api_{n}_t_calc_size,\n"
1743             "   }});".format(n=s.reply, ID=s.reply.upper())
1744         )
1745         write(
1746             '   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1747                 n=s.caller
1748             )
1749         )
1750         try:
1751             write(
1752                 '   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1753                     n=s.caller, help=define_hash[s.caller].options["vat_help"]
1754                 )
1755             )
1756         except KeyError:
1757             pass
1758
1759         # Events
1760         for e in s.events:
1761             write(
1762                 "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1763                 "    .id = VL_API_{ID} + msg_id_base,\n"
1764                 '    .name = "{n}",\n'
1765                 "    .handler = vl_api_{n}_t_handler,\n"
1766                 "    .endian = vl_api_{n}_t_endian,\n"
1767                 "    .format_fn = vl_api_{n}_t_format,\n"
1768                 "    .size = sizeof(vl_api_{n}_t),\n"
1769                 "    .traced = 1,\n"
1770                 "    .tojson = vl_api_{n}_t_tojson,\n"
1771                 "    .fromjson = vl_api_{n}_t_fromjson,\n"
1772                 "    .calc_size = vl_api_{n}_t_calc_size,\n"
1773                 "   }});".format(n=e, ID=e.upper())
1774             )
1775
1776     write("}\n")
1777     write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1778     write("{\n")
1779     write("   {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1780     write("   mainp->vat_main = vam;\n")
1781     write(
1782         "   mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1783         '                       ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1784     )
1785     write("   if (mainp->msg_id_base == (u16) ~0)\n")
1786     write(
1787         '      return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1788             module
1789         )
1790     )
1791     write("   setup_message_id_table (vam, mainp->msg_id_base);\n")
1792     write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1793     write("    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1794     write("#endif\n")
1795     write("   return 0;\n")
1796     write("}\n")
1797
1798
1799 def apifunc(func):
1800     """Check if a method is generated already."""
1801
1802     def _f(module, d, processed, *args):
1803         if d.name in processed:
1804             return None
1805         processed[d.name] = True
1806         return func(module, d, *args)
1807
1808     return _f
1809
1810
1811 def c_test_api_service(s, dump, stream):
1812     """Generate JSON code for a service."""
1813     write = stream.write
1814
1815     req_reply_template = """\
1816 static cJSON *
1817 api_{n} (cJSON *o)
1818 {{
1819   vl_api_{n}_t *mp;
1820   int len;
1821   if (!o) return 0;
1822   mp = vl_api_{n}_t_fromjson(o, &len);
1823   if (!mp) {{
1824     fprintf(stderr, "Failed converting JSON to API\\n");
1825     return 0;
1826   }}
1827
1828   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1829   vl_api_{n}_t_endian(mp, 1);
1830   vac_write((char *)mp, len);
1831   cJSON_free(mp);
1832
1833   /* Read reply */
1834   char *p;
1835   int l;
1836   vac_read(&p, &l, 5); // XXX: Fix timeout
1837   if (p == 0 || l == 0) return 0;
1838     // XXX Will fail in case of event received. Do loop
1839   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1840     fprintf(stderr, "Mismatched reply\\n");
1841     return 0;
1842   }}
1843   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1844   vl_api_{r}_t_endian(rmp, 0);
1845   return vl_api_{r}_t_tojson(rmp);
1846 }}
1847
1848 """
1849     dump_details_template = """\
1850 static cJSON *
1851 api_{n} (cJSON *o)
1852 {{
1853   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1854   int len;
1855   if (!o) return 0;
1856   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1857   if (!mp) {{
1858       fprintf(stderr, "Failed converting JSON to API\\n");
1859       return 0;
1860   }}
1861   mp->_vl_msg_id = msg_id;
1862   vl_api_{n}_t_endian(mp, 1);
1863   vac_write((char *)mp, len);
1864   cJSON_free(mp);
1865
1866   vat2_control_ping(123); // FIX CONTEXT
1867   cJSON *reply = cJSON_CreateArray();
1868
1869   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1870   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1871
1872   while (1) {{
1873     /* Read reply */
1874     char *p;
1875     int l;
1876     vac_read(&p, &l, 5); // XXX: Fix timeout
1877     if (p == 0 || l == 0) {{
1878       cJSON_free(reply);
1879       return 0;
1880     }}
1881
1882     /* Message can be one of [_details, control_ping_reply
1883      * or unrelated event]
1884      */
1885     u16 reply_msg_id = ntohs(*((u16 *)p));
1886     if (reply_msg_id == ping_reply_msg_id) {{
1887         break;
1888     }}
1889
1890     if (reply_msg_id == details_msg_id) {{
1891         if (l < sizeof(vl_api_{r}_t)) {{
1892             cJSON_free(reply);
1893             return 0;
1894         }}
1895         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1896         vl_api_{r}_t_endian(rmp, 0);
1897         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1898     }}
1899   }}
1900   return reply;
1901 }}
1902
1903 """
1904     gets_details_reply_template = """\
1905 static cJSON *
1906 api_{n} (cJSON *o)
1907 {{
1908     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1909   int len = 0;
1910   if (!o) return 0;
1911   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1912   if (!mp) {{
1913     fprintf(stderr, "Failed converting JSON to API\\n");
1914     return 0;
1915   }}
1916   mp->_vl_msg_id = msg_id;
1917
1918   vl_api_{n}_t_endian(mp, 1);
1919   vac_write((char *)mp, len);
1920   cJSON_free(mp);
1921
1922   cJSON *reply = cJSON_CreateArray();
1923
1924   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1925   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1926
1927   while (1) {{
1928     /* Read reply */
1929     char *p;
1930     int l;
1931     vac_read(&p, &l, 5); // XXX: Fix timeout
1932
1933     /* Message can be one of [_details, control_ping_reply
1934      * or unrelated event]
1935      */
1936     u16 msg_id = ntohs(*((u16 *)p));
1937     if (msg_id == reply_msg_id) {{
1938         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1939         vl_api_{r}_t_endian(rmp, 0);
1940         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1941         break;
1942     }}
1943
1944     if (msg_id == details_msg_id) {{
1945         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1946         vl_api_{d}_t_endian(rmp, 0);
1947         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1948     }}
1949   }}
1950   return reply;
1951 }}
1952
1953 """
1954
1955     if dump:
1956         if s.stream_message:
1957             write(
1958                 gets_details_reply_template.format(
1959                     n=s.caller,
1960                     r=s.reply,
1961                     N=s.caller.upper(),
1962                     R=s.reply.upper(),
1963                     d=s.stream_message,
1964                     D=s.stream_message.upper(),
1965                 )
1966             )
1967         else:
1968             write(
1969                 dump_details_template.format(
1970                     n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1971                 )
1972             )
1973     else:
1974         write(
1975             req_reply_template.format(
1976                 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1977             )
1978         )
1979
1980
1981 def generate_c_test2_boilerplate(services, defines, module, stream):
1982     """Generate code for VAT2 plugin."""
1983     write = stream.write
1984
1985     define_hash = {d.name: d for d in defines}
1986     # replies = {}
1987
1988     hdr = """\
1989 #include <vlibapi/api.h>
1990 #include <vlibmemory/api.h>
1991 #include <vppinfra/error.h>
1992 #include <vnet/ip/ip_format_fns.h>
1993 #include <vnet/ethernet/ethernet_format_fns.h>
1994
1995 #define vl_typedefs             /* define message structures */
1996 #include <vlibmemory/vl_memory_api_h.h>
1997 #include <vlibmemory/vlib.api_types.h>
1998 #include <vlibmemory/vlib.api.h>
1999 #undef vl_typedefs
2000
2001 #include "{module}.api_enum.h"
2002 #include "{module}.api_types.h"
2003
2004 #define vl_endianfun            /* define message structures */
2005 #include "{module}.api.h"
2006 #undef vl_endianfun
2007
2008 #define vl_calcsizefun
2009 #include "{module}.api.h"
2010 #undef vl_calsizefun
2011
2012 #define vl_printfun
2013 #include "{module}.api.h"
2014 #undef vl_printfun
2015
2016 #include "{module}.api_tojson.h"
2017 #include "{module}.api_fromjson.h"
2018 #include <vpp-api/client/vppapiclient.h>
2019
2020 #include <vat2/vat2_helpers.h>
2021
2022 """
2023
2024     write(hdr.format(module=module))
2025
2026     for s in services:
2027         if s.reply not in define_hash:
2028             continue
2029         c_test_api_service(s, s.stream, stream)
2030
2031     write(
2032         "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
2033     )
2034     # write('__attribute__((constructor))')
2035     write("clib_error_t *\n")
2036     write("vat2_register_plugin (void) {\n")
2037     for s in services:
2038         if s.reply not in define_hash:
2039             continue
2040         crc = define_hash[s.caller].crc
2041         write(
2042             '   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
2043                 n=s.caller, crc=crc
2044             )
2045         )
2046     write("   return 0;\n")
2047     write("}\n")
2048
2049
2050 #
2051 # Plugin entry point
2052 #
2053 def run(output_dir, apifilename, s):
2054     """Main plugin entry point."""
2055     stream = StringIO()
2056
2057     if not output_dir:
2058         sys.stderr.write("Missing --outputdir argument")
2059         return None
2060
2061     basename = os.path.basename(apifilename)
2062     filename, _ = os.path.splitext(basename)
2063     modulename = filename.replace(".", "_")
2064     filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2065     filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2066     filename_c = os.path.join(output_dir + "/" + basename + ".c")
2067     filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2068     filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2069     filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2070     filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2071
2072     # Generate separate types file
2073     st = StringIO()
2074     generate_include_types(s, modulename, st)
2075     with open(filename_types, "w") as fd:
2076         st.seek(0)
2077         shutil.copyfileobj(st, fd)
2078     st.close()
2079
2080     # Generate separate enum file
2081     st = StringIO()
2082     st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2083     st.write("#define included_{}_api_enum_h\n".format(modulename))
2084     generate_include_enum(s, modulename, st)
2085     generate_include_counters(s["Counters"], st)
2086     st.write("#endif\n")
2087     with open(filename_enum, "w") as fd:
2088         st.seek(0)
2089         shutil.copyfileobj(st, fd)
2090     st.close()
2091
2092     # Generate separate C file
2093     st = StringIO()
2094     generate_c_boilerplate(
2095         s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2096     )
2097     with open(filename_c, "w") as fd:
2098         st.seek(0)
2099         shutil.copyfileobj(st, fd)
2100     st.close()
2101
2102     # Generate separate C test file
2103     st = StringIO()
2104     plugin = bool("plugin" in apifilename)
2105     generate_c_test_boilerplate(
2106         s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2107     )
2108     with open(filename_c_test, "w") as fd:
2109         st.seek(0)
2110         shutil.copyfileobj(st, fd)
2111     st.close()
2112
2113     # Fully autogenerated VATv2 C test file
2114     st = StringIO()
2115     generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2116     with open(filename_c_test2, "w") as fd:
2117         st.seek(0)
2118         shutil.copyfileobj(st, fd)
2119     st.close()  #
2120
2121     # Generate separate JSON file
2122     st = StringIO()
2123     generate_tojson(s, modulename, st)
2124     with open(filename_c_tojson, "w") as fd:
2125         st.seek(0)
2126         shutil.copyfileobj(st, fd)
2127     st.close()
2128     st = StringIO()
2129     generate_fromjson(s, modulename, st)
2130     with open(filename_c_fromjson, "w") as fd:
2131         st.seek(0)
2132         shutil.copyfileobj(st, fd)
2133     st.close()
2134
2135     output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2136     output += generate_imports(s["Import"])
2137     output += msg_ids(s)
2138     output += msg_names(s)
2139     output += msg_name_crc_list(s, filename)
2140     output += typedefs(modulename)
2141     printfun_types(s["types"], stream, modulename)
2142     printfun(s["Define"], stream, modulename)
2143     output += stream.getvalue()
2144     stream.close()
2145     output += endianfun(s["types"] + s["Define"], modulename)
2146     output += calc_size_fun(s["types"] + s["Define"], modulename)
2147     output += version_tuple(s, basename)
2148     output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])
2149
2150     return output