root / scripts / qapi-commands.py @ f53ec699
History | View | Annotate | Download (12 kB)
1 |
#
|
---|---|
2 |
# QAPI command marshaller generator
|
3 |
#
|
4 |
# Copyright IBM, Corp. 2011
|
5 |
#
|
6 |
# Authors:
|
7 |
# Anthony Liguori <aliguori@us.ibm.com>
|
8 |
# Michael Roth <mdroth@linux.vnet.ibm.com>
|
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 |
|
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 |
|
26 |
def generate_decl_enum(name, members, genlist=True): |
27 |
return mcgen(''' |
28 |
|
29 |
void %(visitor)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
|
30 |
''',
|
31 |
visitor=type_visitor(name)) |
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 |
''',
|
45 |
ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() |
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 |
''',
|
62 |
name=c_fun(name), args=arglist, retval=retval).rstrip() |
63 |
if ret_type:
|
64 |
ret += "\n" + mcgen('''' |
65 |
if (!error_is_set(errp)) {
|
66 |
%(marshal_output_call)s
|
67 |
}
|
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 "" |
77 |
return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) |
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 |
errparg = 'errp'
|
132 |
|
133 |
if len(args) == 0: |
134 |
return ret
|
135 |
|
136 |
push_indent() |
137 |
|
138 |
if dealloc:
|
139 |
errparg = 'NULL'
|
140 |
ret += mcgen('''
|
141 |
md = qapi_dealloc_visitor_new();
|
142 |
v = qapi_dealloc_get_visitor(md);
|
143 |
''')
|
144 |
else:
|
145 |
ret += mcgen('''
|
146 |
mi = qmp_input_visitor_new_strict(%(obj)s);
|
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('''
|
154 |
visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
155 |
if (has_%(c_name)s) {
|
156 |
''',
|
157 |
c_name=c_var(argname), name=argname, errp=errparg) |
158 |
push_indent() |
159 |
ret += mcgen('''
|
160 |
%(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s);
|
161 |
''',
|
162 |
c_name=c_var(argname), name=argname, argtype=argtype, |
163 |
visitor=type_visitor(argtype), errp=errparg) |
164 |
if optional:
|
165 |
pop_indent() |
166 |
ret += mcgen('''
|
167 |
}
|
168 |
visit_end_optional(v, %(errp)s);
|
169 |
''', errp=errparg)
|
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 |
|
182 |
def gen_marshal_output(name, args, ret_type, middle_mode): |
183 |
if not ret_type: |
184 |
return "" |
185 |
|
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);
|
194 |
%(visitor)s(v, &ret_in, "unused", errp);
|
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);
|
200 |
%(visitor)s(v, &ret_in, "unused", NULL);
|
201 |
qapi_dealloc_visitor_cleanup(md);
|
202 |
}
|
203 |
''',
|
204 |
c_ret_type=c_type(ret_type), c_name=c_fun(name), |
205 |
visitor=type_visitor(ret_type)) |
206 |
|
207 |
return ret
|
208 |
|
209 |
def gen_marshal_input_decl(name, args, ret_type, middle_mode): |
210 |
if middle_mode:
|
211 |
return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_fun(name) |
212 |
else:
|
213 |
return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_fun(name) |
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 |
|
220 |
ret = mcgen('''
|
221 |
%(header)s
|
222 |
{
|
223 |
''',
|
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 |
''')
|
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)"))
|
254 |
else:
|
255 |
ret += mcgen('''
|
256 |
(void)args;
|
257 |
''')
|
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
|
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('''
|
288 |
return;
|
289 |
''')
|
290 |
|
291 |
ret += mcgen('''
|
292 |
}
|
293 |
''')
|
294 |
|
295 |
return ret
|
296 |
|
297 |
def option_value_matches(opt, val, cmd): |
298 |
if opt in cmd and cmd[opt] == val: |
299 |
return True |
300 |
return False |
301 |
|
302 |
def gen_registry(commands): |
303 |
registry=""
|
304 |
push_indent() |
305 |
for cmd in commands: |
306 |
options = 'QCO_NO_OPTIONS'
|
307 |
if option_value_matches('success-response', 'no', cmd): |
308 |
options = 'QCO_NO_SUCCESS_RESP'
|
309 |
|
310 |
registry += mcgen('''
|
311 |
qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s);
|
312 |
''',
|
313 |
name=cmd['command'], c_name=c_fun(cmd['command']), |
314 |
opts=options) |
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 <aliguori@us.ibm.com>
|
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"
|
348 |
#include "qapi/qmp/qdict.h"
|
349 |
#include "qapi/error.h"
|
350 |
|
351 |
''',
|
352 |
header=basename(header), guard=guardname(header), prefix=prefix) |
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 <aliguori@us.ibm.com>
|
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 |
|
372 |
#include "qemu-common.h"
|
373 |
#include "qemu/module.h"
|
374 |
#include "qapi/qmp/qerror.h"
|
375 |
#include "qapi/qmp/types.h"
|
376 |
#include "qapi/qmp/dispatch.h"
|
377 |
#include "qapi/visitor.h"
|
378 |
#include "qapi/qmp-output-visitor.h"
|
379 |
#include "qapi/qmp-input-visitor.h"
|
380 |
#include "qapi/dealloc-visitor.h"
|
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
|
388 |
return ret + "\n\n" |
389 |
|
390 |
|
391 |
try:
|
392 |
opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:m", |
393 |
["source", "header", "prefix=", |
394 |
"output-dir=", "type=", "middle"]) |
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'
|
404 |
middle_mode = False
|
405 |
|
406 |
do_c = False
|
407 |
do_h = False
|
408 |
|
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 |
416 |
elif o in ("-m", "--middle"): |
417 |
middle_mode = True
|
418 |
elif o in ("-c", "--source"): |
419 |
do_c = True
|
420 |
elif o in ("-h", "--header"): |
421 |
do_h = True
|
422 |
|
423 |
if not do_c and not do_h: |
424 |
do_c = True
|
425 |
do_h = True
|
426 |
|
427 |
c_file = output_dir + prefix + c_file |
428 |
h_file = output_dir + prefix + h_file |
429 |
|
430 |
def maybe_open(really, name, opt): |
431 |
if really:
|
432 |
return open(name, opt) |
433 |
else:
|
434 |
import StringIO |
435 |
return StringIO.StringIO()
|
436 |
|
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) |
445 |
commands = filter(lambda expr: not expr.has_key('gen'), commands) |
446 |
|
447 |
if dispatch_type == "sync": |
448 |
fdecl = maybe_open(do_h, h_file, 'w')
|
449 |
fdef = maybe_open(do_c, c_file, 'w')
|
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:
|
465 |
ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" |
466 |
fdef.write(ret) |
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" |
472 |
fdef.write(ret) |
473 |
|
474 |
fdecl.write("\n#endif\n");
|
475 |
|
476 |
if not middle_mode: |
477 |
ret = gen_registry(commands) |
478 |
fdef.write(ret) |
479 |
|
480 |
fdef.flush() |
481 |
fdef.close() |
482 |
fdecl.flush() |
483 |
fdecl.close() |