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