qapi script: add event support
[qemu.git] / scripts / qapi-event.py
1 #
2 # QAPI event generator
3 #
4 # Copyright (c) 2014 Wenchao Xia
5 #
6 # Authors:
7 # Wenchao Xia <wenchaoqemu@gmail.com>
8 #
9 # This work is licensed under the terms of the GNU GPL, version 2.
10 # See the COPYING file in the top-level directory.
11
12 from ordereddict import OrderedDict
13 from qapi import *
14 import sys
15 import os
16 import getopt
17 import errno
18
19 def _generate_event_api_name(event_name, params):
20 api_name = "void qapi_event_send_%s(" % c_fun(event_name).lower();
21 l = len(api_name)
22
23 if params:
24 for argname, argentry, optional, structured in parse_args(params):
25 if optional:
26 api_name += "bool has_%s,\n" % c_var(argname)
27 api_name += "".ljust(l)
28
29 if argentry == "str":
30 api_name += "const "
31 api_name += "%s %s,\n" % (c_type(argentry), c_var(argname))
32 api_name += "".ljust(l)
33
34 api_name += "Error **errp)"
35 return api_name;
36
37
38 # Following are the core functions that generate C APIs to emit event.
39
40 def generate_event_declaration(api_name):
41 return mcgen('''
42
43 %(api_name)s;
44 ''',
45 api_name = api_name)
46
47 def generate_event_implement(api_name, event_name, params):
48 # step 1: declare any variables
49 ret = mcgen("""
50
51 %(api_name)s
52 {
53 QDict *qmp;
54 Error *local_err = NULL;
55 QMPEventFuncEmit emit;
56 """,
57 api_name = api_name)
58
59 if params:
60 ret += mcgen("""
61 QmpOutputVisitor *qov;
62 Visitor *v;
63 QObject *obj;
64
65 """)
66
67 # step 2: check emit function, create a dict
68 ret += mcgen("""
69 emit = qmp_event_get_func_emit();
70 if (!emit) {
71 return;
72 }
73
74 qmp = qmp_event_build_dict("%(event_name)s");
75
76 """,
77 event_name = event_name)
78
79 # step 3: visit the params if params != None
80 if params:
81 ret += mcgen("""
82 qov = qmp_output_visitor_new();
83 g_assert(qov);
84
85 v = qmp_output_get_visitor(qov);
86 g_assert(v);
87
88 /* Fake visit, as if all members are under a structure */
89 visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
90 if (local_err) {
91 goto clean;
92 }
93
94 """,
95 event_name = event_name)
96
97 for argname, argentry, optional, structured in parse_args(params):
98 if optional:
99 ret += mcgen("""
100 if (has_%(var)s) {
101 """,
102 var = c_var(argname))
103 push_indent()
104
105 if argentry == "str":
106 var_type = "(char **)"
107 else:
108 var_type = ""
109
110 ret += mcgen("""
111 visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
112 if (local_err) {
113 goto clean;
114 }
115 """,
116 var_type = var_type,
117 var = c_var(argname),
118 type = type_name(argentry),
119 name = argname)
120
121 if optional:
122 pop_indent()
123 ret += mcgen("""
124 }
125 """)
126
127 ret += mcgen("""
128
129 visit_end_struct(v, &local_err);
130 if (local_err) {
131 goto clean;
132 }
133
134 obj = qmp_output_get_qobject(qov);
135 g_assert(obj != NULL);
136
137 qdict_put_obj(qmp, "data", obj);
138 """)
139
140 # step 4: call qmp event api
141 ret += mcgen("""
142 emit(%(event_enum_value)s, qmp, &local_err);
143
144 """,
145 event_enum_value = event_enum_value)
146
147 # step 5: clean up
148 if params:
149 ret += mcgen("""
150 clean:
151 qmp_output_visitor_cleanup(qov);
152 """)
153 ret += mcgen("""
154 error_propagate(errp, local_err);
155 QDECREF(qmp);
156 }
157 """)
158
159 return ret
160
161
162 # Following are the functions that generate an enum type for all defined
163 # events, similar to qapi-types.py. Here we already have enum name and
164 # values which were generated before and recorded in event_enum_*. It also
165 # works around the issue that "import qapi-types" can't work.
166
167 def generate_event_enum_decl(event_enum_name, event_enum_values):
168 lookup_decl = mcgen('''
169
170 extern const char *%(event_enum_name)s_lookup[];
171 ''',
172 event_enum_name = event_enum_name)
173
174 enum_decl = mcgen('''
175 typedef enum %(event_enum_name)s
176 {
177 ''',
178 event_enum_name = event_enum_name)
179
180 # append automatically generated _MAX value
181 enum_max_value = generate_enum_full_value(event_enum_name, "MAX")
182 enum_values = event_enum_values + [ enum_max_value ]
183
184 i = 0
185 for value in enum_values:
186 enum_decl += mcgen('''
187 %(value)s = %(i)d,
188 ''',
189 value = value,
190 i = i)
191 i += 1
192
193 enum_decl += mcgen('''
194 } %(event_enum_name)s;
195 ''',
196 event_enum_name = event_enum_name)
197
198 return lookup_decl + enum_decl
199
200 def generate_event_enum_lookup(event_enum_name, event_enum_strings):
201 ret = mcgen('''
202
203 const char *%(event_enum_name)s_lookup[] = {
204 ''',
205 event_enum_name = event_enum_name)
206
207 i = 0
208 for string in event_enum_strings:
209 ret += mcgen('''
210 "%(string)s",
211 ''',
212 string = string)
213
214 ret += mcgen('''
215 NULL,
216 };
217 ''')
218 return ret
219
220
221 # Start the real job
222
223 try:
224 opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:i:o:",
225 ["source", "header", "builtins", "prefix=",
226 "input-file=", "output-dir="])
227 except getopt.GetoptError, err:
228 print str(err)
229 sys.exit(1)
230
231 input_file = ""
232 output_dir = ""
233 prefix = ""
234 c_file = 'qapi-event.c'
235 h_file = 'qapi-event.h'
236
237 do_c = False
238 do_h = False
239 do_builtins = False
240
241 for o, a in opts:
242 if o in ("-p", "--prefix"):
243 prefix = a
244 elif o in ("-i", "--input-file"):
245 input_file = a
246 elif o in ("-o", "--output-dir"):
247 output_dir = a + "/"
248 elif o in ("-c", "--source"):
249 do_c = True
250 elif o in ("-h", "--header"):
251 do_h = True
252 elif o in ("-b", "--builtins"):
253 do_builtins = True
254
255 if not do_c and not do_h:
256 do_c = True
257 do_h = True
258
259 c_file = output_dir + prefix + c_file
260 h_file = output_dir + prefix + h_file
261
262 try:
263 os.makedirs(output_dir)
264 except os.error, e:
265 if e.errno != errno.EEXIST:
266 raise
267
268 def maybe_open(really, name, opt):
269 if really:
270 return open(name, opt)
271 else:
272 import StringIO
273 return StringIO.StringIO()
274
275 fdef = maybe_open(do_c, c_file, 'w')
276 fdecl = maybe_open(do_h, h_file, 'w')
277
278 fdef.write(mcgen('''
279 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
280
281 /*
282 * schema-defined QAPI event functions
283 *
284 * Copyright (c) 2014 Wenchao Xia
285 *
286 * Authors:
287 * Wenchao Xia <wenchaoqemu@gmail.com>
288 *
289 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
290 * See the COPYING.LIB file in the top-level directory.
291 *
292 */
293
294 #include "qemu-common.h"
295 #include "%(header)s"
296 #include "%(prefix)sqapi-visit.h"
297 #include "qapi/qmp-output-visitor.h"
298 #include "qapi/qmp-event.h"
299
300 ''',
301 prefix=prefix, header=basename(h_file)))
302
303 fdecl.write(mcgen('''
304 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
305
306 /*
307 * schema-defined QAPI event functions
308 *
309 * Copyright (c) 2014 Wenchao Xia
310 *
311 * Authors:
312 * Wenchao Xia <wenchaoqemu@gmail.com>
313 *
314 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
315 * See the COPYING.LIB file in the top-level directory.
316 *
317 */
318
319 #ifndef %(guard)s
320 #define %(guard)s
321
322 #include "qapi/error.h"
323 #include "qapi/qmp/qdict.h"
324 #include "%(prefix)sqapi-types.h"
325
326 ''',
327 prefix=prefix, guard=guardname(h_file)))
328
329 exprs = parse_schema(input_file)
330
331 event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent"
332 event_enum_values = []
333 event_enum_strings = []
334
335 for expr in exprs:
336 if expr.has_key('event'):
337 event_name = expr['event']
338 params = expr.get('data')
339 if params and len(params) == 0:
340 params = None
341
342 api_name = _generate_event_api_name(event_name, params)
343 ret = generate_event_declaration(api_name)
344 fdecl.write(ret)
345
346 # We need an enum value per event
347 event_enum_value = generate_enum_full_value(event_enum_name,
348 event_name)
349 ret = generate_event_implement(api_name, event_name, params)
350 fdef.write(ret)
351
352 # Record it, and generate enum later
353 event_enum_values.append(event_enum_value)
354 event_enum_strings.append(event_name)
355
356 ret = generate_event_enum_decl(event_enum_name, event_enum_values)
357 fdecl.write(ret)
358 ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
359 fdef.write(ret)
360
361 fdecl.write('''
362 #endif
363 ''')
364
365 fdecl.flush()
366 fdecl.close()
367
368 fdef.flush()
369 fdef.close()