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
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):

File diff suppressed because it is too large Load Diff

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

Some files were not shown because too many files have changed in this diff Show More