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