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