]>
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 | ||
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): | |
171 | if not ret_type: | |
172 | return "" | |
173 | ret = mcgen(''' | |
174 | static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) | |
175 | { | |
176 | QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); | |
177 | QmpOutputVisitor *mo = qmp_output_visitor_new(); | |
178 | Visitor *v; | |
179 | ||
180 | v = qmp_output_get_visitor(mo); | |
181 | visit_type_%(ret_type)s(v, &ret_in, "unused", errp); | |
182 | if (!error_is_set(errp)) { | |
183 | *ret_out = qmp_output_get_qobject(mo); | |
184 | } | |
185 | qmp_output_visitor_cleanup(mo); | |
186 | v = qapi_dealloc_get_visitor(md); | |
187 | visit_type_%(ret_type)s(v, &ret_in, "unused", errp); | |
188 | qapi_dealloc_visitor_cleanup(md); | |
189 | } | |
190 | ''', | |
191 | c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type) | |
192 | ||
193 | return ret | |
194 | ||
195 | def gen_marshal_input(name, args, ret_type): | |
196 | ret = mcgen(''' | |
197 | static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp) | |
198 | { | |
199 | ''', | |
200 | c_name=c_var(name)) | |
201 | ||
202 | if ret_type: | |
203 | if c_type(ret_type).endswith("*"): | |
204 | retval = " %s retval = NULL;" % c_type(ret_type) | |
205 | else: | |
206 | retval = " %s retval;" % c_type(ret_type) | |
207 | ret += mcgen(''' | |
208 | %(retval)s | |
209 | ''', | |
210 | retval=retval) | |
211 | ||
212 | if len(args) > 0: | |
213 | ret += mcgen(''' | |
214 | %(visitor_input_containers_decl)s | |
215 | %(visitor_input_vars_decl)s | |
216 | ||
217 | %(visitor_input_block)s | |
218 | ||
219 | ''', | |
220 | visitor_input_containers_decl=gen_visitor_input_containers_decl(args), | |
221 | visitor_input_vars_decl=gen_visitor_input_vars_decl(args), | |
222 | visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) | |
223 | ||
224 | ret += mcgen(''' | |
225 | if (error_is_set(errp)) { | |
226 | goto out; | |
227 | } | |
228 | %(sync_call)s | |
229 | ''', | |
230 | sync_call=gen_sync_call(name, args, ret_type, indent=4)) | |
231 | ret += mcgen(''' | |
232 | ||
233 | out: | |
234 | ''') | |
235 | ret += mcgen(''' | |
236 | %(visitor_input_block_cleanup)s | |
237 | return; | |
238 | } | |
239 | ''', | |
240 | visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True)) | |
241 | return ret | |
242 | ||
243 | def gen_registry(commands): | |
244 | registry="" | |
245 | push_indent() | |
246 | for cmd in commands: | |
247 | registry += mcgen(''' | |
248 | qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s); | |
249 | ''', | |
250 | name=cmd['command'], c_name=c_var(cmd['command'])) | |
251 | pop_indent() | |
252 | ret = mcgen(''' | |
253 | static void qmp_init_marshal(void) | |
254 | { | |
255 | %(registry)s | |
256 | } | |
257 | ||
258 | qapi_init(qmp_init_marshal); | |
259 | ''', | |
260 | registry=registry.rstrip()) | |
261 | return ret | |
262 | ||
263 | def gen_command_decl_prologue(header, guard, prefix=""): | |
264 | ret = mcgen(''' | |
265 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
266 | ||
267 | /* | |
268 | * schema-defined QAPI function prototypes | |
269 | * | |
270 | * Copyright IBM, Corp. 2011 | |
271 | * | |
272 | * Authors: | |
273 | * Anthony Liguori <[email protected]> | |
274 | * | |
275 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
276 | * See the COPYING.LIB file in the top-level directory. | |
277 | * | |
278 | */ | |
279 | ||
280 | #ifndef %(guard)s | |
281 | #define %(guard)s | |
282 | ||
283 | #include "%(prefix)sqapi-types.h" | |
284 | #include "error.h" | |
285 | ||
286 | ''', | |
287 | header=basename(h_file), guard=guardname(h_file), prefix=prefix) | |
288 | return ret | |
289 | ||
290 | def gen_command_def_prologue(prefix="", proxy=False): | |
291 | ret = mcgen(''' | |
292 | /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ | |
293 | ||
294 | /* | |
295 | * schema-defined QMP->QAPI command dispatch | |
296 | * | |
297 | * Copyright IBM, Corp. 2011 | |
298 | * | |
299 | * Authors: | |
300 | * Anthony Liguori <[email protected]> | |
301 | * | |
302 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
303 | * See the COPYING.LIB file in the top-level directory. | |
304 | * | |
305 | */ | |
306 | ||
307 | #include "qemu-objects.h" | |
308 | #include "qapi/qmp-core.h" | |
309 | #include "qapi/qapi-visit-core.h" | |
310 | #include "qapi/qmp-output-visitor.h" | |
311 | #include "qapi/qmp-input-visitor.h" | |
312 | #include "qapi/qapi-dealloc-visitor.h" | |
313 | #include "%(prefix)sqapi-types.h" | |
314 | #include "%(prefix)sqapi-visit.h" | |
315 | ||
316 | ''', | |
317 | prefix=prefix) | |
318 | if not proxy: | |
319 | ret += '#include "%sqmp-commands.h"' % prefix | |
320 | return ret + "\n" | |
321 | ||
322 | ||
323 | try: | |
324 | opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="]) | |
325 | except getopt.GetoptError, err: | |
326 | print str(err) | |
327 | sys.exit(1) | |
328 | ||
329 | output_dir = "" | |
330 | prefix = "" | |
331 | dispatch_type = "sync" | |
332 | c_file = 'qmp-marshal.c' | |
333 | h_file = 'qmp-commands.h' | |
334 | ||
335 | for o, a in opts: | |
336 | if o in ("-p", "--prefix"): | |
337 | prefix = a | |
338 | elif o in ("-o", "--output-dir"): | |
339 | output_dir = a + "/" | |
340 | elif o in ("-t", "--type"): | |
341 | dispatch_type = a | |
342 | ||
343 | c_file = output_dir + prefix + c_file | |
344 | h_file = output_dir + prefix + h_file | |
345 | ||
346 | try: | |
347 | os.makedirs(output_dir) | |
348 | except os.error, e: | |
349 | if e.errno != errno.EEXIST: | |
350 | raise | |
351 | ||
352 | exprs = parse_schema(sys.stdin) | |
353 | commands = filter(lambda expr: expr.has_key('command'), exprs) | |
354 | ||
355 | if dispatch_type == "sync": | |
356 | fdecl = open(h_file, 'w') | |
357 | fdef = open(c_file, 'w') | |
358 | ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) | |
359 | fdecl.write(ret) | |
360 | ret = gen_command_def_prologue(prefix=prefix) | |
361 | fdef.write(ret) | |
362 | ||
363 | for cmd in commands: | |
364 | arglist = [] | |
365 | ret_type = None | |
366 | if cmd.has_key('data'): | |
367 | arglist = cmd['data'] | |
368 | if cmd.has_key('returns'): | |
369 | ret_type = cmd['returns'] | |
370 | ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" | |
371 | fdecl.write(ret) | |
372 | if ret_type: | |
373 | ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n" | |
374 | fdef.write(ret) | |
375 | ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n" | |
376 | fdef.write(ret) | |
377 | ||
7534ba01 | 378 | fdecl.write("\n#endif\n"); |
c17d9908 MR |
379 | ret = gen_registry(commands) |
380 | fdef.write(ret) | |
381 | ||
382 | fdef.flush() | |
383 | fdef.close() | |
384 | fdecl.flush() | |
385 | fdecl.close() |