]>
Commit | Line | Data |
---|---|---|
c17d9908 MR |
1 | # |
2 | # QAPI command marshaller generator | |
3 | # | |
4 | # Copyright IBM, Corp. 2011 | |
297a3646 | 5 | # Copyright (C) 2014 Red Hat, Inc. |
c17d9908 MR |
6 | # |
7 | # Authors: | |
8 | # Anthony Liguori <[email protected]> | |
9 | # Michael Roth <[email protected]> | |
297a3646 | 10 | # Markus Armbruster <[email protected]> |
c17d9908 | 11 | # |
678e48a2 MA |
12 | # This work is licensed under the terms of the GNU GPL, version 2. |
13 | # See the COPYING file in the top-level directory. | |
c17d9908 MR |
14 | |
15 | from ordereddict import OrderedDict | |
16 | from qapi import * | |
297a3646 | 17 | import re |
c17d9908 MR |
18 | import sys |
19 | import os | |
20 | import getopt | |
21 | import errno | |
22 | ||
15e43e64 AL |
23 | def type_visitor(name): |
24 | if type(name) == list: | |
25 | return 'visit_type_%sList' % name[0] | |
26 | else: | |
27 | return 'visit_type_%s' % name | |
28 | ||
c17d9908 MR |
29 | def generate_command_decl(name, args, ret_type): |
30 | arglist="" | |
31 | for argname, argtype, optional, structured in parse_args(args): | |
0d14eeb2 | 32 | argtype = c_type(argtype, is_param=True) |
c17d9908 MR |
33 | if optional: |
34 | arglist += "bool has_%s, " % c_var(argname) | |
35 | arglist += "%s %s, " % (argtype, c_var(argname)) | |
36 | return mcgen(''' | |
37 | %(ret_type)s qmp_%(name)s(%(args)sError **errp); | |
38 | ''', | |
c9da228b | 39 | ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() |
c17d9908 | 40 | |
297a3646 MA |
41 | def gen_err_check(errvar): |
42 | if errvar: | |
43 | return mcgen(''' | |
44 | if (local_err) { | |
45 | goto out; | |
46 | } | |
47 | ''') | |
48 | return '' | |
49 | ||
c17d9908 MR |
50 | def gen_sync_call(name, args, ret_type, indent=0): |
51 | ret = "" | |
52 | arglist="" | |
53 | retval="" | |
54 | if ret_type: | |
55 | retval = "retval = " | |
56 | for argname, argtype, optional, structured in parse_args(args): | |
57 | if optional: | |
58 | arglist += "has_%s, " % c_var(argname) | |
59 | arglist += "%s, " % (c_var(argname)) | |
60 | push_indent(indent) | |
61 | ret = mcgen(''' | |
297a3646 | 62 | %(retval)sqmp_%(name)s(%(args)s&local_err); |
c17d9908 MR |
63 | |
64 | ''', | |
c9da228b | 65 | name=c_fun(name), args=arglist, retval=retval).rstrip() |
c17d9908 | 66 | if ret_type: |
297a3646 | 67 | ret += "\n" + gen_err_check('local_err') |
c17d9908 | 68 | ret += "\n" + mcgen('''' |
297a3646 | 69 | %(marshal_output_call)s |
c17d9908 MR |
70 | ''', |
71 | marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() | |
72 | pop_indent(indent) | |
73 | return ret.rstrip() | |
74 | ||
75 | ||
76 | def gen_marshal_output_call(name, ret_type): | |
77 | if not ret_type: | |
78 | return "" | |
297a3646 | 79 | return "qmp_marshal_output_%s(retval, ret, &local_err);" % c_fun(name) |
c17d9908 | 80 | |
f9bee751 | 81 | def gen_visitor_input_containers_decl(args, obj): |
c17d9908 MR |
82 | ret = "" |
83 | ||
84 | push_indent() | |
85 | if len(args) > 0: | |
86 | ret += mcgen(''' | |
f9bee751 | 87 | QmpInputVisitor *mi = qmp_input_visitor_new_strict(%(obj)s); |
c17d9908 MR |
88 | QapiDeallocVisitor *md; |
89 | Visitor *v; | |
f9bee751 MA |
90 | ''', |
91 | obj=obj) | |
c17d9908 MR |
92 | pop_indent() |
93 | ||
94 | return ret.rstrip() | |
95 | ||
96 | def gen_visitor_input_vars_decl(args): | |
97 | ret = "" | |
98 | push_indent() | |
99 | for argname, argtype, optional, structured in parse_args(args): | |
100 | if optional: | |
101 | ret += mcgen(''' | |
102 | bool has_%(argname)s = false; | |
103 | ''', | |
104 | argname=c_var(argname)) | |
05dfb26c | 105 | if is_c_ptr(argtype): |
c17d9908 MR |
106 | ret += mcgen(''' |
107 | %(argtype)s %(argname)s = NULL; | |
108 | ''', | |
109 | argname=c_var(argname), argtype=c_type(argtype)) | |
110 | else: | |
111 | ret += mcgen(''' | |
fc13d937 | 112 | %(argtype)s %(argname)s = {0}; |
c17d9908 MR |
113 | ''', |
114 | argname=c_var(argname), argtype=c_type(argtype)) | |
115 | ||
116 | pop_indent() | |
117 | return ret.rstrip() | |
118 | ||
f9bee751 | 119 | def gen_visitor_input_block(args, dealloc=False): |
c17d9908 | 120 | ret = "" |
297a3646 MA |
121 | errparg = '&local_err' |
122 | errarg = 'local_err' | |
8f91ad8a | 123 | |
c17d9908 MR |
124 | if len(args) == 0: |
125 | return ret | |
126 | ||
127 | push_indent() | |
128 | ||
129 | if dealloc: | |
8f91ad8a | 130 | errparg = 'NULL' |
297a3646 | 131 | errarg = None; |
c17d9908 | 132 | ret += mcgen(''' |
f9bee751 | 133 | qmp_input_visitor_cleanup(mi); |
c17d9908 MR |
134 | md = qapi_dealloc_visitor_new(); |
135 | v = qapi_dealloc_get_visitor(md); | |
136 | ''') | |
137 | else: | |
138 | ret += mcgen(''' | |
c17d9908 | 139 | v = qmp_input_get_visitor(mi); |
f9bee751 | 140 | ''') |
c17d9908 MR |
141 | |
142 | for argname, argtype, optional, structured in parse_args(args): | |
143 | if optional: | |
144 | ret += mcgen(''' | |
e2cd0f4f | 145 | visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); |
c17d9908 | 146 | ''', |
8f91ad8a | 147 | c_name=c_var(argname), name=argname, errp=errparg) |
297a3646 MA |
148 | ret += gen_err_check(errarg) |
149 | ret += mcgen(''' | |
150 | if (has_%(c_name)s) { | |
151 | ''', | |
152 | c_name=c_var(argname)) | |
c17d9908 MR |
153 | push_indent() |
154 | ret += mcgen(''' | |
8f91ad8a | 155 | %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); |
c17d9908 | 156 | ''', |
15e43e64 | 157 | c_name=c_var(argname), name=argname, argtype=argtype, |
8f91ad8a | 158 | visitor=type_visitor(argtype), errp=errparg) |
297a3646 | 159 | ret += gen_err_check(errarg) |
c17d9908 MR |
160 | if optional: |
161 | pop_indent() | |
162 | ret += mcgen(''' | |
163 | } | |
e2cd0f4f | 164 | ''') |
c17d9908 MR |
165 | |
166 | if dealloc: | |
167 | ret += mcgen(''' | |
168 | qapi_dealloc_visitor_cleanup(md); | |
c17d9908 MR |
169 | ''') |
170 | pop_indent() | |
171 | return ret.rstrip() | |
172 | ||
776574d6 | 173 | def gen_marshal_output(name, args, ret_type, middle_mode): |
c17d9908 MR |
174 | if not ret_type: |
175 | return "" | |
776574d6 | 176 | |
c17d9908 MR |
177 | ret = mcgen(''' |
178 | static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) | |
179 | { | |
297a3646 | 180 | Error *local_err = NULL; |
c17d9908 | 181 | QmpOutputVisitor *mo = qmp_output_visitor_new(); |
f9bee751 | 182 | QapiDeallocVisitor *md; |
c17d9908 MR |
183 | Visitor *v; |
184 | ||
185 | v = qmp_output_get_visitor(mo); | |
297a3646 MA |
186 | %(visitor)s(v, &ret_in, "unused", &local_err); |
187 | if (local_err) { | |
188 | goto out; | |
c17d9908 | 189 | } |
297a3646 MA |
190 | *ret_out = qmp_output_get_qobject(mo); |
191 | ||
192 | out: | |
193 | error_propagate(errp, local_err); | |
c17d9908 | 194 | qmp_output_visitor_cleanup(mo); |
f9bee751 | 195 | md = qapi_dealloc_visitor_new(); |
c17d9908 | 196 | v = qapi_dealloc_get_visitor(md); |
8f91ad8a | 197 | %(visitor)s(v, &ret_in, "unused", NULL); |
c17d9908 MR |
198 | qapi_dealloc_visitor_cleanup(md); |
199 | } | |
200 | ''', | |
c9da228b | 201 | c_ret_type=c_type(ret_type), c_name=c_fun(name), |
15e43e64 | 202 | visitor=type_visitor(ret_type)) |
c17d9908 MR |
203 | |
204 | return ret | |
205 | ||
776574d6 AL |
206 | def gen_marshal_input_decl(name, args, ret_type, middle_mode): |
207 | if middle_mode: | |
c9da228b | 208 | return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_fun(name) |
776574d6 | 209 | else: |
c9da228b | 210 | return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_fun(name) |
776574d6 AL |
211 | |
212 | ||
213 | ||
214 | def gen_marshal_input(name, args, ret_type, middle_mode): | |
215 | hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) | |
216 | ||
c17d9908 | 217 | ret = mcgen(''' |
776574d6 | 218 | %(header)s |
c17d9908 | 219 | { |
297a3646 | 220 | Error *local_err = NULL; |
c17d9908 | 221 | ''', |
776574d6 AL |
222 | header=hdr) |
223 | ||
224 | if middle_mode: | |
225 | ret += mcgen(''' | |
776574d6 AL |
226 | QDict *args = (QDict *)qdict; |
227 | ''') | |
c17d9908 MR |
228 | |
229 | if ret_type: | |
05dfb26c | 230 | if is_c_ptr(ret_type): |
c17d9908 MR |
231 | retval = " %s retval = NULL;" % c_type(ret_type) |
232 | else: | |
233 | retval = " %s retval;" % c_type(ret_type) | |
234 | ret += mcgen(''' | |
235 | %(retval)s | |
236 | ''', | |
237 | retval=retval) | |
238 | ||
239 | if len(args) > 0: | |
240 | ret += mcgen(''' | |
241 | %(visitor_input_containers_decl)s | |
242 | %(visitor_input_vars_decl)s | |
243 | ||
244 | %(visitor_input_block)s | |
245 | ||
246 | ''', | |
f9bee751 | 247 | visitor_input_containers_decl=gen_visitor_input_containers_decl(args, "QOBJECT(args)"), |
c17d9908 | 248 | visitor_input_vars_decl=gen_visitor_input_vars_decl(args), |
f9bee751 | 249 | visitor_input_block=gen_visitor_input_block(args)) |
776574d6 AL |
250 | else: |
251 | ret += mcgen(''' | |
297a3646 | 252 | |
776574d6 AL |
253 | (void)args; |
254 | ''') | |
c17d9908 MR |
255 | |
256 | ret += mcgen(''' | |
c17d9908 MR |
257 | %(sync_call)s |
258 | ''', | |
259 | sync_call=gen_sync_call(name, args, ret_type, indent=4)) | |
297a3646 MA |
260 | if re.search('^ *goto out\\;', ret, re.MULTILINE): |
261 | ret += mcgen(''' | |
c17d9908 MR |
262 | |
263 | out: | |
297a3646 MA |
264 | ''') |
265 | if not middle_mode: | |
266 | ret += mcgen(''' | |
267 | error_propagate(errp, local_err); | |
c17d9908 MR |
268 | ''') |
269 | ret += mcgen(''' | |
270 | %(visitor_input_block_cleanup)s | |
776574d6 | 271 | ''', |
f9bee751 | 272 | visitor_input_block_cleanup=gen_visitor_input_block(args, |
776574d6 AL |
273 | dealloc=True)) |
274 | ||
275 | if middle_mode: | |
276 | ret += mcgen(''' | |
277 | ||
278 | if (local_err) { | |
279 | qerror_report_err(local_err); | |
280 | error_free(local_err); | |
281 | return -1; | |
282 | } | |
283 | return 0; | |
284 | ''') | |
285 | else: | |
286 | ret += mcgen(''' | |
c17d9908 | 287 | return; |
776574d6 AL |
288 | ''') |
289 | ||
290 | ret += mcgen(''' | |
c17d9908 | 291 | } |
776574d6 AL |
292 | ''') |
293 | ||
c17d9908 MR |
294 | return ret |
295 | ||
d34b867d LC |
296 | def option_value_matches(opt, val, cmd): |
297 | if opt in cmd and cmd[opt] == val: | |
298 | return True | |
299 | return False | |
300 | ||
c17d9908 MR |
301 | def gen_registry(commands): |
302 | registry="" | |
303 | push_indent() | |
304 | for cmd in commands: | |
d34b867d LC |
305 | options = 'QCO_NO_OPTIONS' |
306 | if option_value_matches('success-response', 'no', cmd): | |
307 | options = 'QCO_NO_SUCCESS_RESP' | |
308 | ||
c17d9908 | 309 | registry += mcgen(''' |
d34b867d | 310 | qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); |
c17d9908 | 311 | ''', |
d34b867d LC |
312 | name=cmd['command'], c_name=c_fun(cmd['command']), |
313 | opts=options) | |
c17d9908 MR |
314 | pop_indent() |
315 | ret = mcgen(''' | |
316 | static void qmp_init_marshal(void) | |
317 | { | |
318 | %(registry)s | |
319 | } | |
320 | ||
321 | qapi_init(qmp_init_marshal); | |
322 | ''', | |
323 | registry=registry.rstrip()) | |
324 | return ret | |
325 | ||
326 | def gen_command_decl_prologue(header, guard, prefix=""): | |
327 | ret = mcgen(''' | |
328 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
329 | ||
330 | /* | |
331 | * schema-defined QAPI function prototypes | |
332 | * | |
333 | * Copyright IBM, Corp. 2011 | |
334 | * | |
335 | * Authors: | |
336 | * Anthony Liguori <[email protected]> | |
337 | * | |
338 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
339 | * See the COPYING.LIB file in the top-level directory. | |
340 | * | |
341 | */ | |
342 | ||
343 | #ifndef %(guard)s | |
344 | #define %(guard)s | |
345 | ||
346 | #include "%(prefix)sqapi-types.h" | |
7b1b5d19 PB |
347 | #include "qapi/qmp/qdict.h" |
348 | #include "qapi/error.h" | |
c17d9908 MR |
349 | |
350 | ''', | |
776574d6 | 351 | header=basename(header), guard=guardname(header), prefix=prefix) |
c17d9908 MR |
352 | return ret |
353 | ||
354 | def gen_command_def_prologue(prefix="", proxy=False): | |
355 | ret = mcgen(''' | |
356 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
357 | ||
358 | /* | |
359 | * schema-defined QMP->QAPI command dispatch | |
360 | * | |
361 | * Copyright IBM, Corp. 2011 | |
362 | * | |
363 | * Authors: | |
364 | * Anthony Liguori <[email protected]> | |
365 | * | |
366 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
367 | * See the COPYING.LIB file in the top-level directory. | |
368 | * | |
369 | */ | |
370 | ||
79ee7df8 | 371 | #include "qemu-common.h" |
1de7afc9 | 372 | #include "qemu/module.h" |
7b1b5d19 PB |
373 | #include "qapi/qmp/qerror.h" |
374 | #include "qapi/qmp/types.h" | |
375 | #include "qapi/qmp/dispatch.h" | |
376 | #include "qapi/visitor.h" | |
c17d9908 MR |
377 | #include "qapi/qmp-output-visitor.h" |
378 | #include "qapi/qmp-input-visitor.h" | |
7b1b5d19 | 379 | #include "qapi/dealloc-visitor.h" |
c17d9908 MR |
380 | #include "%(prefix)sqapi-types.h" |
381 | #include "%(prefix)sqapi-visit.h" | |
382 | ||
383 | ''', | |
384 | prefix=prefix) | |
385 | if not proxy: | |
386 | ret += '#include "%sqmp-commands.h"' % prefix | |
776574d6 | 387 | return ret + "\n\n" |
c17d9908 MR |
388 | |
389 | ||
390 | try: | |
33aaad52 | 391 | opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:i:o:m", |
8d3bc517 | 392 | ["source", "header", "prefix=", |
33aaad52 LV |
393 | "input-file=", "output-dir=", |
394 | "type=", "middle"]) | |
c17d9908 MR |
395 | except getopt.GetoptError, err: |
396 | print str(err) | |
397 | sys.exit(1) | |
398 | ||
399 | output_dir = "" | |
400 | prefix = "" | |
401 | dispatch_type = "sync" | |
402 | c_file = 'qmp-marshal.c' | |
403 | h_file = 'qmp-commands.h' | |
776574d6 | 404 | middle_mode = False |
c17d9908 | 405 | |
8d3bc517 AK |
406 | do_c = False |
407 | do_h = False | |
408 | ||
c17d9908 MR |
409 | for o, a in opts: |
410 | if o in ("-p", "--prefix"): | |
411 | prefix = a | |
33aaad52 LV |
412 | elif o in ("-i", "--input-file"): |
413 | input_file = a | |
c17d9908 MR |
414 | elif o in ("-o", "--output-dir"): |
415 | output_dir = a + "/" | |
416 | elif o in ("-t", "--type"): | |
417 | dispatch_type = a | |
776574d6 AL |
418 | elif o in ("-m", "--middle"): |
419 | middle_mode = True | |
8d3bc517 | 420 | elif o in ("-c", "--source"): |
8d3bc517 | 421 | do_c = True |
19bf7c87 AK |
422 | elif o in ("-h", "--header"): |
423 | do_h = True | |
8d3bc517 AK |
424 | |
425 | if not do_c and not do_h: | |
426 | do_c = True | |
427 | do_h = True | |
c17d9908 MR |
428 | |
429 | c_file = output_dir + prefix + c_file | |
430 | h_file = output_dir + prefix + h_file | |
431 | ||
8d3bc517 | 432 | def maybe_open(really, name, opt): |
8d3bc517 AK |
433 | if really: |
434 | return open(name, opt) | |
435 | else: | |
19bf7c87 AK |
436 | import StringIO |
437 | return StringIO.StringIO() | |
8d3bc517 | 438 | |
c17d9908 MR |
439 | try: |
440 | os.makedirs(output_dir) | |
441 | except os.error, e: | |
442 | if e.errno != errno.EEXIST: | |
443 | raise | |
444 | ||
33aaad52 | 445 | exprs = parse_schema(input_file) |
c17d9908 | 446 | commands = filter(lambda expr: expr.has_key('command'), exprs) |
5dbee474 | 447 | commands = filter(lambda expr: not expr.has_key('gen'), commands) |
c17d9908 MR |
448 | |
449 | if dispatch_type == "sync": | |
8d3bc517 AK |
450 | fdecl = maybe_open(do_h, h_file, 'w') |
451 | fdef = maybe_open(do_c, c_file, 'w') | |
c17d9908 MR |
452 | ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) |
453 | fdecl.write(ret) | |
454 | ret = gen_command_def_prologue(prefix=prefix) | |
455 | fdef.write(ret) | |
456 | ||
457 | for cmd in commands: | |
458 | arglist = [] | |
459 | ret_type = None | |
460 | if cmd.has_key('data'): | |
461 | arglist = cmd['data'] | |
462 | if cmd.has_key('returns'): | |
463 | ret_type = cmd['returns'] | |
464 | ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" | |
465 | fdecl.write(ret) | |
466 | if ret_type: | |
776574d6 | 467 | ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" |
c17d9908 | 468 | fdef.write(ret) |
776574d6 AL |
469 | |
470 | if middle_mode: | |
471 | fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) | |
472 | ||
473 | ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" | |
c17d9908 MR |
474 | fdef.write(ret) |
475 | ||
7534ba01 | 476 | fdecl.write("\n#endif\n"); |
776574d6 AL |
477 | |
478 | if not middle_mode: | |
479 | ret = gen_registry(commands) | |
480 | fdef.write(ret) | |
c17d9908 MR |
481 | |
482 | fdef.flush() | |
483 | fdef.close() | |
484 | fdecl.flush() | |
485 | fdecl.close() |