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