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(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/qobject-input-visitor.h"
262 #include "qapi/dealloc-visitor.h"
263 #include "qapi/error.h"
264 #include "%(visit)s.h"
265 #include "%(commands)s.h"
268 commands
=commands
, visit
=visit
))
269 self
._genh
.add(mcgen('''
270 #include "%(types)s.h"
275 def visit_begin(self
, schema
: QAPISchema
) -> None:
276 self
._add
_module
('./init', ' * QAPI Commands initialization')
277 self
._genh
.add(mcgen('''
278 #include "qapi/qmp/dispatch.h"
280 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
282 c_prefix
=c_name(self
._prefix
, protect
=False)))
283 self
._genc
.add(mcgen('''
284 #include "qemu/osdep.h"
285 #include "%(prefix)sqapi-commands.h"
286 #include "%(prefix)sqapi-init-commands.h"
288 void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
294 c_prefix
=c_name(self
._prefix
, protect
=False)))
296 def visit_end(self
) -> None:
297 with self
._temp
_module
('./init'):
298 self
._genc
.add(mcgen('''
302 def visit_command(self
,
304 info
: Optional
[QAPISourceInfo
],
305 ifcond
: Sequence
[str],
306 features
: List
[QAPISchemaFeature
],
307 arg_type
: Optional
[QAPISchemaObjectType
],
308 ret_type
: Optional
[QAPISchemaType
],
310 success_response
: bool,
313 allow_preconfig
: bool,
314 coroutine
: bool) -> None:
317 # FIXME: If T is a user-defined type, the user is responsible
318 # for making this work, i.e. to make T's condition the
319 # conjunction of the T-returning commands' conditions. If T
320 # is a built-in type, this isn't possible: the
321 # qmp_marshal_output_T() will be generated unconditionally.
322 if ret_type
and ret_type
not in self
._visited
_ret
_types
[self
._genc
]:
323 self
._visited
_ret
_types
[self
._genc
].add(ret_type
)
324 with
ifcontext(ret_type
.ifcond
,
325 self
._genh
, self
._genc
):
326 self
._genc
.add(gen_marshal_output(ret_type
))
327 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
328 self
._genh
.add(gen_command_decl(name
, arg_type
, boxed
, ret_type
))
329 self
._genh
.add(gen_marshal_decl(name
))
330 self
._genc
.add(gen_marshal(name
, arg_type
, boxed
, ret_type
))
331 with self
._temp
_module
('./init'):
332 with
ifcontext(ifcond
, self
._genh
, self
._genc
):
333 self
._genc
.add(gen_register_command(
334 name
, features
, success_response
, allow_oob
,
335 allow_preconfig
, coroutine
))
338 def gen_commands(schema
: QAPISchema
,
340 prefix
: str) -> None:
341 vis
= QAPISchemaGenCommandVisitor(prefix
)
343 vis
.write(output_dir
)