]>
Commit | Line | Data |
---|---|---|
c17d9908 MR |
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 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 | ||
776574d6 | 170 | def gen_marshal_output(name, args, ret_type, middle_mode): |
c17d9908 MR |
171 | if not ret_type: |
172 | return "" | |
776574d6 | 173 | |
c17d9908 MR |
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 | ''', | |
776574d6 AL |
192 | c_ret_type=c_type(ret_type), c_name=c_var(name), |
193 | ret_type=ret_type) | |
c17d9908 MR |
194 | |
195 | return ret | |
196 | ||
776574d6 AL |
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 | ||
c17d9908 | 208 | ret = mcgen(''' |
776574d6 | 209 | %(header)s |
c17d9908 MR |
210 | { |
211 | ''', | |
776574d6 AL |
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 | ''') | |
c17d9908 MR |
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)")) | |
776574d6 AL |
242 | else: |
243 | ret += mcgen(''' | |
244 | (void)args; | |
245 | ''') | |
c17d9908 MR |
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 | |
776574d6 AL |
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(''' | |
c17d9908 | 276 | return; |
776574d6 AL |
277 | ''') |
278 | ||
279 | ret += mcgen(''' | |
c17d9908 | 280 | } |
776574d6 AL |
281 | ''') |
282 | ||
c17d9908 MR |
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 <aliguori@us.ibm.com> | |
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 | ''', | |
776574d6 | 329 | header=basename(header), guard=guardname(header), prefix=prefix) |
c17d9908 MR |
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 <aliguori@us.ibm.com> | |
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 | |
776574d6 | 362 | return ret + "\n\n" |
c17d9908 MR |
363 | |
364 | ||
365 | try: | |
776574d6 | 366 | opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:m", ["prefix=", "output-dir=", "type=", "middle"]) |
c17d9908 MR |
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' | |
776574d6 | 376 | middle_mode = False |
c17d9908 MR |
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 | |
776574d6 AL |
385 | elif o in ("-m", "--middle"): |
386 | middle_mode = True | |
c17d9908 MR |
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: | |
776574d6 | 418 | ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" |
c17d9908 | 419 | fdef.write(ret) |
776574d6 AL |
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" | |
c17d9908 MR |
425 | fdef.write(ret) |
426 | ||
7534ba01 | 427 | fdecl.write("\n#endif\n"); |
776574d6 AL |
428 | |
429 | if not middle_mode: | |
430 | ret = gen_registry(commands) | |
431 | fdef.write(ret) | |
c17d9908 MR |
432 | |
433 | fdef.flush() | |
434 | fdef.close() | |
435 | fdecl.flush() | |
436 | fdecl.close() |