QAPI patches for 2018-07-03

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbO+InAAoJEDhwtADrkYZTtsQQAI2IEFWwsIWtjh+JO2xWHjTC
 sXrzD2m8g50xledX1mKgQdA9NujvrSreQBFEgXvOEz65K8hl1HIqNp9C4NeOPUGn
 vVxDGRrMz1rGRrBXXM9mf2v3ZSMw0mVAmpBzQ2lO/fPsGEAThJ4451c0mHiMhBXi
 Ez2v3Qod597ITWCG73DccCN7ehl+3HCpqAyvBjVjtolmnt0h4oefqb8OV1l59kl3
 dywlnEoMcLq80uznAkkCnQ89yZR6ygrZIYfyMynpkTo4quf/ZB8eHF8Au4Jyynby
 1cAPAs0tfjmfvSpSEGvWyhmuu8jKm9pGnby7Me18HV/dkyNWzQWWMpzWpYHxiu41
 fa0SpBpw4g112SaeNOOt0g+SDpcdtvTheWmyPbzSL1yTDJ2NTppbl2zpNo9BHY3X
 jFSXpNi74RTB4KDXx+t/QXJepo3f35dTOOoOvodhT4EHzTIkPQoalKbKZij6zDHo
 CoBRrJQ8WQIxaYuiYHCDdRxTBbq44XbW30rWDnjmUBcLW6uMuUOiZxUh+n2GQjP7
 dEZel23zslqPdFexnTX32miIfkiHABKWIr7EzZ4NC8P/0U8McKD/lxGP8IHrs9Lo
 D8UYgfOGse2w5MWXCxSwq+VY5aFPasYrpVpi31gnCzLtJ9Q8qBMoqIGdyFKHzxXj
 qTvkP1iSHnXjSsq0KryZ
 =uZ84
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2018-07-03' into staging

QAPI patches for 2018-07-03

# gpg: Signature made Tue 03 Jul 2018 21:52:55 BST
# gpg:                using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2018-07-03:
  qapi: add conditions to SPICE type/commands/events on the schema
  qapi: add conditions to VNC type/commands/events on the schema
  qapi: add 'If:' section to generated documentation
  qapi-types: add #if conditions to types & visitors
  qapi/events: add #if conditions to events
  qapi/commands: add #if conditions to commands
  qapi-introspect: add preprocessor conditions to generated QLit
  qapi-introspect: modify to_qlit() to append ',' on level > 0
  qapi: add #if/#endif helpers
  qapi: mcgen() shouldn't indent # lines
  qapi: add 'ifcond' to visitor methods
  qapi: leave the ifcond attribute undefined until check()
  qapi: pass 'if' condition into QAPISchemaEntity objects
  qapi: add 'if' to top-level expressions

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-07-05 10:31:36 +01:00
commit 5dafaf4fbc
39 changed files with 561 additions and 225 deletions

View File

@ -744,6 +744,35 @@ Example: Red Hat, Inc. controls redhat.com, and may therefore add a
downstream command __com.redhat_drive-mirror.
=== Configuring the schema ===
The 'struct', 'enum', 'union', 'alternate', 'command' and 'event'
top-level expressions can take an 'if' key. Its value must be a string
or a list of strings. A string is shorthand for a list containing just
that string. The code generated for the top-level expression will then
be guarded by #if COND for each COND in the list.
Example: a conditional struct
{ 'struct': 'IfStruct', 'data': { 'foo': 'int' },
'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] }
gets its generated code guarded like this:
#if defined(CONFIG_FOO)
#if defined(HAVE_BAR)
... generated code ...
#endif /* defined(HAVE_BAR) */
#endif /* defined(CONFIG_FOO) */
Please note that you are responsible to ensure that the C code will
compile with an arbitrary combination of conditions, since the
generators are unable to check it at this point.
The presence of 'if' keys in the schema is reflected through to the
introspection output depending on the build configuration.
== Client JSON Protocol introspection ==
Clients of a Client JSON Protocol commonly need to figure out what

View File

@ -426,6 +426,7 @@ STEXI
Show which guest mouse is receiving events.
ETEXI
#if defined(CONFIG_VNC)
{
.name = "vnc",
.args_type = "",
@ -433,6 +434,7 @@ ETEXI
.help = "show the vnc server status",
.cmd = hmp_info_vnc,
},
#endif
STEXI
@item info vnc

9
hmp.c
View File

@ -616,6 +616,7 @@ void hmp_info_blockstats(Monitor *mon, const QDict *qdict)
qapi_free_BlockStatsList(stats_list);
}
#ifdef CONFIG_VNC
/* Helper for hmp_info_vnc_clients, _servers */
static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
const char *name)
@ -703,6 +704,7 @@ void hmp_info_vnc(Monitor *mon, const QDict *qdict)
qapi_free_VncInfo2List(info2l);
}
#endif
#ifdef CONFIG_SPICE
void hmp_info_spice(Monitor *mon, const QDict *qdict)
@ -1772,12 +1774,14 @@ void hmp_eject(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, &err);
}
#ifdef CONFIG_VNC
static void hmp_change_read_arg(void *opaque, const char *password,
void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
monitor_read_command(opaque, 1);
}
#endif
void hmp_change(Monitor *mon, const QDict *qdict)
{
@ -1788,6 +1792,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
BlockdevChangeReadOnlyMode read_only_mode = 0;
Error *err = NULL;
#ifdef CONFIG_VNC
if (strcmp(device, "vnc") == 0) {
if (read_only) {
monitor_printf(mon,
@ -1802,7 +1807,9 @@ void hmp_change(Monitor *mon, const QDict *qdict)
}
}
qmp_change("vnc", target, !!arg, arg, &err);
} else {
} else
#endif
{
if (read_only) {
read_only_mode =
qapi_enum_parse(&BlockdevChangeReadOnlyMode_lookup,

View File

@ -1191,9 +1191,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
*/
static void qmp_unregister_commands_hack(void)
{
#ifndef CONFIG_SPICE
qmp_unregister_command(&qmp_commands, "query-spice");
#endif
#ifndef CONFIG_REPLICATION
qmp_unregister_command(&qmp_commands, "xen-set-replication");
qmp_unregister_command(&qmp_commands, "query-xen-replication-status");

View File

@ -320,6 +320,7 @@
##
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' },
'base': 'ChardevCommon' }
# TODO: 'if': 'defined(CONFIG_SPICE)'
##
# @ChardevSpicePort:
@ -332,6 +333,7 @@
##
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' },
'base': 'ChardevCommon' }
# TODO: 'if': 'defined(CONFIG_SPICE)'
##
# @ChardevVC:
@ -385,8 +387,10 @@
'testdev': 'ChardevCommon',
'stdio' : 'ChardevStdio',
'console': 'ChardevCommon',
'spicevmc' : 'ChardevSpiceChannel',
'spiceport' : 'ChardevSpicePort',
'spicevmc': 'ChardevSpiceChannel',
# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
'spiceport': 'ChardevSpicePort',
# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
'vc' : 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility

View File

@ -118,7 +118,8 @@
{ 'struct': 'SpiceBasicInfo',
'data': { 'host': 'str',
'port': 'str',
'family': 'NetworkAddressFamily' } }
'family': 'NetworkAddressFamily' },
'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceServerInfo:
@ -131,7 +132,8 @@
##
{ 'struct': 'SpiceServerInfo',
'base': 'SpiceBasicInfo',
'data': { '*auth': 'str' } }
'data': { '*auth': 'str' },
'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceChannel:
@ -156,7 +158,8 @@
{ 'struct': 'SpiceChannel',
'base': 'SpiceBasicInfo',
'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
'tls': 'bool'} }
'tls': 'bool'},
'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceQueryMouseMode:
@ -175,7 +178,8 @@
# Since: 1.1
##
{ 'enum': 'SpiceQueryMouseMode',
'data': [ 'client', 'server', 'unknown' ] }
'data': [ 'client', 'server', 'unknown' ],
'if': 'defined(CONFIG_SPICE)' }
##
# @SpiceInfo:
@ -212,7 +216,8 @@
{ 'struct': 'SpiceInfo',
'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
'*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']},
'if': 'defined(CONFIG_SPICE)' }
##
# @query-spice:
@ -257,7 +262,8 @@
# }
#
##
{ 'command': 'query-spice', 'returns': 'SpiceInfo' }
{ 'command': 'query-spice', 'returns': 'SpiceInfo',
'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_CONNECTED:
@ -282,7 +288,8 @@
##
{ 'event': 'SPICE_CONNECTED',
'data': { 'server': 'SpiceBasicInfo',
'client': 'SpiceBasicInfo' } }
'client': 'SpiceBasicInfo' },
'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_INITIALIZED:
@ -310,7 +317,8 @@
##
{ 'event': 'SPICE_INITIALIZED',
'data': { 'server': 'SpiceServerInfo',
'client': 'SpiceChannel' } }
'client': 'SpiceChannel' },
'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_DISCONNECTED:
@ -335,7 +343,8 @@
##
{ 'event': 'SPICE_DISCONNECTED',
'data': { 'server': 'SpiceBasicInfo',
'client': 'SpiceBasicInfo' } }
'client': 'SpiceBasicInfo' },
'if': 'defined(CONFIG_SPICE)' }
##
# @SPICE_MIGRATE_COMPLETED:
@ -350,7 +359,8 @@
# "event": "SPICE_MIGRATE_COMPLETED" }
#
##
{ 'event': 'SPICE_MIGRATE_COMPLETED' }
{ 'event': 'SPICE_MIGRATE_COMPLETED',
'if': 'defined(CONFIG_SPICE)' }
##
# == VNC
@ -377,7 +387,8 @@
'data': { 'host': 'str',
'service': 'str',
'family': 'NetworkAddressFamily',
'websocket': 'bool' } }
'websocket': 'bool' },
'if': 'defined(CONFIG_VNC)' }
##
# @VncServerInfo:
@ -391,7 +402,8 @@
##
{ 'struct': 'VncServerInfo',
'base': 'VncBasicInfo',
'data': { '*auth': 'str' } }
'data': { '*auth': 'str' },
'if': 'defined(CONFIG_VNC)' }
##
# @VncClientInfo:
@ -408,7 +420,8 @@
##
{ 'struct': 'VncClientInfo',
'base': 'VncBasicInfo',
'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
'data': { '*x509_dname': 'str', '*sasl_username': 'str' },
'if': 'defined(CONFIG_VNC)' }
##
# @VncInfo:
@ -449,7 +462,8 @@
{ 'struct': 'VncInfo',
'data': {'enabled': 'bool', '*host': 'str',
'*family': 'NetworkAddressFamily',
'*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
'*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']},
'if': 'defined(CONFIG_VNC)' }
##
# @VncPrimaryAuth:
@ -460,7 +474,8 @@
##
{ 'enum': 'VncPrimaryAuth',
'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra',
'tls', 'vencrypt', 'sasl' ] }
'tls', 'vencrypt', 'sasl' ],
'if': 'defined(CONFIG_VNC)' }
##
# @VncVencryptSubAuth:
@ -474,8 +489,8 @@
'tls-none', 'x509-none',
'tls-vnc', 'x509-vnc',
'tls-plain', 'x509-plain',
'tls-sasl', 'x509-sasl' ] }
'tls-sasl', 'x509-sasl' ],
'if': 'defined(CONFIG_VNC)' }
##
# @VncServerInfo2:
@ -492,8 +507,8 @@
{ 'struct': 'VncServerInfo2',
'base': 'VncBasicInfo',
'data': { 'auth' : 'VncPrimaryAuth',
'*vencrypt' : 'VncVencryptSubAuth' } }
'*vencrypt' : 'VncVencryptSubAuth' },
'if': 'defined(CONFIG_VNC)' }
##
# @VncInfo2:
@ -525,7 +540,8 @@
'clients' : ['VncClientInfo'],
'auth' : 'VncPrimaryAuth',
'*vencrypt' : 'VncVencryptSubAuth',
'*display' : 'str' } }
'*display' : 'str' },
'if': 'defined(CONFIG_VNC)' }
##
# @query-vnc:
@ -556,8 +572,8 @@
# }
#
##
{ 'command': 'query-vnc', 'returns': 'VncInfo' }
{ 'command': 'query-vnc', 'returns': 'VncInfo',
'if': 'defined(CONFIG_VNC)' }
##
# @query-vnc-servers:
#
@ -567,7 +583,8 @@
#
# Since: 2.3
##
{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'] }
{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'],
'if': 'defined(CONFIG_VNC)' }
##
# @change-vnc-password:
@ -581,7 +598,8 @@
# Notes: An empty password in this command will set the password to the empty
# string. Existing clients are unaffected by executing this command.
##
{ 'command': 'change-vnc-password', 'data': {'password': 'str'} }
{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
'if': 'defined(CONFIG_VNC)' }
##
# @VNC_CONNECTED:
@ -610,7 +628,8 @@
##
{ 'event': 'VNC_CONNECTED',
'data': { 'server': 'VncServerInfo',
'client': 'VncBasicInfo' } }
'client': 'VncBasicInfo' },
'if': 'defined(CONFIG_VNC)' }
##
# @VNC_INITIALIZED:
@ -637,7 +656,8 @@
##
{ 'event': 'VNC_INITIALIZED',
'data': { 'server': 'VncServerInfo',
'client': 'VncClientInfo' } }
'client': 'VncClientInfo' },
'if': 'defined(CONFIG_VNC)' }
##
# @VNC_DISCONNECTED:
@ -663,7 +683,8 @@
##
{ 'event': 'VNC_DISCONNECTED',
'data': { 'server': 'VncServerInfo',
'client': 'VncClientInfo' } }
'client': 'VncClientInfo' },
'if': 'defined(CONFIG_VNC)' }
##
# = Input

46
qmp.c
View File

@ -129,38 +129,6 @@ void qmp_cpu_add(int64_t id, Error **errp)
}
}
#ifndef CONFIG_VNC
/* If VNC support is enabled, the "true" query-vnc command is
defined in the VNC subsystem */
VncInfo *qmp_query_vnc(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
return NULL;
};
VncInfo2List *qmp_query_vnc_servers(Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
return NULL;
};
#endif
#ifndef CONFIG_SPICE
/*
* qmp_unregister_commands_hack() ensures that QMP command query-spice
* exists only #ifdef CONFIG_SPICE. Necessary for an accurate
* query-commands result. However, the QAPI schema is blissfully
* unaware of that, and the QAPI code generator happily generates a
* dead qmp_marshal_query_spice() that calls qmp_query_spice().
* Provide it one, or else linking fails. FIXME Educate the QAPI
* schema on CONFIG_SPICE.
*/
SpiceInfo *qmp_query_spice(Error **errp)
{
abort();
};
#endif
void qmp_exit_preconfig(Error **errp)
{
if (!runstate_check(RUN_STATE_PRECONFIG)) {
@ -412,23 +380,17 @@ static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
qmp_change_vnc_listen(target, errp);
}
}
#else
void qmp_change_vnc_password(const char *password, Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
}
static void qmp_change_vnc(const char *target, bool has_arg, const char *arg,
Error **errp)
{
error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
}
#endif /* !CONFIG_VNC */
void qmp_change(const char *device, const char *target,
bool has_arg, const char *arg, Error **errp)
{
if (strcmp(device, "vnc") == 0) {
#ifdef CONFIG_VNC
qmp_change_vnc(target, has_arg, arg, errp);
#else
error_setg(errp, QERR_FEATURE_DISABLED, "vnc");
#endif
} else {
qmp_blockdev_change_medium(true, device, false, NULL, target,
has_arg, arg, false, 0, errp);

View File

@ -239,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
QAPISchemaModularCVisitor.__init__(
self, prefix, 'qapi-commands',
' * Schema-defined QAPI/QMP commands', __doc__)
self._regy = ''
self._regy = QAPIGenCCode()
self._visited_ret_types = {}
def _begin_module(self, name):
@ -275,20 +275,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''',
c_prefix=c_name(self._prefix, protect=False)))
genc.add(gen_registry(self._regy, self._prefix))
genc.add(gen_registry(self._regy.get_content(), self._prefix))
def visit_command(self, name, info, arg_type, ret_type, gen,
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
if not gen:
return
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
# FIXME: If T is a user-defined type, the user is responsible
# for making this work, i.e. to make T's condition the
# conjunction of the T-returning commands' conditions. If T
# is a built-in type, this isn't possible: the
# qmp_marshal_output_T() will be generated unconditionally.
if ret_type and ret_type not in self._visited_ret_types[self._genc]:
self._visited_ret_types[self._genc].add(ret_type)
self._genc.add(gen_marshal_output(ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
self._regy += gen_register_command(name, success_response, allow_oob,
allow_preconfig)
with ifcontext(ret_type.ifcond,
self._genh, self._genc, self._regy):
self._genc.add(gen_marshal_output(ret_type))
with ifcontext(ifcond, self._genh, self._genc, self._regy):
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
self._regy.add(gen_register_command(name, success_response,
allow_oob, allow_preconfig))
def gen_commands(schema, output_dir, prefix):

View File

@ -12,6 +12,7 @@
# See the COPYING file in the top-level directory.
from __future__ import print_function
from contextlib import contextmanager
import errno
import os
import re
@ -638,6 +639,27 @@ def add_name(name, info, meta, implicit=False):
all_names[name] = meta
def check_if(expr, info):
def check_if_str(ifcond, info):
if not isinstance(ifcond, str):
raise QAPISemError(
info, "'if' condition must be a string or a list of strings")
if ifcond == '':
raise QAPISemError(info, "'if' condition '' makes no sense")
ifcond = expr.get('if')
if ifcond is None:
return
if isinstance(ifcond, list):
if ifcond == []:
raise QAPISemError(info, "'if' condition [] is useless")
for elt in ifcond:
check_if_str(elt, info)
else:
check_if_str(ifcond, info)
def check_type(info, source, value, allow_array=False,
allow_dict=False, allow_optional=False,
allow_metas=[]):
@ -871,6 +893,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
if key == 'if':
check_if(expr, info)
for key in required:
if key not in expr:
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
@ -899,28 +923,28 @@ def check_exprs(exprs):
if 'enum' in expr:
meta = 'enum'
check_keys(expr_elem, 'enum', ['data'], ['prefix'])
check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
enum_types[expr[meta]] = expr
elif 'union' in expr:
meta = 'union'
check_keys(expr_elem, 'union', ['data'],
['base', 'discriminator'])
['base', 'discriminator', 'if'])
union_types[expr[meta]] = expr
elif 'alternate' in expr:
meta = 'alternate'
check_keys(expr_elem, 'alternate', ['data'])
check_keys(expr_elem, 'alternate', ['data'], ['if'])
elif 'struct' in expr:
meta = 'struct'
check_keys(expr_elem, 'struct', ['data'], ['base'])
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
struct_types[expr[meta]] = expr
elif 'command' in expr:
meta = 'command'
check_keys(expr_elem, 'command', [],
['data', 'returns', 'gen', 'success-response',
'boxed', 'allow-oob', 'allow-preconfig'])
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
else:
raise QAPISemError(expr_elem['info'],
"Expression is missing metatype")
@ -978,8 +1002,16 @@ def check_exprs(exprs):
# Schema compiler frontend
#
def listify_cond(ifcond):
if not ifcond:
return []
if not isinstance(ifcond, list):
return [ifcond]
return ifcond
class QAPISchemaEntity(object):
def __init__(self, name, info, doc):
def __init__(self, name, info, doc, ifcond=None):
assert name is None or isinstance(name, str)
self.name = name
self.module = None
@ -990,12 +1022,19 @@ class QAPISchemaEntity(object):
# such place).
self.info = info
self.doc = doc
self._ifcond = ifcond # self.ifcond is set only after .check()
def c_name(self):
return c_name(self.name)
def check(self, schema):
pass
if isinstance(self._ifcond, QAPISchemaType):
# inherit the condition from a type
typ = self._ifcond
typ.check(schema)
self.ifcond = typ.ifcond
else:
self.ifcond = listify_cond(self._ifcond)
def is_implicit(self):
return not self.info
@ -1024,26 +1063,26 @@ class QAPISchemaVisitor(object):
def visit_builtin_type(self, name, info, json_type):
pass
def visit_enum_type(self, name, info, values, prefix):
def visit_enum_type(self, name, info, ifcond, values, prefix):
pass
def visit_array_type(self, name, info, element_type):
def visit_array_type(self, name, info, ifcond, element_type):
pass
def visit_object_type(self, name, info, base, members, variants):
def visit_object_type(self, name, info, ifcond, base, members, variants):
pass
def visit_object_type_flat(self, name, info, members, variants):
def visit_object_type_flat(self, name, info, ifcond, members, variants):
pass
def visit_alternate_type(self, name, info, variants):
def visit_alternate_type(self, name, info, ifcond, variants):
pass
def visit_command(self, name, info, arg_type, ret_type, gen,
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
pass
def visit_event(self, name, info, arg_type, boxed):
def visit_event(self, name, info, ifcond, arg_type, boxed):
pass
@ -1122,8 +1161,8 @@ class QAPISchemaBuiltinType(QAPISchemaType):
class QAPISchemaEnumType(QAPISchemaType):
def __init__(self, name, info, doc, values, prefix):
QAPISchemaType.__init__(self, name, info, doc)
def __init__(self, name, info, doc, ifcond, values, prefix):
QAPISchemaType.__init__(self, name, info, doc, ifcond)
for v in values:
assert isinstance(v, QAPISchemaMember)
v.set_owner(name)
@ -1132,6 +1171,7 @@ class QAPISchemaEnumType(QAPISchemaType):
self.prefix = prefix
def check(self, schema):
QAPISchemaType.check(self, schema)
seen = {}
for v in self.values:
v.check_clash(self.info, seen)
@ -1152,20 +1192,23 @@ class QAPISchemaEnumType(QAPISchemaType):
return 'string'
def visit(self, visitor):
visitor.visit_enum_type(self.name, self.info,
visitor.visit_enum_type(self.name, self.info, self.ifcond,
self.member_names(), self.prefix)
class QAPISchemaArrayType(QAPISchemaType):
def __init__(self, name, info, element_type):
QAPISchemaType.__init__(self, name, info, None)
QAPISchemaType.__init__(self, name, info, None, None)
assert isinstance(element_type, str)
self._element_type_name = element_type
self.element_type = None
def check(self, schema):
QAPISchemaType.check(self, schema)
self.element_type = schema.lookup_type(self._element_type_name)
assert self.element_type
self.element_type.check(schema)
self.ifcond = self.element_type.ifcond
def is_implicit(self):
return True
@ -1183,15 +1226,17 @@ class QAPISchemaArrayType(QAPISchemaType):
return 'array of ' + elt_doc_type
def visit(self, visitor):
visitor.visit_array_type(self.name, self.info, self.element_type)
visitor.visit_array_type(self.name, self.info, self.ifcond,
self.element_type)
class QAPISchemaObjectType(QAPISchemaType):
def __init__(self, name, info, doc, base, local_members, variants):
def __init__(self, name, info, doc, ifcond,
base, local_members, variants):
# struct has local_members, optional base, and no variants
# flat union has base, variants, and no local_members
# simple union has local_members, variants, and no base
QAPISchemaType.__init__(self, name, info, doc)
QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert base is None or isinstance(base, str)
for m in local_members:
assert isinstance(m, QAPISchemaObjectTypeMember)
@ -1206,6 +1251,7 @@ class QAPISchemaObjectType(QAPISchemaType):
self.members = None
def check(self, schema):
QAPISchemaType.check(self, schema)
if self.members is False: # check for cycles
raise QAPISemError(self.info,
"Object %s contains itself" % self.name)
@ -1263,9 +1309,9 @@ class QAPISchemaObjectType(QAPISchemaType):
return 'object'
def visit(self, visitor):
visitor.visit_object_type(self.name, self.info,
visitor.visit_object_type(self.name, self.info, self.ifcond,
self.base, self.local_members, self.variants)
visitor.visit_object_type_flat(self.name, self.info,
visitor.visit_object_type_flat(self.name, self.info, self.ifcond,
self.members, self.variants)
@ -1387,8 +1433,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
class QAPISchemaAlternateType(QAPISchemaType):
def __init__(self, name, info, doc, variants):
QAPISchemaType.__init__(self, name, info, doc)
def __init__(self, name, info, doc, ifcond, variants):
QAPISchemaType.__init__(self, name, info, doc, ifcond)
assert isinstance(variants, QAPISchemaObjectTypeVariants)
assert variants.tag_member
variants.set_owner(name)
@ -1396,6 +1442,7 @@ class QAPISchemaAlternateType(QAPISchemaType):
self.variants = variants
def check(self, schema):
QAPISchemaType.check(self, schema)
self.variants.tag_member.check(schema)
# Not calling self.variants.check_clash(), because there's nothing
# to clash with
@ -1417,16 +1464,17 @@ class QAPISchemaAlternateType(QAPISchemaType):
return 'value'
def visit(self, visitor):
visitor.visit_alternate_type(self.name, self.info, self.variants)
visitor.visit_alternate_type(self.name, self.info, self.ifcond,
self.variants)
def is_empty(self):
return False
class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type,
def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
gen, success_response, boxed, allow_oob, allow_preconfig):
QAPISchemaEntity.__init__(self, name, info, doc)
QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
self._arg_type_name = arg_type
@ -1440,6 +1488,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.allow_preconfig = allow_preconfig
def check(self, schema):
QAPISchemaEntity.check(self, schema)
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@ -1459,7 +1508,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
assert isinstance(self.ret_type, QAPISchemaType)
def visit(self, visitor):
visitor.visit_command(self.name, self.info,
visitor.visit_command(self.name, self.info, self.ifcond,
self.arg_type, self.ret_type,
self.gen, self.success_response,
self.boxed, self.allow_oob,
@ -1467,14 +1516,15 @@ class QAPISchemaCommand(QAPISchemaEntity):
class QAPISchemaEvent(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, boxed):
QAPISchemaEntity.__init__(self, name, info, doc)
def __init__(self, name, info, doc, ifcond, arg_type, boxed):
QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str)
self._arg_type_name = arg_type
self.arg_type = None
self.boxed = boxed
def check(self, schema):
QAPISchemaEntity.check(self, schema)
if self._arg_type_name:
self.arg_type = schema.lookup_type(self._arg_type_name)
assert (isinstance(self.arg_type, QAPISchemaObjectType) or
@ -1491,7 +1541,8 @@ class QAPISchemaEvent(QAPISchemaEntity):
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
def visit(self, visitor):
visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
visitor.visit_event(self.name, self.info, self.ifcond,
self.arg_type, self.boxed)
class QAPISchema(object):
@ -1567,22 +1618,22 @@ class QAPISchema(object):
('null', 'null', 'QNull' + pointer_suffix)]:
self._def_builtin_type(*t)
self.the_empty_object_type = QAPISchemaObjectType(
'q_empty', None, None, None, [], None)
'q_empty', None, None, None, None, [], None)
self._def_entity(self.the_empty_object_type)
qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
'qstring', 'qdict', 'qlist',
'qbool'])
self._def_entity(QAPISchemaEnumType('QType', None, None,
self._def_entity(QAPISchemaEnumType('QType', None, None, None,
qtype_values, 'QTYPE'))
def _make_enum_members(self, values):
return [QAPISchemaMember(v) for v in values]
def _make_implicit_enum_type(self, name, info, values):
def _make_implicit_enum_type(self, name, info, ifcond, values):
# See also QAPISchemaObjectTypeMember._pretty_owner()
name = name + 'Kind' # Use namespace reserved by add_name()
self._def_entity(QAPISchemaEnumType(
name, info, None, self._make_enum_members(values), None))
name, info, None, ifcond, self._make_enum_members(values), None))
return name
def _make_array_type(self, element_type, info):
@ -1591,22 +1642,37 @@ class QAPISchema(object):
self._def_entity(QAPISchemaArrayType(name, info, element_type))
return name
def _make_implicit_object_type(self, name, info, doc, role, members):
def _make_implicit_object_type(self, name, info, doc, ifcond,
role, members):
if not members:
return None
# See also QAPISchemaObjectTypeMember._pretty_owner()
name = 'q_obj_%s-%s' % (name, role)
if not self.lookup_entity(name, QAPISchemaObjectType):
self._def_entity(QAPISchemaObjectType(name, info, doc, None,
members, None))
typ = self.lookup_entity(name, QAPISchemaObjectType)
if typ:
# The implicit object type has multiple users. This can
# happen only for simple unions' implicit wrapper types.
# Its ifcond should be the disjunction of its user's
# ifconds. Not implemented. Instead, we always pass the
# wrapped type's ifcond, which is trivially the same for all
# users. It's also necessary for the wrapper to compile.
# But it's not tight: the disjunction need not imply it. We
# may end up compiling useless wrapper types.
# TODO kill simple unions or implement the disjunction
assert ifcond == typ._ifcond # pylint: disable=protected-access
else:
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond,
None, members, None))
return name
def _def_enum_type(self, expr, info, doc):
name = expr['enum']
data = expr['data']
prefix = expr.get('prefix')
ifcond = expr.get('if')
self._def_entity(QAPISchemaEnumType(
name, info, doc, self._make_enum_members(data), prefix))
name, info, doc, ifcond,
self._make_enum_members(data), prefix))
def _make_member(self, name, typ, info):
optional = False
@ -1626,7 +1692,8 @@ class QAPISchema(object):
name = expr['struct']
base = expr.get('base')
data = expr['data']
self._def_entity(QAPISchemaObjectType(name, info, doc, base,
ifcond = expr.get('if')
self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base,
self._make_members(data, info),
None))
@ -1638,18 +1705,21 @@ class QAPISchema(object):
assert len(typ) == 1
typ = self._make_array_type(typ[0], info)
typ = self._make_implicit_object_type(
typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
typ, info, None, self.lookup_type(typ),
'wrapper', [self._make_member('data', typ, info)])
return QAPISchemaObjectTypeVariant(case, typ)
def _def_union_type(self, expr, info, doc):
name = expr['union']
data = expr['data']
base = expr.get('base')
ifcond = expr.get('if')
tag_name = expr.get('discriminator')
tag_member = None
if isinstance(base, dict):
base = (self._make_implicit_object_type(
name, info, doc, 'base', self._make_members(base, info)))
base = self._make_implicit_object_type(
name, info, doc, ifcond,
'base', self._make_members(base, info))
if tag_name:
variants = [self._make_variant(key, value)
for (key, value) in data.items()]
@ -1657,12 +1727,12 @@ class QAPISchema(object):
else:
variants = [self._make_simple_variant(key, value, info)
for (key, value) in data.items()]
typ = self._make_implicit_enum_type(name, info,
typ = self._make_implicit_enum_type(name, info, ifcond,
[v.name for v in variants])
tag_member = QAPISchemaObjectTypeMember('type', typ, False)
members = [tag_member]
self._def_entity(
QAPISchemaObjectType(name, info, doc, base, members,
QAPISchemaObjectType(name, info, doc, ifcond, base, members,
QAPISchemaObjectTypeVariants(tag_name,
tag_member,
variants)))
@ -1670,11 +1740,12 @@ class QAPISchema(object):
def _def_alternate_type(self, expr, info, doc):
name = expr['alternate']
data = expr['data']
ifcond = expr.get('if')
variants = [self._make_variant(key, value)
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
self._def_entity(
QAPISchemaAlternateType(name, info, doc,
QAPISchemaAlternateType(name, info, doc, ifcond,
QAPISchemaObjectTypeVariants(None,
tag_member,
variants)))
@ -1688,13 +1759,14 @@ class QAPISchema(object):
boxed = expr.get('boxed', False)
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
ifcond = expr.get('if')
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
name, info, doc, ifcond, 'arg', self._make_members(data, info))
if isinstance(rets, list):
assert len(rets) == 1
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
gen, success_response,
boxed, allow_oob, allow_preconfig))
@ -1702,10 +1774,11 @@ class QAPISchema(object):
name = expr['event']
data = expr.get('data')
boxed = expr.get('boxed', False)
ifcond = expr.get('if')
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
name, info, doc, ifcond, 'arg', self._make_members(data, info))
self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed))
def _def_exprs(self, exprs):
for expr_elem in exprs:
@ -1869,8 +1942,8 @@ def cgen(code, **kwds):
if indent_level:
indent = genindent(indent_level)
# re.subn() lacks flags support before Python 2.7, use re.compile()
raw = re.subn(re.compile(r'^.', re.MULTILINE),
indent + r'\g<0>', raw)
raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE),
indent, raw)
raw = raw[0]
return re.sub(re.escape(eatspace) + r' *', '', raw)
@ -1902,6 +1975,40 @@ def guardend(name):
name=guardname(name))
def gen_if(ifcond):
ret = ''
for ifc in ifcond:
ret += mcgen('''
#if %(cond)s
''', cond=ifc)
return ret
def gen_endif(ifcond):
ret = ''
for ifc in reversed(ifcond):
ret += mcgen('''
#endif /* %(cond)s */
''', cond=ifc)
return ret
def _wrap_ifcond(ifcond, before, after):
if before == after:
return after # suppress empty #if ... #endif
assert after.startswith(before)
out = before
added = after[len(before):]
if added[0] == '\n':
out += '\n'
added = added[1:]
out += gen_if(ifcond)
out += added
out += gen_endif(ifcond)
return out
def gen_enum_lookup(name, values, prefix=None):
ret = mcgen('''
@ -1999,6 +2106,10 @@ class QAPIGen(object):
def add(self, text):
self._body += text
def get_content(self, fname=None):
return (self._top(fname) + self._preamble + self._body
+ self._bottom(fname))
def _top(self, fname):
return ''
@ -2019,8 +2130,7 @@ class QAPIGen(object):
f = open(fd, 'r+', encoding='utf-8')
else:
f = os.fdopen(fd, 'r+')
text = (self._top(fname) + self._preamble + self._body
+ self._bottom(fname))
text = self.get_content(fname)
oldtext = f.read(len(text) + 1)
if text != oldtext:
f.seek(0)
@ -2029,10 +2139,62 @@ class QAPIGen(object):
f.close()
class QAPIGenC(QAPIGen):
@contextmanager
def ifcontext(ifcond, *args):
"""A 'with' statement context manager to wrap with start_if()/end_if()
*args: any number of QAPIGenCCode
Example::
with ifcontext(ifcond, self._genh, self._genc):
modify self._genh and self._genc ...
Is equivalent to calling::
self._genh.start_if(ifcond)
self._genc.start_if(ifcond)
modify self._genh and self._genc ...
self._genh.end_if()
self._genc.end_if()
"""
for arg in args:
arg.start_if(ifcond)
yield
for arg in args:
arg.end_if()
class QAPIGenCCode(QAPIGen):
def __init__(self):
QAPIGen.__init__(self)
self._start_if = None
def start_if(self, ifcond):
assert self._start_if is None
self._start_if = (ifcond, self._body, self._preamble)
def end_if(self):
assert self._start_if
self._wrap_ifcond()
self._start_if = None
def _wrap_ifcond(self):
self._body = _wrap_ifcond(self._start_if[0],
self._start_if[1], self._body)
self._preamble = _wrap_ifcond(self._start_if[0],
self._start_if[2], self._preamble)
def get_content(self, fname=None):
assert self._start_if is None
return QAPIGen.get_content(self, fname)
class QAPIGenC(QAPIGenCCode):
def __init__(self, blurb, pydoc):
QAPIGen.__init__(self)
QAPIGenCCode.__init__(self)
self._blurb = blurb
self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
re.MULTILINE))

32
scripts/qapi/doc.py Normal file → Executable file
View File

@ -174,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func):
return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items)
def texi_sections(doc):
def texi_sections(doc, ifcond):
"""Format additional sections following arguments"""
body = ''
for section in doc.sections:
@ -185,14 +185,16 @@ def texi_sections(doc):
body += texi_example(section.text)
else:
body += texi_format(section.text)
if ifcond:
body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond)
return body
def texi_entity(doc, what, base=None, variants=None,
def texi_entity(doc, what, ifcond, base=None, variants=None,
member_func=texi_member):
return (texi_body(doc)
+ texi_members(doc, what, base, variants, member_func)
+ texi_sections(doc))
+ texi_sections(doc, ifcond))
class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
@ -204,47 +206,47 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
def write(self, output_dir):
self._gen.write(output_dir, self._prefix + 'qapi-doc.texi')
def visit_enum_type(self, name, info, values, prefix):
def visit_enum_type(self, name, info, ifcond, values, prefix):
doc = self.cur_doc
self._gen.add(TYPE_FMT(type='Enum',
name=doc.symbol,
body=texi_entity(doc, 'Values',
body=texi_entity(doc, 'Values', ifcond,
member_func=texi_enum_value)))
def visit_object_type(self, name, info, base, members, variants):
def visit_object_type(self, name, info, ifcond, base, members, variants):
doc = self.cur_doc
if base and base.is_implicit():
base = None
self._gen.add(TYPE_FMT(type='Object',
name=doc.symbol,
body=texi_entity(doc, 'Members',
body=texi_entity(doc, 'Members', ifcond,
base, variants)))
def visit_alternate_type(self, name, info, variants):
def visit_alternate_type(self, name, info, ifcond, variants):
doc = self.cur_doc
self._gen.add(TYPE_FMT(type='Alternate',
name=doc.symbol,
body=texi_entity(doc, 'Members')))
body=texi_entity(doc, 'Members', ifcond)))
def visit_command(self, name, info, arg_type, ret_type, gen,
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
body += ('\n@b{Arguments:} the members of @code{%s}\n'
% arg_type.name)
body += texi_sections(doc)
body += texi_sections(doc, ifcond)
else:
body = texi_entity(doc, 'Arguments')
body = texi_entity(doc, 'Arguments', ifcond)
self._gen.add(MSG_FMT(type='Command',
name=doc.symbol,
body=body))
def visit_event(self, name, info, arg_type, boxed):
def visit_event(self, name, info, ifcond, arg_type, boxed):
doc = self.cur_doc
self._gen.add(MSG_FMT(type='Event',
name=doc.symbol,
body=texi_entity(doc, 'Arguments')))
body=texi_entity(doc, 'Arguments', ifcond)))
def symbol(self, doc, entity):
if self._gen._body:
@ -257,7 +259,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
assert not doc.args
if self._gen._body:
self._gen.add('\n')
self._gen.add(texi_body(doc) + texi_sections(doc))
self._gen.add(texi_body(doc) + texi_sections(doc, None))
def gen_doc(schema, output_dir, prefix):

View File

@ -184,9 +184,11 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
genh.add(gen_enum(self._enum_name, self._event_names))
genc.add(gen_enum_lookup(self._enum_name, self._event_names))
def visit_event(self, name, info, arg_type, boxed):
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name))
def visit_event(self, name, info, ifcond, arg_type, boxed):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_event_send_decl(name, arg_type, boxed))
self._genc.add(gen_event_send(name, arg_type, boxed,
self._enum_name))
self._event_names.append(name)

View File

@ -18,6 +18,15 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
def indent(level):
return level * 4 * ' '
if isinstance(obj, tuple):
ifobj, ifcond = obj
ret = gen_if(ifcond)
ret += to_qlit(ifobj, level)
endif = gen_endif(ifcond)
if endif:
ret += '\n' + endif
return ret
ret = ''
if not suppress_first_indent:
ret += indent(level)
@ -26,11 +35,11 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
elif isinstance(obj, str):
ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
elif isinstance(obj, list):
elts = [to_qlit(elt, level + 1)
elts = [to_qlit(elt, level + 1).strip('\n')
for elt in obj]
elts.append(indent(level + 1) + "{}")
ret += 'QLIT_QLIST(((QLitObject[]) {\n'
ret += ',\n'.join(elts) + '\n'
ret += '\n'.join(elts) + '\n'
ret += indent(level) + '}))'
elif isinstance(obj, dict):
elts = []
@ -45,6 +54,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False):
ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
else:
assert False # not implemented
if level > 0:
ret += ','
return ret
@ -126,12 +137,12 @@ const QLitObject %(c_name)s = %(c_string)s;
return '[' + self._use_type(typ.element_type) + ']'
return self._name(typ.name)
def _gen_qlit(self, name, mtype, obj):
def _gen_qlit(self, name, mtype, obj, ifcond):
if mtype not in ('command', 'event', 'builtin', 'array'):
name = self._name(name)
obj['name'] = name
obj['meta-type'] = mtype
self._qlits.append(obj)
self._qlits.append((obj, ifcond))
def _gen_member(self, member):
ret = {'name': member.name, 'type': self._use_type(member.type)}
@ -147,28 +158,29 @@ const QLitObject %(c_name)s = %(c_string)s;
return {'case': variant.name, 'type': self._use_type(variant.type)}
def visit_builtin_type(self, name, info, json_type):
self._gen_qlit(name, 'builtin', {'json-type': json_type})
self._gen_qlit(name, 'builtin', {'json-type': json_type}, [])
def visit_enum_type(self, name, info, values, prefix):
self._gen_qlit(name, 'enum', {'values': values})
def visit_enum_type(self, name, info, ifcond, values, prefix):
self._gen_qlit(name, 'enum', {'values': values}, ifcond)
def visit_array_type(self, name, info, element_type):
def visit_array_type(self, name, info, ifcond, element_type):
element = self._use_type(element_type)
self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
self._gen_qlit('[' + element + ']', 'array', {'element-type': element},
ifcond)
def visit_object_type_flat(self, name, info, members, variants):
def visit_object_type_flat(self, name, info, ifcond, members, variants):
obj = {'members': [self._gen_member(m) for m in members]}
if variants:
obj.update(self._gen_variants(variants.tag_member.name,
variants.variants))
self._gen_qlit(name, 'object', obj)
self._gen_qlit(name, 'object', obj, ifcond)
def visit_alternate_type(self, name, info, variants):
def visit_alternate_type(self, name, info, ifcond, variants):
self._gen_qlit(name, 'alternate',
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})
for m in variants.variants]}, ifcond)
def visit_command(self, name, info, arg_type, ret_type, gen,
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
@ -176,11 +188,12 @@ const QLitObject %(c_name)s = %(c_string)s;
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type),
'allow-oob': allow_oob,
'allow-preconfig': allow_preconfig})
'allow-preconfig': allow_preconfig}, ifcond)
def visit_event(self, name, info, arg_type, boxed):
def visit_event(self, name, info, ifcond, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)},
ifcond)
def gen_introspect(schema, output_dir, prefix, opt_unmask):

View File

@ -55,7 +55,7 @@ def gen_struct_members(members):
return ret
def gen_object(name, base, members, variants):
def gen_object(name, ifcond, base, members, variants):
if name in objects_seen:
return ''
objects_seen.add(name)
@ -64,11 +64,14 @@ def gen_object(name, base, members, variants):
if variants:
for v in variants.variants:
if isinstance(v.type, QAPISchemaObjectType):
ret += gen_object(v.type.name, v.type.base,
ret += gen_object(v.type.name, v.type.ifcond, v.type.base,
v.type.local_members, v.type.variants)
ret += mcgen('''
''')
ret += gen_if(ifcond)
ret += mcgen('''
struct %(c_name)s {
''',
c_name=c_name(name))
@ -101,6 +104,7 @@ struct %(c_name)s {
ret += mcgen('''
};
''')
ret += gen_endif(ifcond)
return ret
@ -208,34 +212,40 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
self._genh.add(gen_type_cleanup_decl(name))
self._genc.add(gen_type_cleanup(name))
def visit_enum_type(self, name, info, values, prefix):
self._genh.preamble_add(gen_enum(name, values, prefix))
self._genc.add(gen_enum_lookup(name, values, prefix))
def visit_enum_type(self, name, info, ifcond, values, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.preamble_add(gen_enum(name, values, prefix))
self._genc.add(gen_enum_lookup(name, values, prefix))
def visit_array_type(self, name, info, element_type):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_array(name, element_type))
self._gen_type_cleanup(name)
def visit_array_type(self, name, info, ifcond, element_type):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_array(name, element_type))
self._gen_type_cleanup(name)
def visit_object_type(self, name, info, base, members, variants):
def visit_object_type(self, name, info, ifcond, base, members, variants):
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, base, members, variants))
if base and not base.is_implicit():
self._genh.add(gen_upcast(name, base))
# TODO Worth changing the visitor signature, so we could
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
# implicit types won't be directly allocated/freed
self._gen_type_cleanup(name)
with ifcontext(ifcond, self._genh):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, ifcond, base, members, variants))
with ifcontext(ifcond, self._genh, self._genc):
if base and not base.is_implicit():
self._genh.add(gen_upcast(name, base))
# TODO Worth changing the visitor signature, so we could
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
# implicit types won't be directly allocated/freed
self._gen_type_cleanup(name)
def visit_alternate_type(self, name, info, variants):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, None,
def visit_alternate_type(self, name, info, ifcond, variants):
with ifcontext(ifcond, self._genh):
self._genh.preamble_add(gen_fwd_object_or_array(name))
self._genh.add(gen_object(name, ifcond, None,
[variants.tag_member], variants))
self._gen_type_cleanup(name)
with ifcontext(ifcond, self._genh, self._genc):
self._gen_type_cleanup(name)
def gen_types(schema, output_dir, prefix, opt_builtins):

View File

@ -310,30 +310,35 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
''',
types=types))
def visit_enum_type(self, name, info, values, prefix):
self._genh.add(gen_visit_decl(name, scalar=True))
self._genc.add(gen_visit_enum(name))
def visit_enum_type(self, name, info, ifcond, values, prefix):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name, scalar=True))
self._genc.add(gen_visit_enum(name))
def visit_array_type(self, name, info, element_type):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_list(name, element_type))
def visit_array_type(self, name, info, ifcond, element_type):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_list(name, element_type))
def visit_object_type(self, name, info, base, members, variants):
def visit_object_type(self, name, info, ifcond, base, members, variants):
# Nothing to do for the special empty builtin
if name == 'q_empty':
return
self._genh.add(gen_visit_members_decl(name))
self._genc.add(gen_visit_object_members(name, base, members, variants))
# TODO Worth changing the visitor signature, so we could
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
# only explicit types need an allocating visit
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_object(name, base, members, variants))
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_members_decl(name))
self._genc.add(gen_visit_object_members(name, base,
members, variants))
# TODO Worth changing the visitor signature, so we could
# directly use rather than repeat type.is_implicit()?
if not name.startswith('q_'):
# only explicit types need an allocating visit
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_object(name, base, members, variants))
def visit_alternate_type(self, name, info, variants):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_alternate(name, variants))
def visit_alternate_type(self, name, info, ifcond, variants):
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
self._genc.add(gen_visit_alternate(name, variants))
def gen_visit(schema, output_dir, prefix, opt_builtins):

View File

@ -442,6 +442,10 @@ qapi-schema += args-unknown.json
qapi-schema += bad-base.json
qapi-schema += bad-data.json
qapi-schema += bad-ident.json
qapi-schema += bad-if.json
qapi-schema += bad-if-empty.json
qapi-schema += bad-if-empty-list.json
qapi-schema += bad-if-list.json
qapi-schema += bad-type-bool.json
qapi-schema += bad-type-dict.json
qapi-schema += bad-type-int.json

View File

@ -0,0 +1 @@
tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# check empty 'if' list
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': [] }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# check empty 'if'
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': '' }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# check invalid 'if' content
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': ['foo', ''] }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# check invalid 'if' type
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': { 'value': 'defined(TEST_IF_STRUCT)' } }

View File

View File

@ -55,7 +55,7 @@
#
# @two is undocumented
##
{ 'enum': 'Enum', 'data': [ 'one', 'two' ] }
{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' }
##
# @Base:

View File

@ -3,6 +3,7 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
prefix QTYPE
module doc-good.json
enum Enum ['one', 'two']
if ['defined(IFCOND)']
object Base
member base1: Enum optional=False
object Variant1

View File

@ -89,6 +89,8 @@ Not documented
@end table
@code{two} is undocumented
@b{If:} @code{defined(IFCOND)}
@end deftp

View File

@ -56,6 +56,9 @@
'data': { 'string0': 'str',
'dict1': 'UserDefTwoDict' } }
{ 'struct': 'UserDefThree',
'data': { 'string0': 'str' } }
# dummy struct to force generation of array types not otherwise mentioned
{ 'struct': 'ForceArrays',
'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
@ -193,3 +196,26 @@
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
'returns': '__org.qemu_x-Union1' }
# test 'if' condition handling
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': 'defined(TEST_IF_STRUCT)' }
{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ],
'if': 'defined(TEST_IF_ENUM)' }
{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' },
'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' },
'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' },
'returns': 'UserDefThree',
'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }

View File

@ -36,6 +36,8 @@ object UserDefTwoDict
object UserDefTwo
member string0: str optional=False
member dict1: UserDefTwoDict optional=False
object UserDefThree
member string0: str optional=False
object ForceArrays
member unused1: UserDefOneList optional=False
member unused2: UserDefTwoList optional=False
@ -233,3 +235,36 @@ object q_obj___org.qemu_x-command-arg
member d: __org.qemu_x-Alt optional=False
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
gen=True success_response=True boxed=False oob=False preconfig=False
object TestIfStruct
member foo: int optional=False
if ['defined(TEST_IF_STRUCT)']
enum TestIfEnum ['foo', 'bar']
if ['defined(TEST_IF_ENUM)']
object q_obj_TestStruct-wrapper
member data: TestStruct optional=False
enum TestIfUnionKind ['foo']
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
object TestIfUnion
member type: TestIfUnionKind optional=False
tag type
case foo: q_obj_TestStruct-wrapper
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
alternate TestIfAlternate
tag type
case foo: int
case bar: TestStruct
if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
object q_obj_TestIfCmd-arg
member foo: TestIfStruct optional=False
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
command TestCmdReturnDefThree None -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
object q_obj_TestIfEvent-arg
member foo: TestIfStruct optional=False
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
event TestIfEvent q_obj_TestIfEvent-arg
boxed=False
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']

View File

@ -23,12 +23,13 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
def visit_include(self, name, info):
print('include %s' % name)
def visit_enum_type(self, name, info, values, prefix):
def visit_enum_type(self, name, info, ifcond, values, prefix):
print('enum %s %s' % (name, values))
if prefix:
print(' prefix %s' % prefix)
self._print_if(ifcond)
def visit_object_type(self, name, info, base, members, variants):
def visit_object_type(self, name, info, ifcond, base, members, variants):
print('object %s' % name)
if base:
print(' base %s' % base.name)
@ -36,21 +37,25 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
print(' member %s: %s optional=%s' % \
(m.name, m.type.name, m.optional))
self._print_variants(variants)
self._print_if(ifcond)
def visit_alternate_type(self, name, info, variants):
def visit_alternate_type(self, name, info, ifcond, variants):
print('alternate %s' % name)
self._print_variants(variants)
self._print_if(ifcond)
def visit_command(self, name, info, arg_type, ret_type, gen,
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \
(gen, success_response, boxed, allow_oob, allow_preconfig))
self._print_if(ifcond)
def visit_event(self, name, info, arg_type, boxed):
def visit_event(self, name, info, ifcond, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))
print(' boxed=%s' % boxed)
self._print_if(ifcond)
@staticmethod
def _print_variants(variants):
@ -59,6 +64,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
for v in variants.variants:
print(' case %s: %s' % (v.name, v.type.name))
@staticmethod
def _print_if(ifcond, indent=4):
if ifcond:
print('%sif %s' % (' ' * indent, ifcond))
try:
schema = QAPISchema(sys.argv[1])

View File

@ -12,6 +12,18 @@
static QmpCommandList qmp_commands;
#if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD)
UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp)
{
return NULL;
}
#endif
UserDefThree *qmp_TestCmdReturnDefThree(Error **errp)
{
return NULL;
}
void qmp_user_def_cmd(Error **errp)
{
}

View File

@ -297,7 +297,9 @@ struct VncState
bool encode_ws;
bool websocket;
#ifdef CONFIG_VNC
VncClientInfo *info;
#endif
/* Job thread bottom half has put data for a forced update
* into the output buffer. This offset points to the end of