]>
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 | # | |
10 | # This work is licensed under the terms of the GNU GPLv2. | |
11 | # See the COPYING.LIB file in the top-level directory. | |
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 = "" | |
131 | if len(args) == 0: | |
132 | return ret | |
133 | ||
134 | push_indent() | |
135 | ||
136 | if dealloc: | |
137 | ret += mcgen(''' | |
138 | md = qapi_dealloc_visitor_new(); | |
139 | v = qapi_dealloc_get_visitor(md); | |
140 | ''') | |
141 | else: | |
142 | ret += mcgen(''' | |
6d36d7dc | 143 | mi = qmp_input_visitor_new_strict(%(obj)s); |
c17d9908 MR |
144 | v = qmp_input_get_visitor(mi); |
145 | ''', | |
146 | obj=obj) | |
147 | ||
148 | for argname, argtype, optional, structured in parse_args(args): | |
149 | if optional: | |
150 | ret += mcgen(''' | |
151 | visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp); | |
152 | if (has_%(c_name)s) { | |
153 | ''', | |
154 | c_name=c_var(argname), name=argname) | |
155 | push_indent() | |
156 | ret += mcgen(''' | |
15e43e64 | 157 | %(visitor)s(v, &%(c_name)s, "%(name)s", errp); |
c17d9908 | 158 | ''', |
15e43e64 AL |
159 | c_name=c_var(argname), name=argname, argtype=argtype, |
160 | visitor=type_visitor(argtype)) | |
c17d9908 MR |
161 | if optional: |
162 | pop_indent() | |
163 | ret += mcgen(''' | |
164 | } | |
165 | visit_end_optional(v, errp); | |
166 | ''') | |
167 | ||
168 | if dealloc: | |
169 | ret += mcgen(''' | |
170 | qapi_dealloc_visitor_cleanup(md); | |
171 | ''') | |
172 | else: | |
173 | ret += mcgen(''' | |
174 | qmp_input_visitor_cleanup(mi); | |
175 | ''') | |
176 | pop_indent() | |
177 | return ret.rstrip() | |
178 | ||
776574d6 | 179 | def gen_marshal_output(name, args, ret_type, middle_mode): |
c17d9908 MR |
180 | if not ret_type: |
181 | return "" | |
776574d6 | 182 | |
c17d9908 MR |
183 | ret = mcgen(''' |
184 | static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) | |
185 | { | |
186 | QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); | |
187 | QmpOutputVisitor *mo = qmp_output_visitor_new(); | |
188 | Visitor *v; | |
189 | ||
190 | v = qmp_output_get_visitor(mo); | |
15e43e64 | 191 | %(visitor)s(v, &ret_in, "unused", errp); |
c17d9908 MR |
192 | if (!error_is_set(errp)) { |
193 | *ret_out = qmp_output_get_qobject(mo); | |
194 | } | |
195 | qmp_output_visitor_cleanup(mo); | |
196 | v = qapi_dealloc_get_visitor(md); | |
15e43e64 | 197 | %(visitor)s(v, &ret_in, "unused", errp); |
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 MR |
219 | { |
220 | ''', | |
776574d6 AL |
221 | header=hdr) |
222 | ||
223 | if middle_mode: | |
224 | ret += mcgen(''' | |
225 | Error *local_err = NULL; | |
226 | Error **errp = &local_err; | |
227 | QDict *args = (QDict *)qdict; | |
228 | ''') | |
c17d9908 MR |
229 | |
230 | if ret_type: | |
231 | if c_type(ret_type).endswith("*"): | |
232 | retval = " %s retval = NULL;" % c_type(ret_type) | |
233 | else: | |
234 | retval = " %s retval;" % c_type(ret_type) | |
235 | ret += mcgen(''' | |
236 | %(retval)s | |
237 | ''', | |
238 | retval=retval) | |
239 | ||
240 | if len(args) > 0: | |
241 | ret += mcgen(''' | |
242 | %(visitor_input_containers_decl)s | |
243 | %(visitor_input_vars_decl)s | |
244 | ||
245 | %(visitor_input_block)s | |
246 | ||
247 | ''', | |
248 | visitor_input_containers_decl=gen_visitor_input_containers_decl(args), | |
249 | visitor_input_vars_decl=gen_visitor_input_vars_decl(args), | |
250 | visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) | |
776574d6 AL |
251 | else: |
252 | ret += mcgen(''' | |
253 | (void)args; | |
254 | ''') | |
c17d9908 MR |
255 | |
256 | ret += mcgen(''' | |
257 | if (error_is_set(errp)) { | |
258 | goto out; | |
259 | } | |
260 | %(sync_call)s | |
261 | ''', | |
262 | sync_call=gen_sync_call(name, args, ret_type, indent=4)) | |
263 | ret += mcgen(''' | |
264 | ||
265 | out: | |
266 | ''') | |
267 | ret += mcgen(''' | |
268 | %(visitor_input_block_cleanup)s | |
776574d6 AL |
269 | ''', |
270 | visitor_input_block_cleanup=gen_visitor_input_block(args, None, | |
271 | dealloc=True)) | |
272 | ||
273 | if middle_mode: | |
274 | ret += mcgen(''' | |
275 | ||
276 | if (local_err) { | |
277 | qerror_report_err(local_err); | |
278 | error_free(local_err); | |
279 | return -1; | |
280 | } | |
281 | return 0; | |
282 | ''') | |
283 | else: | |
284 | ret += mcgen(''' | |
c17d9908 | 285 | return; |
776574d6 AL |
286 | ''') |
287 | ||
288 | ret += mcgen(''' | |
c17d9908 | 289 | } |
776574d6 AL |
290 | ''') |
291 | ||
c17d9908 MR |
292 | return ret |
293 | ||
d34b867d LC |
294 | def option_value_matches(opt, val, cmd): |
295 | if opt in cmd and cmd[opt] == val: | |
296 | return True | |
297 | return False | |
298 | ||
c17d9908 MR |
299 | def gen_registry(commands): |
300 | registry="" | |
301 | push_indent() | |
302 | for cmd in commands: | |
d34b867d LC |
303 | options = 'QCO_NO_OPTIONS' |
304 | if option_value_matches('success-response', 'no', cmd): | |
305 | options = 'QCO_NO_SUCCESS_RESP' | |
306 | ||
c17d9908 | 307 | registry += mcgen(''' |
d34b867d | 308 | qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); |
c17d9908 | 309 | ''', |
d34b867d LC |
310 | name=cmd['command'], c_name=c_fun(cmd['command']), |
311 | opts=options) | |
c17d9908 MR |
312 | pop_indent() |
313 | ret = mcgen(''' | |
314 | static void qmp_init_marshal(void) | |
315 | { | |
316 | %(registry)s | |
317 | } | |
318 | ||
319 | qapi_init(qmp_init_marshal); | |
320 | ''', | |
321 | registry=registry.rstrip()) | |
322 | return ret | |
323 | ||
324 | def gen_command_decl_prologue(header, guard, prefix=""): | |
325 | ret = mcgen(''' | |
326 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
327 | ||
328 | /* | |
329 | * schema-defined QAPI function prototypes | |
330 | * | |
331 | * Copyright IBM, Corp. 2011 | |
332 | * | |
333 | * Authors: | |
334 | * Anthony Liguori <[email protected]> | |
335 | * | |
336 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
337 | * See the COPYING.LIB file in the top-level directory. | |
338 | * | |
339 | */ | |
340 | ||
341 | #ifndef %(guard)s | |
342 | #define %(guard)s | |
343 | ||
344 | #include "%(prefix)sqapi-types.h" | |
08b76b9f | 345 | #include "qdict.h" |
c17d9908 MR |
346 | #include "error.h" |
347 | ||
348 | ''', | |
776574d6 | 349 | header=basename(header), guard=guardname(header), prefix=prefix) |
c17d9908 MR |
350 | return ret |
351 | ||
352 | def gen_command_def_prologue(prefix="", proxy=False): | |
353 | ret = mcgen(''' | |
354 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
355 | ||
356 | /* | |
357 | * schema-defined QMP->QAPI command dispatch | |
358 | * | |
359 | * Copyright IBM, Corp. 2011 | |
360 | * | |
361 | * Authors: | |
362 | * Anthony Liguori <[email protected]> | |
363 | * | |
364 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
365 | * See the COPYING.LIB file in the top-level directory. | |
366 | * | |
367 | */ | |
368 | ||
369 | #include "qemu-objects.h" | |
370 | #include "qapi/qmp-core.h" | |
371 | #include "qapi/qapi-visit-core.h" | |
372 | #include "qapi/qmp-output-visitor.h" | |
373 | #include "qapi/qmp-input-visitor.h" | |
374 | #include "qapi/qapi-dealloc-visitor.h" | |
375 | #include "%(prefix)sqapi-types.h" | |
376 | #include "%(prefix)sqapi-visit.h" | |
377 | ||
378 | ''', | |
379 | prefix=prefix) | |
380 | if not proxy: | |
381 | ret += '#include "%sqmp-commands.h"' % prefix | |
776574d6 | 382 | return ret + "\n\n" |
c17d9908 MR |
383 | |
384 | ||
385 | try: | |
8d3bc517 AK |
386 | opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:m", |
387 | ["source", "header", "prefix=", | |
388 | "output-dir=", "type=", "middle"]) | |
c17d9908 MR |
389 | except getopt.GetoptError, err: |
390 | print str(err) | |
391 | sys.exit(1) | |
392 | ||
393 | output_dir = "" | |
394 | prefix = "" | |
395 | dispatch_type = "sync" | |
396 | c_file = 'qmp-marshal.c' | |
397 | h_file = 'qmp-commands.h' | |
776574d6 | 398 | middle_mode = False |
c17d9908 | 399 | |
8d3bc517 AK |
400 | do_c = False |
401 | do_h = False | |
402 | ||
c17d9908 MR |
403 | for o, a in opts: |
404 | if o in ("-p", "--prefix"): | |
405 | prefix = a | |
406 | elif o in ("-o", "--output-dir"): | |
407 | output_dir = a + "/" | |
408 | elif o in ("-t", "--type"): | |
409 | dispatch_type = a | |
776574d6 AL |
410 | elif o in ("-m", "--middle"): |
411 | middle_mode = True | |
8d3bc517 | 412 | elif o in ("-c", "--source"): |
8d3bc517 | 413 | do_c = True |
19bf7c87 AK |
414 | elif o in ("-h", "--header"): |
415 | do_h = True | |
8d3bc517 AK |
416 | |
417 | if not do_c and not do_h: | |
418 | do_c = True | |
419 | do_h = True | |
c17d9908 MR |
420 | |
421 | c_file = output_dir + prefix + c_file | |
422 | h_file = output_dir + prefix + h_file | |
423 | ||
8d3bc517 | 424 | def maybe_open(really, name, opt): |
8d3bc517 AK |
425 | if really: |
426 | return open(name, opt) | |
427 | else: | |
19bf7c87 AK |
428 | import StringIO |
429 | return StringIO.StringIO() | |
8d3bc517 | 430 | |
c17d9908 MR |
431 | try: |
432 | os.makedirs(output_dir) | |
433 | except os.error, e: | |
434 | if e.errno != errno.EEXIST: | |
435 | raise | |
436 | ||
437 | exprs = parse_schema(sys.stdin) | |
438 | commands = filter(lambda expr: expr.has_key('command'), exprs) | |
5dbee474 | 439 | commands = filter(lambda expr: not expr.has_key('gen'), commands) |
c17d9908 MR |
440 | |
441 | if dispatch_type == "sync": | |
8d3bc517 AK |
442 | fdecl = maybe_open(do_h, h_file, 'w') |
443 | fdef = maybe_open(do_c, c_file, 'w') | |
c17d9908 MR |
444 | ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) |
445 | fdecl.write(ret) | |
446 | ret = gen_command_def_prologue(prefix=prefix) | |
447 | fdef.write(ret) | |
448 | ||
449 | for cmd in commands: | |
450 | arglist = [] | |
451 | ret_type = None | |
452 | if cmd.has_key('data'): | |
453 | arglist = cmd['data'] | |
454 | if cmd.has_key('returns'): | |
455 | ret_type = cmd['returns'] | |
456 | ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" | |
457 | fdecl.write(ret) | |
458 | if ret_type: | |
776574d6 | 459 | ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" |
c17d9908 | 460 | fdef.write(ret) |
776574d6 AL |
461 | |
462 | if middle_mode: | |
463 | fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) | |
464 | ||
465 | ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" | |
c17d9908 MR |
466 | fdef.write(ret) |
467 | ||
7534ba01 | 468 | fdecl.write("\n#endif\n"); |
776574d6 AL |
469 | |
470 | if not middle_mode: | |
471 | ret = gen_registry(commands) | |
472 | fdef.write(ret) | |
c17d9908 MR |
473 | |
474 | fdef.flush() | |
475 | fdef.close() | |
476 | fdecl.flush() | |
477 | fdecl.close() |