vppapigen: allow for enum size other than u32
[vpp.git] / src / tools / vppapigen / vppapigen_c.py
1 # C generation
2 import datetime
3 import os
4 import time
5
6 datestring = datetime.datetime.utcfromtimestamp(
7     int(os.environ.get('SOURCE_DATE_EPOCH', time.time())))
8 input_filename = 'inputfil'
9 top_boilerplate = '''\
10 /*
11  * VLIB API definitions {datestring}
12  * Input file: {input_filename}
13  * Automatically generated: please edit the input file NOT this file!
14  */
15
16 #if defined(vl_msg_id)||defined(vl_union_id) \\
17     || defined(vl_printfun) ||defined(vl_endianfun) \\
18     || defined(vl_api_version)||defined(vl_typedefs) \\
19     || defined(vl_msg_name)||defined(vl_msg_name_crc_list) \\
20     || defined(vl_api_version_tuple)
21 /* ok, something was selected */
22 #else
23 #warning no content included from {input_filename}
24 #endif
25
26 #define VL_API_PACKED(x) x __attribute__ ((packed))
27 '''
28
29 bottom_boilerplate = '''\
30 /****** API CRC (whole file) *****/
31
32 #ifdef vl_api_version
33 vl_api_version({input_filename}, {file_crc:#08x})
34
35 #endif
36 '''
37
38
39 def msg_ids(s):
40     output = '''\
41
42 /****** Message ID / handler enum ******/
43
44 #ifdef vl_msg_id
45 '''
46
47     for t in s['Define']:
48         output += "vl_msg_id(VL_API_%s, vl_api_%s_t_handler)\n" % \
49                   (t.name.upper(), t.name)
50     output += "#endif"
51
52     return output
53
54
55 def msg_names(s):
56     output = '''\
57
58 /****** Message names ******/
59
60 #ifdef vl_msg_name
61 '''
62
63     for t in s['Define']:
64         dont_trace = 0 if t.dont_trace else 1
65         output += "vl_msg_name(vl_api_%s_t, %d)\n" % (t.name, dont_trace)
66     output += "#endif"
67
68     return output
69
70
71 def msg_name_crc_list(s, suffix):
72     output = '''\
73
74 /****** Message name, crc list ******/
75
76 #ifdef vl_msg_name_crc_list
77 '''
78     output += "#define foreach_vl_msg_name_crc_%s " % suffix
79
80     for t in s['Define']:
81         output += "\\\n_(VL_API_%s, %s, %08x) " % \
82                    (t.name.upper(), t.name, t.crc)
83     output += "\n#endif"
84
85     return output
86
87
88 def duplicate_wrapper_head(name):
89     s = "#ifndef _vl_api_defined_%s\n" % name
90     s += "#define _vl_api_defined_%s\n" % name
91     return s
92
93
94 def duplicate_wrapper_tail():
95     return '#endif\n\n'
96
97
98 def api2c(fieldtype):
99     mappingtable = {'string': 'vl_api_string_t', }
100     if fieldtype in mappingtable:
101         return mappingtable[fieldtype]
102     return fieldtype
103
104
105 def typedefs(objs, aliases, filename):
106     name = filename.replace('.', '_')
107     output = '''\
108
109
110 /****** Typedefs ******/
111
112 #ifdef vl_typedefs
113 #ifndef included_{module}
114 #define included_{module}
115 '''
116     output = output.format(module=name)
117
118     for k, v in aliases.items():
119         output += duplicate_wrapper_head(k)
120         if 'length' in v:
121             output +=  'typedef %s vl_api_%s_t[%s];\n' % (v['type'], k, v['length'])
122         else:
123             output += 'typedef %s vl_api_%s_t;\n' % (v['type'], k)
124         output += duplicate_wrapper_tail()
125
126     for o in objs:
127         tname = o.__class__.__name__
128         output += duplicate_wrapper_head(o.name)
129         if tname == 'Enum':
130             if o.enumtype == 'u32':
131                 output += "typedef enum {\n"
132             else:
133                 output += "typedef enum __attribute__((__packed__)) {\n"
134
135             for b in o.block:
136                 output += "    %s = %s,\n" % (b[0], b[1])
137             output += '} vl_api_%s_t;\n' % o.name
138             if o.enumtype != 'u32':
139                 size1 = 'sizeof(vl_api_%s_t)' % o.name
140                 size2 = 'sizeof(%s)' % o.enumtype
141                 err_str = 'size of API enum %s is wrong' % o.name
142                 output += 'STATIC_ASSERT(%s == %s, "%s");\n' % (size1, size2, err_str)
143         else:
144             if tname == 'Union':
145                 output += "typedef VL_API_PACKED(union _vl_api_%s {\n" % o.name
146             else:
147                 output += "typedef VL_API_PACKED(struct _vl_api_%s {\n" % o.name
148             for b in o.block:
149                 if b.type == 'Field':
150                     output += "    %s %s;\n" % (api2c(b.fieldtype), b.fieldname)
151                 elif b.type == 'Array':
152                     if b.lengthfield:
153                         output += "    %s %s[0];\n" % (api2c(b.fieldtype), b.fieldname)
154                     else:
155                         output += "    %s %s[%s];\n" % (api2c(b.fieldtype), b.fieldname,
156                                                         b.length)
157                 else:
158                     raise ValueError("Error in processing array type %s" % b)
159
160             output += '}) vl_api_%s_t;\n' % o.name
161         output += duplicate_wrapper_tail()
162
163     output += "\n#endif"
164     output += "\n#endif\n\n"
165
166     return output
167
168
169 format_strings = {'u8': '%u',
170                   'i8': '%d',
171                   'u16': '%u',
172                   'i16': '%d',
173                   'u32': '%u',
174                   'i32': '%ld',
175                   'u64': '%llu',
176                   'i64': '%llu',
177                   'f64': '%.2f', }
178
179
180 def printfun(objs):
181     output = '''\
182 /****** Print functions *****/
183 #ifdef vl_printfun
184
185 #ifdef LP64
186 #define _uword_fmt \"%lld\"
187 #define _uword_cast (long long)
188 #else
189 #define _uword_fmt \"%ld\"
190 #define _uword_cast long
191 #endif
192
193 '''
194     for t in objs:
195         if t.__class__.__name__ == 'Enum':
196             continue
197         if t.manual_print:
198             output += "/***** manual: vl_api_%s_t_print  *****/\n\n" % t.name
199             continue
200         output += duplicate_wrapper_head(t.name + '_t_print')
201         output += "static inline void *vl_api_%s_t_print (vl_api_%s_t *a," % \
202                   (t.name, t.name)
203         output += "void *handle)\n{\n"
204         output += "    vl_print(handle, \"vl_api_%s_t:\\n\");\n" % t.name
205
206         for o in t.block:
207             if o.type != 'Field':
208                 continue
209             if o.fieldtype in format_strings:
210                 output += "    vl_print(handle, \"%s: %s\\n\", a->%s);\n" % \
211                           (o.fieldname, format_strings[o.fieldtype],
212                            o.fieldname)
213
214         output += '    return handle;\n'
215         output += '}\n\n'
216         output += duplicate_wrapper_tail()
217
218     output += "\n#endif /* vl_printfun */\n"
219
220     return output
221
222
223 endian_strings = {
224     'u16': 'clib_net_to_host_u16',
225     'u32': 'clib_net_to_host_u32',
226     'u64': 'clib_net_to_host_u64',
227     'i16': 'clib_net_to_host_u16',
228     'i32': 'clib_net_to_host_u32',
229     'i64': 'clib_net_to_host_u64',
230 }
231
232
233 def endianfun(objs):
234     output = '''\
235
236 /****** Endian swap functions *****/\n\
237 #ifdef vl_endianfun
238
239 #undef clib_net_to_host_uword
240 #ifdef LP64
241 #define clib_net_to_host_uword clib_net_to_host_u64
242 #else
243 #define clib_net_to_host_uword clib_net_to_host_u32
244 #endif
245
246 '''
247
248     for t in objs:
249         if t.__class__.__name__ == 'Enum':
250             continue
251         if t.manual_endian:
252             output += "/***** manual: vl_api_%s_t_endian  *****/\n\n" % t.name
253             continue
254         output += duplicate_wrapper_head(t.name + '_t_endian')
255         output += "static inline void vl_api_%s_t_endian (vl_api_%s_t *a)" % \
256                   (t.name, t.name)
257         output += "\n{\n"
258
259         for o in t.block:
260             if o.type != 'Field':
261                 continue
262             if o.fieldtype in endian_strings:
263                 output += "    a->%s = %s(a->%s);\n" % \
264                         (o.fieldname, endian_strings[o.fieldtype], o.fieldname)
265             else:
266                 output += "    /* a->%s = a->%s (no-op) */\n" % \
267                                           (o.fieldname, o.fieldname)
268
269         output += '}\n\n'
270         output += duplicate_wrapper_tail()
271     output += "\n#endif /* vl_endianfun */\n\n"
272
273     return output
274
275
276 def version_tuple(s, module):
277     output = '''\
278 /****** Version tuple *****/
279
280 #ifdef vl_api_version_tuple
281
282 '''
283     if 'version' in s['Option']:
284         v = s['Option']['version']
285         (major, minor, patch) = v.split('.')
286         output += "vl_api_version_tuple(%s, %s, %s, %s)\n" % \
287                   (module, major, minor, patch)
288
289     output += "\n#endif /* vl_api_version_tuple */\n\n"
290
291     return output
292
293
294 #
295 # Plugin entry point
296 #
297 def run(input_filename, s, file_crc):
298     basename = os.path.basename(input_filename)
299     filename, file_extension = os.path.splitext(basename)
300     output = top_boilerplate.format(datestring=datestring,
301                                     input_filename=basename)
302     output += msg_ids(s)
303     output += msg_names(s)
304     output += msg_name_crc_list(s, filename)
305     output += typedefs(s['types'] + s['Define'], s['Alias'], filename + file_extension)
306     output += printfun(s['types'] + s['Define'])
307     output += endianfun(s['types'] + s['Define'])
308     output += version_tuple(s, basename)
309     output += bottom_boilerplate.format(input_filename=basename,
310                                         file_crc=file_crc)
311
312     return output