acl: add missing byteswap header for musl
[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 #include "{module}.api_json.h"
1578 """
1579
1580     write(hdr.format(module=module))
1581     if len(defines) > 0:
1582         write("static u16\n")
1583         write("setup_message_id_table (void) {\n")
1584         write("   api_main_t *am = my_api_main;\n")
1585         write("   vl_msg_api_msg_config_t c;\n")
1586         write(
1587             '   u16 msg_id_base = vl_msg_api_get_msg_ids ("{}_{crc:08x}", '
1588             "VL_MSG_{m}_LAST);\n".format(module, crc=file_crc, m=module.upper())
1589         )
1590         write(f"   vec_add1(am->json_api_repr, (u8 *)json_api_repr_{module});\n")
1591
1592     for d in defines:
1593         write(
1594             '   vl_msg_api_add_msg_name_crc (am, "{n}_{crc:08x}",\n'
1595             "                                VL_API_{ID} + msg_id_base);\n".format(
1596                 n=d.name, ID=d.name.upper(), crc=d.crc
1597             )
1598         )
1599     for s in services:
1600         d = define_hash[s.caller]
1601         write(
1602             "   c = (vl_msg_api_msg_config_t) "
1603             " {{.id = VL_API_{ID} + msg_id_base,\n"
1604             '   .name = "{n}",\n'
1605             "   .handler = vl_api_{n}_t_handler,\n"
1606             "   .endian = vl_api_{n}_t_endian,\n"
1607             "   .format_fn = vl_api_{n}_t_format,\n"
1608             "   .traced = 1,\n"
1609             "   .replay = 1,\n"
1610             "   .tojson = vl_api_{n}_t_tojson,\n"
1611             "   .fromjson = vl_api_{n}_t_fromjson,\n"
1612             "   .calc_size = vl_api_{n}_t_calc_size,\n"
1613             "   .is_autoendian = {auto}}};\n".format(
1614                 n=s.caller, ID=s.caller.upper(), auto=d.autoendian
1615             )
1616         )
1617         write("   vl_msg_api_config (&c);\n")
1618         try:
1619             d = define_hash[s.reply]
1620             write(
1621                 "   c = (vl_msg_api_msg_config_t) "
1622                 "{{.id = VL_API_{ID} + msg_id_base,\n"
1623                 '  .name = "{n}",\n'
1624                 "  .handler = 0,\n"
1625                 "  .endian = vl_api_{n}_t_endian,\n"
1626                 "  .format_fn = vl_api_{n}_t_format,\n"
1627                 "  .traced = 1,\n"
1628                 "  .replay = 1,\n"
1629                 "  .tojson = vl_api_{n}_t_tojson,\n"
1630                 "  .fromjson = vl_api_{n}_t_fromjson,\n"
1631                 "  .calc_size = vl_api_{n}_t_calc_size,\n"
1632                 "  .is_autoendian = {auto}}};\n".format(
1633                     n=s.reply, ID=s.reply.upper(), auto=d.autoendian
1634                 )
1635             )
1636             write("   vl_msg_api_config (&c);\n")
1637         except KeyError:
1638             pass
1639
1640         try:
1641             if s.stream:
1642                 d = define_hash[s.stream_message]
1643                 write(
1644                     "   c = (vl_msg_api_msg_config_t) "
1645                     "{{.id = VL_API_{ID} + msg_id_base,\n"
1646                     '  .name = "{n}",\n'
1647                     "  .handler = 0,\n"
1648                     "  .endian = vl_api_{n}_t_endian,\n"
1649                     "  .format_fn = vl_api_{n}_t_format,\n"
1650                     "  .traced = 1,\n"
1651                     "  .replay = 1,\n"
1652                     "  .tojson = vl_api_{n}_t_tojson,\n"
1653                     "  .fromjson = vl_api_{n}_t_fromjson,\n"
1654                     "  .calc_size = vl_api_{n}_t_calc_size,\n"
1655                     "  .is_autoendian = {auto}}};\n".format(
1656                         n=s.stream_message,
1657                         ID=s.stream_message.upper(),
1658                         auto=d.autoendian,
1659                     )
1660                 )
1661                 write("   vl_msg_api_config (&c);\n")
1662         except KeyError:
1663             pass
1664     if len(defines) > 0:
1665         write("   return msg_id_base;\n")
1666         write("}\n")
1667
1668     severity = {
1669         "error": "VL_COUNTER_SEVERITY_ERROR",
1670         "info": "VL_COUNTER_SEVERITY_INFO",
1671         "warn": "VL_COUNTER_SEVERITY_WARN",
1672     }
1673
1674     for cnt in counters:
1675         csetname = cnt.name
1676         write("vlib_error_desc_t {}_error_counters[] = {{\n".format(csetname))
1677         for c in cnt.block:
1678             write("  {\n")
1679             write('   .name = "{}",\n'.format(c["name"]))
1680             write('   .desc = "{}",\n'.format(c["description"]))
1681             write("   .severity = {},\n".format(severity[c["severity"]]))
1682             write("  },\n")
1683         write("};\n")
1684
1685
1686 def generate_c_test_boilerplate(services, defines, file_crc, module, plugin, stream):
1687     """Generate code for legacy style VAT. To be deleted."""
1688     write = stream.write
1689
1690     define_hash = {d.name: d for d in defines}
1691
1692     hdr = """\
1693 #define vl_endianfun            /* define message structures */
1694 #include "{module}.api.h"
1695 #undef vl_endianfun
1696
1697 #define vl_calcsizefun
1698 #include "{module}.api.h"
1699 #undef vl_calsizefun
1700
1701 /* instantiate all the print functions we know about */
1702 #define vl_printfun
1703 #include "{module}.api.h"
1704 #undef vl_printfun
1705
1706 """
1707
1708     write(hdr.format(module=module))
1709     for s in services:
1710         try:
1711             d = define_hash[s.reply]
1712         except KeyError:
1713             continue
1714         if d.manual_print:
1715             write(
1716                 "/*\n"
1717                 " * Manual definition requested for: \n"
1718                 " * vl_api_{n}_t_handler()\n"
1719                 " */\n".format(n=s.reply)
1720             )
1721             continue
1722         if not define_hash[s.caller].autoreply:
1723             write(
1724                 "/* Generation not supported (vl_api_{n}_t_handler()) */\n".format(
1725                     n=s.reply
1726                 )
1727             )
1728             continue
1729         write("#ifndef VL_API_{n}_T_HANDLER\n".format(n=s.reply.upper()))
1730         write("static void\n")
1731         write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=s.reply))
1732         write("   vat_main_t * vam = {}_test_main.vat_main;\n".format(module))
1733         write("   i32 retval = ntohl(mp->retval);\n")
1734         write("   if (vam->async_mode) {\n")
1735         write("      vam->async_errors += (retval < 0);\n")
1736         write("   } else {\n")
1737         write("      vam->retval = retval;\n")
1738         write("      vam->result_ready = 1;\n")
1739         write("   }\n")
1740         write("}\n")
1741         write("#endif\n")
1742
1743         for e in s.events:
1744             if define_hash[e].manual_print:
1745                 continue
1746             write("static void\n")
1747             write("vl_api_{n}_t_handler (vl_api_{n}_t * mp) {{\n".format(n=e))
1748             write('    vlib_cli_output(0, "{n} event called:");\n'.format(n=e))
1749             write(
1750                 '    vlib_cli_output(0, "%U", vl_api_{n}_t_format, mp);\n'.format(n=e)
1751             )
1752             write("}\n")
1753
1754     write("static void\n")
1755     write("setup_message_id_table (vat_main_t * vam, u16 msg_id_base) {\n")
1756     for s in services:
1757         write(
1758             "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1759             "    .id = VL_API_{ID} + msg_id_base,\n"
1760             '    .name = "{n}",\n'
1761             "    .handler = vl_api_{n}_t_handler,\n"
1762             "    .endian = vl_api_{n}_t_endian,\n"
1763             "    .format_fn = vl_api_{n}_t_format,\n"
1764             "    .size = sizeof(vl_api_{n}_t),\n"
1765             "    .traced = 1,\n"
1766             "    .tojson = vl_api_{n}_t_tojson,\n"
1767             "    .fromjson = vl_api_{n}_t_fromjson,\n"
1768             "    .calc_size = vl_api_{n}_t_calc_size,\n"
1769             "   }});".format(n=s.reply, ID=s.reply.upper())
1770         )
1771         write(
1772             '   hash_set_mem (vam->function_by_name, "{n}", api_{n});\n'.format(
1773                 n=s.caller
1774             )
1775         )
1776         try:
1777             write(
1778                 '   hash_set_mem (vam->help_by_name, "{n}", "{help}");\n'.format(
1779                     n=s.caller, help=define_hash[s.caller].options["vat_help"]
1780                 )
1781             )
1782         except KeyError:
1783             pass
1784
1785         # Events
1786         for e in s.events:
1787             write(
1788                 "   vl_msg_api_config (&(vl_msg_api_msg_config_t){{\n"
1789                 "    .id = VL_API_{ID} + msg_id_base,\n"
1790                 '    .name = "{n}",\n'
1791                 "    .handler = vl_api_{n}_t_handler,\n"
1792                 "    .endian = vl_api_{n}_t_endian,\n"
1793                 "    .format_fn = vl_api_{n}_t_format,\n"
1794                 "    .size = sizeof(vl_api_{n}_t),\n"
1795                 "    .traced = 1,\n"
1796                 "    .tojson = vl_api_{n}_t_tojson,\n"
1797                 "    .fromjson = vl_api_{n}_t_fromjson,\n"
1798                 "    .calc_size = vl_api_{n}_t_calc_size,\n"
1799                 "   }});".format(n=e, ID=e.upper())
1800             )
1801
1802     write("}\n")
1803     write("clib_error_t * vat_plugin_register (vat_main_t *vam)\n")
1804     write("{\n")
1805     write("   {n}_test_main_t * mainp = &{n}_test_main;\n".format(n=module))
1806     write("   mainp->vat_main = vam;\n")
1807     write(
1808         "   mainp->msg_id_base = vl_client_get_first_plugin_msg_id "
1809         '                       ("{n}_{crc:08x}");\n'.format(n=module, crc=file_crc)
1810     )
1811     write("   if (mainp->msg_id_base == (u16) ~0)\n")
1812     write(
1813         '      return clib_error_return (0, "{} plugin not loaded...");\n'.format(
1814             module
1815         )
1816     )
1817     write("   setup_message_id_table (vam, mainp->msg_id_base);\n")
1818     write("#ifdef VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE\n")
1819     write("    VL_API_LOCAL_SETUP_MESSAGE_ID_TABLE(vam);\n")
1820     write("#endif\n")
1821     write("   return 0;\n")
1822     write("}\n")
1823
1824
1825 def apifunc(func):
1826     """Check if a method is generated already."""
1827
1828     def _f(module, d, processed, *args):
1829         if d.name in processed:
1830             return None
1831         processed[d.name] = True
1832         return func(module, d, *args)
1833
1834     return _f
1835
1836
1837 def c_test_api_service(s, dump, stream):
1838     """Generate JSON code for a service."""
1839     write = stream.write
1840
1841     req_reply_template = """\
1842 static cJSON *
1843 api_{n} (cJSON *o)
1844 {{
1845   vl_api_{n}_t *mp;
1846   int len;
1847   if (!o) return 0;
1848   mp = vl_api_{n}_t_fromjson(o, &len);
1849   if (!mp) {{
1850     fprintf(stderr, "Failed converting JSON to API\\n");
1851     return 0;
1852   }}
1853
1854   mp->_vl_msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1855   vl_api_{n}_t_endian(mp);
1856   vac_write((char *)mp, len);
1857   cJSON_free(mp);
1858
1859   /* Read reply */
1860   char *p;
1861   int l;
1862   vac_read(&p, &l, 5); // XXX: Fix timeout
1863   if (p == 0 || l == 0) return 0;
1864     // XXX Will fail in case of event received. Do loop
1865   if (ntohs(*((u16 *)p)) != vac_get_msg_index(VL_API_{R}_CRC)) {{
1866     fprintf(stderr, "Mismatched reply\\n");
1867     return 0;
1868   }}
1869   vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1870   vl_api_{r}_t_endian(rmp);
1871   return vl_api_{r}_t_tojson(rmp);
1872 }}
1873
1874 """
1875     dump_details_template = """\
1876 static cJSON *
1877 api_{n} (cJSON *o)
1878 {{
1879   u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1880   int len;
1881   if (!o) return 0;
1882   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1883   if (!mp) {{
1884       fprintf(stderr, "Failed converting JSON to API\\n");
1885       return 0;
1886   }}
1887   mp->_vl_msg_id = msg_id;
1888   vl_api_{n}_t_endian(mp);
1889   vac_write((char *)mp, len);
1890   cJSON_free(mp);
1891
1892   vat2_control_ping(123); // FIX CONTEXT
1893   cJSON *reply = cJSON_CreateArray();
1894
1895   u16 ping_reply_msg_id = vac_get_msg_index(VL_API_CONTROL_PING_REPLY_CRC);
1896   u16 details_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1897
1898   while (1) {{
1899     /* Read reply */
1900     char *p;
1901     int l;
1902     vac_read(&p, &l, 5); // XXX: Fix timeout
1903     if (p == 0 || l == 0) {{
1904       cJSON_free(reply);
1905       return 0;
1906     }}
1907
1908     /* Message can be one of [_details, control_ping_reply
1909      * or unrelated event]
1910      */
1911     u16 reply_msg_id = ntohs(*((u16 *)p));
1912     if (reply_msg_id == ping_reply_msg_id) {{
1913         break;
1914     }}
1915
1916     if (reply_msg_id == details_msg_id) {{
1917         if (l < sizeof(vl_api_{r}_t)) {{
1918             cJSON_free(reply);
1919             return 0;
1920         }}
1921         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1922         vl_api_{r}_t_endian(rmp);
1923         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1924     }}
1925   }}
1926   return reply;
1927 }}
1928
1929 """
1930     gets_details_reply_template = """\
1931 static cJSON *
1932 api_{n} (cJSON *o)
1933 {{
1934     u16 msg_id = vac_get_msg_index(VL_API_{N}_CRC);
1935   int len = 0;
1936   if (!o) return 0;
1937   vl_api_{n}_t *mp = vl_api_{n}_t_fromjson(o, &len);
1938   if (!mp) {{
1939     fprintf(stderr, "Failed converting JSON to API\\n");
1940     return 0;
1941   }}
1942   mp->_vl_msg_id = msg_id;
1943
1944   vl_api_{n}_t_endian(mp);
1945   vac_write((char *)mp, len);
1946   cJSON_free(mp);
1947
1948   cJSON *reply = cJSON_CreateArray();
1949
1950   u16 reply_msg_id = vac_get_msg_index(VL_API_{R}_CRC);
1951   u16 details_msg_id = vac_get_msg_index(VL_API_{D}_CRC);
1952
1953   while (1) {{
1954     /* Read reply */
1955     char *p;
1956     int l;
1957     vac_read(&p, &l, 5); // XXX: Fix timeout
1958
1959     /* Message can be one of [_details, control_ping_reply
1960      * or unrelated event]
1961      */
1962     u16 msg_id = ntohs(*((u16 *)p));
1963     if (msg_id == reply_msg_id) {{
1964         vl_api_{r}_t *rmp = (vl_api_{r}_t *)p;
1965         vl_api_{r}_t_endian(rmp);
1966         cJSON_AddItemToArray(reply, vl_api_{r}_t_tojson(rmp));
1967         break;
1968     }}
1969
1970     if (msg_id == details_msg_id) {{
1971         vl_api_{d}_t *rmp = (vl_api_{d}_t *)p;
1972         vl_api_{d}_t_endian(rmp);
1973         cJSON_AddItemToArray(reply, vl_api_{d}_t_tojson(rmp));
1974     }}
1975   }}
1976   return reply;
1977 }}
1978
1979 """
1980
1981     if dump:
1982         if s.stream_message:
1983             write(
1984                 gets_details_reply_template.format(
1985                     n=s.caller,
1986                     r=s.reply,
1987                     N=s.caller.upper(),
1988                     R=s.reply.upper(),
1989                     d=s.stream_message,
1990                     D=s.stream_message.upper(),
1991                 )
1992             )
1993         else:
1994             write(
1995                 dump_details_template.format(
1996                     n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
1997                 )
1998             )
1999     else:
2000         write(
2001             req_reply_template.format(
2002                 n=s.caller, r=s.reply, N=s.caller.upper(), R=s.reply.upper()
2003             )
2004         )
2005
2006
2007 def generate_c_test2_boilerplate(services, defines, module, stream):
2008     """Generate code for VAT2 plugin."""
2009     write = stream.write
2010
2011     define_hash = {d.name: d for d in defines}
2012     # replies = {}
2013
2014     hdr = """\
2015 #include <vlibapi/api.h>
2016 #include <vlibmemory/api.h>
2017 #include <vppinfra/error.h>
2018 #include <vnet/ip/ip_format_fns.h>
2019 #include <vnet/ethernet/ethernet_format_fns.h>
2020
2021 #define vl_typedefs             /* define message structures */
2022 #include <vlibmemory/vl_memory_api_h.h>
2023 #include <vlibmemory/vlib.api_types.h>
2024 #include <vlibmemory/vlib.api.h>
2025 #undef vl_typedefs
2026
2027 #include "{module}.api_enum.h"
2028 #include "{module}.api_types.h"
2029
2030 #define vl_endianfun            /* define message structures */
2031 #include "{module}.api.h"
2032 #undef vl_endianfun
2033
2034 #define vl_calcsizefun
2035 #include "{module}.api.h"
2036 #undef vl_calsizefun
2037
2038 #define vl_printfun
2039 #include "{module}.api.h"
2040 #undef vl_printfun
2041
2042 #include "{module}.api_tojson.h"
2043 #include "{module}.api_fromjson.h"
2044 #include <vpp-api/client/vppapiclient.h>
2045
2046 #include <vat2/vat2_helpers.h>
2047
2048 """
2049
2050     write(hdr.format(module=module))
2051
2052     for s in services:
2053         if s.reply not in define_hash:
2054             continue
2055         c_test_api_service(s, s.stream, stream)
2056
2057     write(
2058         "void vat2_register_function(char *, cJSON * (*)(cJSON *), cJSON * (*)(void *), u32);\n"
2059     )
2060     # write('__attribute__((constructor))')
2061     write("clib_error_t *\n")
2062     write("vat2_register_plugin (void) {\n")
2063     for s in services:
2064         if s.reply not in define_hash:
2065             continue
2066         crc = define_hash[s.caller].crc
2067         write(
2068             '   vat2_register_function("{n}", api_{n}, (cJSON * (*)(void *))vl_api_{n}_t_tojson, 0x{crc:08x});\n'.format(
2069                 n=s.caller, crc=crc
2070             )
2071         )
2072     write("   return 0;\n")
2073     write("}\n")
2074
2075
2076 #
2077 # Plugin entry point
2078 #
2079 def run(output_dir, apifilename, s):
2080     """Main plugin entry point."""
2081     stream = StringIO()
2082
2083     if not output_dir:
2084         sys.stderr.write("Missing --outputdir argument")
2085         return None
2086
2087     basename = os.path.basename(apifilename)
2088     filename, _ = os.path.splitext(basename)
2089     modulename = filename.replace(".", "_")
2090     filename_enum = os.path.join(output_dir + "/" + basename + "_enum.h")
2091     filename_types = os.path.join(output_dir + "/" + basename + "_types.h")
2092     filename_c = os.path.join(output_dir + "/" + basename + ".c")
2093     filename_c_test = os.path.join(output_dir + "/" + basename + "_test.c")
2094     filename_c_test2 = os.path.join(output_dir + "/" + basename + "_test2.c")
2095     filename_c_tojson = os.path.join(output_dir + "/" + basename + "_tojson.h")
2096     filename_c_fromjson = os.path.join(output_dir + "/" + basename + "_fromjson.h")
2097
2098     # Generate separate types file
2099     st = StringIO()
2100     generate_include_types(s, modulename, st)
2101     with open(filename_types, "w") as fd:
2102         st.seek(0)
2103         shutil.copyfileobj(st, fd)
2104     st.close()
2105
2106     # Generate separate enum file
2107     st = StringIO()
2108     st.write("#ifndef included_{}_api_enum_h\n".format(modulename))
2109     st.write("#define included_{}_api_enum_h\n".format(modulename))
2110     generate_include_enum(s, modulename, st)
2111     generate_include_counters(s["Counters"], st)
2112     st.write("#endif\n")
2113     with open(filename_enum, "w") as fd:
2114         st.seek(0)
2115         shutil.copyfileobj(st, fd)
2116     st.close()
2117
2118     # Generate separate C file
2119     st = StringIO()
2120     generate_c_boilerplate(
2121         s["Service"], s["Define"], s["Counters"], s["file_crc"], modulename, st
2122     )
2123     with open(filename_c, "w") as fd:
2124         st.seek(0)
2125         shutil.copyfileobj(st, fd)
2126     st.close()
2127
2128     # Generate separate C test file
2129     st = StringIO()
2130     plugin = bool("plugin" in apifilename)
2131     generate_c_test_boilerplate(
2132         s["Service"], s["Define"], s["file_crc"], modulename, plugin, st
2133     )
2134     with open(filename_c_test, "w") as fd:
2135         st.seek(0)
2136         shutil.copyfileobj(st, fd)
2137     st.close()
2138
2139     # Fully autogenerated VATv2 C test file
2140     st = StringIO()
2141     generate_c_test2_boilerplate(s["Service"], s["Define"], modulename, st)
2142     with open(filename_c_test2, "w") as fd:
2143         st.seek(0)
2144         shutil.copyfileobj(st, fd)
2145     st.close()  #
2146
2147     # Generate separate JSON file
2148     st = StringIO()
2149     generate_tojson(s, modulename, st)
2150     with open(filename_c_tojson, "w") as fd:
2151         st.seek(0)
2152         shutil.copyfileobj(st, fd)
2153     st.close()
2154     st = StringIO()
2155     generate_fromjson(s, modulename, st)
2156     with open(filename_c_fromjson, "w") as fd:
2157         st.seek(0)
2158         shutil.copyfileobj(st, fd)
2159     st.close()
2160
2161     output = TOP_BOILERPLATE.format(datestring=DATESTRING, input_filename=basename)
2162     output += generate_imports(s["Import"])
2163     output += msg_ids(s)
2164     output += msg_names(s)
2165     output += msg_name_crc_list(s, filename)
2166     output += typedefs(modulename)
2167     printfun_types(s["types"], stream, modulename)
2168     printfun(s["Define"], stream, modulename)
2169     output += stream.getvalue()
2170     stream.close()
2171     output += endianfun(s["types"] + s["Define"], modulename)
2172     output += calc_size_fun(s["types"] + s["Define"], modulename)
2173     output += version_tuple(s, basename)
2174     output += BOTTOM_BOILERPLATE.format(input_filename=basename, file_crc=s["file_crc"])
2175
2176     return output