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