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