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