]>
Commit | Line | Data |
---|---|---|
5ddeec83 MA |
1 | """ |
2 | QAPI command marshaller generator | |
3 | ||
4 | Copyright IBM, Corp. 2011 | |
5 | Copyright (C) 2014-2018 Red Hat, Inc. | |
6 | ||
7 | Authors: | |
8 | Anthony Liguori <[email protected]> | |
9 | Michael Roth <[email protected]> | |
10 | Markus Armbruster <[email protected]> | |
11 | ||
12 | This work is licensed under the terms of the GNU GPL, version 2. | |
13 | See the COPYING file in the top-level directory. | |
14 | """ | |
c17d9908 | 15 | |
fb0bc835 | 16 | from qapi.common import * |
e6c42b96 | 17 | from qapi.gen import QAPIGenCCode, QAPISchemaModularCVisitor, ifcontext |
c17d9908 | 18 | |
e98859a9 | 19 | |
48825ca4 | 20 | def gen_command_decl(name, arg_type, boxed, ret_type): |
c17d9908 | 21 | return mcgen(''' |
03b4367a | 22 | %(c_type)s qmp_%(c_name)s(%(params)s); |
c17d9908 | 23 | ''', |
e98859a9 MA |
24 | c_type=(ret_type and ret_type.c_type()) or 'void', |
25 | c_name=c_name(name), | |
086ee7a6 | 26 | params=build_params(arg_type, boxed, 'Error **errp')) |
e98859a9 | 27 | |
c17d9908 | 28 | |
48825ca4 | 29 | def gen_call(name, arg_type, boxed, ret_type): |
e98859a9 MA |
30 | ret = '' |
31 | ||
32 | argstr = '' | |
48825ca4 | 33 | if boxed: |
675b214b | 34 | assert arg_type |
c818408e | 35 | argstr = '&arg, ' |
48825ca4 | 36 | elif arg_type: |
29f6bd15 | 37 | assert not arg_type.variants |
e98859a9 | 38 | for memb in arg_type.members: |
ee446028 | 39 | if memb.optional: |
386230a2 EB |
40 | argstr += 'arg.has_%s, ' % c_name(memb.name) |
41 | argstr += 'arg.%s, ' % c_name(memb.name) | |
e98859a9 MA |
42 | |
43 | lhs = '' | |
44 | if ret_type: | |
45 | lhs = 'retval = ' | |
46 | ||
c17d9908 | 47 | ret = mcgen(''' |
f1538019 | 48 | |
05372f70 | 49 | %(lhs)sqmp_%(c_name)s(%(args)s&err); |
c17d9908 | 50 | ''', |
e98859a9 | 51 | c_name=c_name(name), args=argstr, lhs=lhs) |
c17d9908 | 52 | if ret_type: |
e02bca28 | 53 | ret += mcgen(''' |
fa274ed6 EB |
54 | if (err) { |
55 | goto out; | |
56 | } | |
e02bca28 | 57 | |
05372f70 | 58 | qmp_marshal_output_%(c_name)s(retval, ret, &err); |
c17d9908 | 59 | ''', |
56d92b00 | 60 | c_name=ret_type.c_name()) |
1f9a7a1a | 61 | return ret |
c17d9908 | 62 | |
e98859a9 | 63 | |
56d92b00 | 64 | def gen_marshal_output(ret_type): |
f1538019 | 65 | return mcgen(''' |
ee446028 | 66 | |
56d92b00 | 67 | static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) |
c17d9908 | 68 | { |
2a0f50e8 | 69 | Error *err = NULL; |
c17d9908 MR |
70 | Visitor *v; |
71 | ||
7d5e199a | 72 | v = qobject_output_visitor_new(ret_out); |
51e72bc1 | 73 | visit_type_%(c_name)s(v, "unused", &ret_in, &err); |
3b098d56 EB |
74 | if (!err) { |
75 | visit_complete(v, ret_out); | |
c17d9908 | 76 | } |
2a0f50e8 | 77 | error_propagate(errp, err); |
2c0ef9f4 EB |
78 | visit_free(v); |
79 | v = qapi_dealloc_visitor_new(); | |
51e72bc1 | 80 | visit_type_%(c_name)s(v, "unused", &ret_in, NULL); |
2c0ef9f4 | 81 | visit_free(v); |
c17d9908 MR |
82 | } |
83 | ''', | |
56d92b00 | 84 | c_type=ret_type.c_type(), c_name=ret_type.c_name()) |
c17d9908 | 85 | |
e98859a9 | 86 | |
086ee7a6 | 87 | def build_marshal_proto(name): |
c2613949 MA |
88 | return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' |
89 | % c_name(name)) | |
776574d6 | 90 | |
e98859a9 | 91 | |
f1538019 MA |
92 | def gen_marshal_decl(name): |
93 | return mcgen(''' | |
94 | %(proto)s; | |
95 | ''', | |
086ee7a6 | 96 | proto=build_marshal_proto(name)) |
f1538019 | 97 | |
776574d6 | 98 | |
48825ca4 | 99 | def gen_marshal(name, arg_type, boxed, ret_type): |
675b214b | 100 | have_args = boxed or (arg_type and not arg_type.is_empty()) |
a0067da1 | 101 | |
c17d9908 | 102 | ret = mcgen(''' |
ee446028 | 103 | |
f1538019 | 104 | %(proto)s |
c17d9908 | 105 | { |
c1ff0e6c | 106 | Error *err = NULL; |
c17d9908 | 107 | ''', |
086ee7a6 | 108 | proto=build_marshal_proto(name)) |
c17d9908 | 109 | |
c1ff0e6c EB |
110 | if ret_type: |
111 | ret += mcgen(''' | |
112 | %(c_type)s retval; | |
113 | ''', | |
114 | c_type=ret_type.c_type()) | |
115 | ||
a0067da1 MAL |
116 | if have_args: |
117 | visit_members = ('visit_type_%s_members(v, &arg, &err);' | |
118 | % arg_type.c_name()) | |
c1ff0e6c | 119 | ret += mcgen(''' |
c1ff0e6c EB |
120 | Visitor *v; |
121 | %(c_name)s arg = {0}; | |
122 | ||
a0067da1 MAL |
123 | ''', |
124 | c_name=arg_type.c_name()) | |
125 | else: | |
126 | visit_members = '' | |
127 | ret += mcgen(''' | |
128 | Visitor *v = NULL; | |
129 | ||
130 | if (args) { | |
131 | ''') | |
132 | push_indent() | |
133 | ||
134 | ret += mcgen(''' | |
048abb7b | 135 | v = qobject_input_visitor_new(QOBJECT(args)); |
ed841535 EB |
136 | visit_start_struct(v, NULL, NULL, 0, &err); |
137 | if (err) { | |
138 | goto out; | |
139 | } | |
a0067da1 | 140 | %(visit_members)s |
15c2f669 EB |
141 | if (!err) { |
142 | visit_check_struct(v, &err); | |
143 | } | |
1158bb2a | 144 | visit_end_struct(v, NULL); |
c1ff0e6c EB |
145 | if (err) { |
146 | goto out; | |
147 | } | |
148 | ''', | |
a0067da1 | 149 | visit_members=visit_members) |
c1ff0e6c | 150 | |
a0067da1 MAL |
151 | if not have_args: |
152 | pop_indent() | |
c1ff0e6c | 153 | ret += mcgen(''' |
a0067da1 | 154 | } |
c1ff0e6c EB |
155 | ''') |
156 | ||
48825ca4 | 157 | ret += gen_call(name, arg_type, boxed, ret_type) |
1f9a7a1a | 158 | |
a0067da1 | 159 | ret += mcgen(''' |
c17d9908 MR |
160 | |
161 | out: | |
2a0f50e8 | 162 | error_propagate(errp, err); |
a0067da1 | 163 | visit_free(v); |
1f9a7a1a | 164 | ''') |
a0067da1 MAL |
165 | |
166 | if have_args: | |
167 | visit_members = ('visit_type_%s_members(v, &arg, NULL);' | |
168 | % arg_type.c_name()) | |
169 | else: | |
170 | visit_members = '' | |
c1ff0e6c | 171 | ret += mcgen(''' |
a0067da1 MAL |
172 | if (args) { |
173 | ''') | |
174 | push_indent() | |
175 | ||
176 | ret += mcgen(''' | |
2c0ef9f4 | 177 | v = qapi_dealloc_visitor_new(); |
ed841535 | 178 | visit_start_struct(v, NULL, NULL, 0, NULL); |
a0067da1 | 179 | %(visit_members)s |
1158bb2a | 180 | visit_end_struct(v, NULL); |
2c0ef9f4 | 181 | visit_free(v); |
c1ff0e6c | 182 | ''', |
a0067da1 MAL |
183 | visit_members=visit_members) |
184 | ||
185 | if not have_args: | |
186 | pop_indent() | |
187 | ret += mcgen(''' | |
188 | } | |
189 | ''') | |
c1ff0e6c | 190 | |
1f9a7a1a | 191 | ret += mcgen(''' |
485febc6 | 192 | } |
1f9a7a1a | 193 | ''') |
c17d9908 MR |
194 | return ret |
195 | ||
e98859a9 | 196 | |
d6fe3d02 | 197 | def gen_register_command(name, success_response, allow_oob, allow_preconfig): |
876c6751 PX |
198 | options = [] |
199 | ||
ee446028 | 200 | if not success_response: |
876c6751 PX |
201 | options += ['QCO_NO_SUCCESS_RESP'] |
202 | if allow_oob: | |
203 | options += ['QCO_ALLOW_OOB'] | |
d6fe3d02 IM |
204 | if allow_preconfig: |
205 | options += ['QCO_ALLOW_PRECONFIG'] | |
876c6751 PX |
206 | |
207 | if not options: | |
208 | options = ['QCO_NO_OPTIONS'] | |
209 | ||
210 | options = " | ".join(options) | |
d34b867d | 211 | |
ee446028 | 212 | ret = mcgen(''' |
c2613949 | 213 | qmp_register_command(cmds, "%(name)s", |
1527badb | 214 | qmp_marshal_%(c_name)s, %(opts)s); |
c17d9908 | 215 | ''', |
e98859a9 MA |
216 | name=name, c_name=c_name(name), |
217 | opts=options) | |
ee446028 MA |
218 | return ret |
219 | ||
e98859a9 | 220 | |
93b564c4 | 221 | def gen_registry(registry, prefix): |
c17d9908 | 222 | ret = mcgen(''' |
ee446028 | 223 | |
1527badb | 224 | void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) |
c17d9908 | 225 | { |
1527badb MA |
226 | QTAILQ_INIT(cmds); |
227 | ||
228 | ''', | |
229 | c_prefix=c_name(prefix, protect=False)) | |
1f9a7a1a MA |
230 | ret += registry |
231 | ret += mcgen(''' | |
c17d9908 | 232 | } |
1f9a7a1a | 233 | ''') |
c17d9908 MR |
234 | return ret |
235 | ||
ee446028 | 236 | |
252dc310 | 237 | class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): |
71b3f045 | 238 | |
93b564c4 | 239 | def __init__(self, prefix): |
252dc310 MA |
240 | QAPISchemaModularCVisitor.__init__( |
241 | self, prefix, 'qapi-commands', | |
3bef3aae | 242 | ' * Schema-defined QAPI/QMP commands', None, __doc__) |
dddee4d7 | 243 | self._regy = QAPIGenCCode(None) |
252dc310 MA |
244 | self._visited_ret_types = {} |
245 | ||
dcac6471 | 246 | def _begin_user_module(self, name): |
252dc310 | 247 | self._visited_ret_types[self._genc] = set() |
9af23989 MA |
248 | commands = self._module_basename('qapi-commands', name) |
249 | types = self._module_basename('qapi-types', name) | |
250 | visit = self._module_basename('qapi-visit', name) | |
71b3f045 | 251 | self._genc.add(mcgen(''' |
9167ebd9 | 252 | #include "qemu/osdep.h" |
4180978c | 253 | #include "qapi/visitor.h" |
452fcdbc | 254 | #include "qapi/qmp/qdict.h" |
b3db211f DB |
255 | #include "qapi/qobject-output-visitor.h" |
256 | #include "qapi/qobject-input-visitor.h" | |
4180978c | 257 | #include "qapi/dealloc-visitor.h" |
e688df6b | 258 | #include "qapi/error.h" |
9af23989 MA |
259 | #include "%(visit)s.h" |
260 | #include "%(commands)s.h" | |
4180978c MA |
261 | |
262 | ''', | |
9af23989 | 263 | commands=commands, visit=visit)) |
71b3f045 | 264 | self._genh.add(mcgen(''' |
9af23989 | 265 | #include "%(types)s.h" |
4180978c MA |
266 | |
267 | ''', | |
9af23989 | 268 | types=types)) |
71b3f045 MA |
269 | |
270 | def visit_end(self): | |
00ca24ff MA |
271 | self._add_system_module('init', ' * QAPI Commands initialization') |
272 | self._genh.add(mcgen(''' | |
273 | #include "qapi/qmp/dispatch.h" | |
274 | ||
252dc310 MA |
275 | void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); |
276 | ''', | |
277 | c_prefix=c_name(self._prefix, protect=False))) | |
00ca24ff MA |
278 | self._genc.preamble_add(mcgen(''' |
279 | #include "qemu/osdep.h" | |
280 | #include "%(prefix)sqapi-commands.h" | |
281 | #include "%(prefix)sqapi-init-commands.h" | |
282 | ''', | |
283 | prefix=self._prefix)) | |
284 | self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) | |
71b3f045 | 285 | |
fbf09a2f | 286 | def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, |
23394b4c PK |
287 | success_response, boxed, allow_oob, allow_preconfig, |
288 | features): | |
71b3f045 MA |
289 | if not gen: |
290 | return | |
1f7b9f31 MAL |
291 | # FIXME: If T is a user-defined type, the user is responsible |
292 | # for making this work, i.e. to make T's condition the | |
293 | # conjunction of the T-returning commands' conditions. If T | |
294 | # is a built-in type, this isn't possible: the | |
295 | # qmp_marshal_output_T() will be generated unconditionally. | |
252dc310 MA |
296 | if ret_type and ret_type not in self._visited_ret_types[self._genc]: |
297 | self._visited_ret_types[self._genc].add(ret_type) | |
1f7b9f31 MAL |
298 | with ifcontext(ret_type.ifcond, |
299 | self._genh, self._genc, self._regy): | |
300 | self._genc.add(gen_marshal_output(ret_type)) | |
301 | with ifcontext(ifcond, self._genh, self._genc, self._regy): | |
302 | self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) | |
303 | self._genh.add(gen_marshal_decl(name)) | |
304 | self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) | |
305 | self._regy.add(gen_register_command(name, success_response, | |
306 | allow_oob, allow_preconfig)) | |
71b3f045 | 307 | |
26df4e7f | 308 | |
71b3f045 | 309 | def gen_commands(schema, output_dir, prefix): |
26df4e7f MA |
310 | vis = QAPISchemaGenCommandVisitor(prefix) |
311 | schema.visit(vis) | |
71b3f045 | 312 | vis.write(output_dir) |