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