2 QAPI command marshaller generator
4 Copyright IBM, Corp. 2011
5 Copyright (C) 2014-2018 Red Hat, Inc.
8 Anthony Liguori <aliguori@us.ibm.com>
9 Michael Roth <mdroth@linux.vnet.ibm.com>
10 Markus Armbruster <armbru@redhat.com>
12 This work is licensed under the terms of the GNU GPL, version 2.
13 See the COPYING file in the top-level directory.
24 from .common
import c_name
, mcgen
27 QAPISchemaModularCVisitor
,
37 from .source
import QAPISourceInfo
40 def gen_command_decl(name
: str,
41 arg_type
: Optional
[QAPISchemaObjectType
],
43 ret_type
: Optional
[QAPISchemaType
]) -> str:
45 %(c_type)s qmp_%(c_name)s(%(params)s);
47 c_type
=(ret_type
and ret_type
.c_type()) or 'void',
49 params
=build_params(arg_type
, boxed
, 'Error **errp'))
52 def gen_call(name
: str,
53 arg_type
: Optional
[QAPISchemaObjectType
],
55 ret_type
: Optional
[QAPISchemaType
]) -> str:
63 assert not arg_type
.variants
64 for memb
in arg_type
.members
:
66 argstr
+= 'arg.has_%s, ' % c_name(memb
.name
)
67 argstr
+= 'arg.%s, ' % c_name(memb
.name
)
75 %(lhs)sqmp_%(c_name)s(%(args)s&err);
76 error_propagate(errp, err);
78 c_name
=c_name(name
), args
=argstr
, lhs
=lhs
)
85 qmp_marshal_output_%(c_name)s(retval, ret, errp);
87 c_name
=ret_type
.c_name())
91 def gen_marshal_output(ret_type
: QAPISchemaType
) -> str:
94 static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
95 QObject **ret_out, Error **errp)
99 v = qobject_output_visitor_new_qmp(ret_out);
100 if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
101 visit_complete(v, ret_out);
104 v = qapi_dealloc_visitor_new();
105 visit_type_%(c_name)s(v, "unused", &ret_in, NULL);
109 c_type
=ret_type
.c_type(), c_name
=ret_type
.c_name())
112 def build_marshal_proto(name
: str) -> str:
113 return ('void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)'
117 def gen_marshal_decl(name
: str) -> str:
121 proto
=build_marshal_proto(name
))
124 def gen_marshal(name
: str,
125 arg_type
: Optional
[QAPISchemaObjectType
],
127 ret_type
: Optional
[QAPISchemaType
]) -> str:
128 have_args
= boxed
or (arg_type
and not arg_type
.is_empty())
130 assert arg_type
is not None
131 arg_type_c_name
= arg_type
.c_name()
141 proto
=build_marshal_proto(name
))
147 c_type
=ret_type
.c_type())
151 %(c_name)s arg = {0};
153 c_name
=arg_type_c_name
)
157 v = qobject_input_visitor_new_qmp(QOBJECT(args));
158 if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
165 if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) {
166 ok = visit_check_struct(v, errp);
169 c_arg_type
=arg_type_c_name
)
172 ok = visit_check_struct(v, errp);
176 visit_end_struct(v, NULL);
182 ret
+= gen_call(name
, arg_type
, boxed
, ret_type
)
191 v = qapi_dealloc_visitor_new();
192 visit_start_struct(v, NULL, NULL, 0, NULL);
197 visit_type_%(c_arg_type)s_members(v, &arg, NULL);
199 c_arg_type
=arg_type_c_name
)
202 visit_end_struct(v, NULL);
212 def gen_register_command(name
: str,
213 features
: List
[QAPISchemaFeature
],
214 success_response
: bool,
216 allow_preconfig
: bool,
217 coroutine
: bool) -> str:
220 if 'deprecated' in [f
.name
for f
in features
]:
221 options
+= ['QCO_DEPRECATED']
223 if not success_response
:
224 options
+= ['QCO_NO_SUCCESS_RESP']
226 options
+= ['QCO_ALLOW_OOB']
228 options
+= ['QCO_ALLOW_PRECONFIG']
230 options
+= ['QCO_COROUTINE']
233 options
= ['QCO_NO_OPTIONS']
236 qmp_register_command(cmds, "%(name)s",
237 qmp_marshal_%(c_name)s, %(opts)s);
239 name
=name
, c_name
=c_name(name
),
240 opts
=" | ".join(options
))
244 class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor
):
245 def __init__(self
, prefix
: str):
247 prefix
, 'qapi-commands',
248 ' * Schema-defined QAPI/QMP commands', None, __doc__
)
249 self
._visited
_ret
_types
: Dict
[QAPIGenC
, Set
[QAPISchemaType
]] = {}
251 def _begin_user_module(self
, name
: str) -> None:
252 self
._visited
_ret
_types
[self
._genc
] = set()
253 commands
= self
._module
_basename
('qapi-commands', name
)
254 types
= self
._module
_basename
('qapi-types', name
)
255 visit
= self
._module
_basename
('qapi-visit', name
)
256 self
._genc
.add(mcgen('''
257 #include "qemu/osdep.h"
258 #include "qapi/compat-policy.h"
259 #include "qapi/visitor.h"
260 #include "qapi/qmp/qdict.h"
261 #include "qapi/dealloc-visitor.h"
262 #include "qapi/error.h"
263 #include "%(visit)s.h"
264 #include "%(commands)s.h"
267 commands
=commands
, visit
=visit
))
268 self
._genh
.add(mcgen('''
269 #include "%(types)s.h"
274 def visit_begin(self
, schema
: QAPISchema
) -> None:
275 self
._add
_module
('./init', ' * QAPI Commands initialization')
276 self
._genh
.add(mcgen('''
277 #include "qapi/qmp/dispatch.h"
279 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
281 c_prefix
=c_name(self
._prefix
, protect
=False)))
282 self
._genc
.add(mcgen('''
283 #include "qemu/osdep.h"
284 #include "%(prefix)sqapi-commands.h"
285 #include "%(prefix)sqapi-init-commands.h"
287 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
293 c_prefix
=c_name(self
._prefix
, protect
=False)))
295 def visit_end(self
) -> None:
296 with self
._temp
_module
('./init'):
297 self
._genc
.add(mcgen('''
301 def visit_command(self
,
303 info
: Optional
[QAPISourceInfo
],
304 ifcond
: Sequence
[str],
305 features
: List
[QAPISchemaFeature
],
306 arg_type
: Optional
[QAPISchemaObjectType
],
307 ret_type
: Optional
[QAPISchemaType
],
309 success_response
: bool,
312 allow_preconfig
: bool,
313 coroutine
: bool) -> None:
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.
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
)
323 with
ifcontext(ret_type
.ifcond
,
324 self
._genh
, self
._genc
):
325 self
._genc
.add(gen_marshal_output(ret_type
))
326 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
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
))
330 with self
._temp
_module
('./init'):
331 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
332 self
._genc
.add(gen_register_command(
333 name
, features
, success_response
, allow_oob
,
334 allow_preconfig
, coroutine
))
337 def gen_commands(schema
: QAPISchema
,
339 prefix
: str) -> None:
340 vis
= QAPISchemaGenCommandVisitor(prefix
)
342 vis
.write(output_dir
)