Files
qemu/scripts/qapi/commands.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

394 lines
11 KiB
Python
Raw Normal View History

"""
QAPI command marshaller generator
Copyright IBM, Corp. 2011
Copyright (C) 2014-2018 Red Hat, Inc.
Authors:
Anthony Liguori <aliguori@us.ibm.com>
Michael Roth <mdroth@linux.vnet.ibm.com>
Markus Armbruster <armbru@redhat.com>
This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory.
"""
from typing import List, Optional
from .common import c_name, mcgen
from .gen import (
QAPISchemaModularCVisitor,
build_params,
gen_features,
ifcontext,
)
from .schema import (
QAPISchema,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
QAPISchemaType,
)
from .source import QAPISourceInfo
def gen_command_decl(name: str,
arg_type: Optional[QAPISchemaObjectType],
boxed: bool,
ret_type: Optional[QAPISchemaType],
coroutine: bool) -> str:
return mcgen('''
%(c_type)s %(coroutine_fn)sqmp_%(c_name)s(%(params)s);
''',
c_type=(ret_type and ret_type.c_type()) or 'void',
coroutine_fn='coroutine_fn ' if coroutine else '',
c_name=c_name(name),
params=build_params(arg_type, boxed, 'Error **errp'))
def gen_call(name: str,
arg_type: Optional[QAPISchemaObjectType],
boxed: bool,
ret_type: Optional[QAPISchemaType],
gen_tracing: bool) -> str:
ret = ''
argstr = ''
if boxed:
assert arg_type
qapi: Implement boxed types for commands/events Turn on the ability to pass command and event arguments in a single boxed parameter, which must name a non-empty type (although the type can be a struct with all optional members). For structs, it makes it possible to pass a single qapi type instead of a breakout of all struct members (useful if the arguments are already in a struct or if the number of members is large); for other complex types, it is now possible to use a union or alternate as the data for a command or event. The empty type may be technically feasible if needed down the road, but it's easier to forbid it now and relax things to allow it later, than it is to allow it now and have to special case how the generated 'q_empty' type is handled (see commit 7ce106a9 for reasons why nothing is generated for the empty type). An alternate type is never considered empty, but now that a boxed type can be either an object or an alternate, we have to provide a trivial QAPISchemaAlternateType.is_empty(). The new call to arg_type.is_empty() during QAPISchemaCommand.check() requires that we first check the type in question; but there is no chance of introducing a cycle since objects do not refer back to commands. We still have a split in syntax checking between ad-hoc parsing up front (merely validates that 'boxed' has a sane value) and during .check() methods (if 'boxed' is set, then 'data' must name a non-empty user-defined type). Generated code is unchanged, as long as no client uses the new feature. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1468468228-27827-10-git-send-email-eblake@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Test files renamed to *-boxed-*] Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-07-13 21:50:20 -06:00
argstr = '&arg, '
elif arg_type:
assert not arg_type.branches
for memb in arg_type.members:
2023-03-16 08:13:25 +01:00
assert not memb.ifcond.is_present()
qapi: Start to elide redundant has_FOO in generated C In QAPI, absent optional members are distinct from any present value. We thus represent an optional schema member FOO as two C members: a FOO with the member's type, and a bool has_FOO. Likewise for function arguments. However, has_FOO is actually redundant for a pointer-valued FOO, which can be null only when has_FOO is false, i.e. has_FOO == !!FOO. Except for arrays, where we a null FOO can also be a present empty array. The redundant has_FOO are a nuisance to work with. Improve the generator to elide them. Uses of has_FOO need to be replaced as follows. Tests of has_FOO become the equivalent comparison of FOO with null. For brevity, this is commonly done by implicit conversion to bool. Assignments to has_FOO get dropped. Likewise for arguments to has_FOO parameters. Beware: code may violate the invariant has_FOO == !!FOO before the transformation, and get away with it. The above transformation can then break things. Two cases: * Absent: if code ignores FOO entirely when !has_FOO (except for freeing it if necessary), even non-null / uninitialized FOO works. Such code is known to exist. * Present: if code ignores FOO entirely when has_FOO, even null FOO works. Such code should not exist. In both cases, replacing tests of has_FOO by FOO reverts their sense. We have to fix the value of FOO then. To facilitate review of the necessary updates to handwritten code, add means to opt out of this change, and opt out for all QAPI schema modules where the change requires updates to handwritten code. The next few commits will remove these opt-outs in reviewable chunks, then drop the means to opt out. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Message-Id: <20221104160712.3005652-5-armbru@redhat.com>
2022-11-04 17:06:46 +01:00
if memb.need_has():
qapi-commands: Utilize implicit struct visits Rather than generate inline per-member visits, take advantage of the 'visit_type_FOO_members()' function for command marshalling. This is possible now that implicit structs can be visited like any other. Generate call arguments from a stack- allocated struct, rather than a list of local variables: |@@ -57,26 +57,15 @@ void qmp_marshal_add_fd(QDict *args, QOb | QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); | QapiDeallocVisitor *qdv; | Visitor *v; |- bool has_fdset_id = false; |- int64_t fdset_id = 0; |- bool has_opaque = false; |- char *opaque = NULL; |+ q_obj_add_fd_arg arg = {0}; | | v = qmp_input_get_visitor(qiv); |- if (visit_optional(v, "fdset-id", &has_fdset_id)) { |- visit_type_int(v, "fdset-id", &fdset_id, &err); |- if (err) { |- goto out; |- } |- } |- if (visit_optional(v, "opaque", &has_opaque)) { |- visit_type_str(v, "opaque", &opaque, &err); |- if (err) { |- goto out; |- } |+ visit_type_q_obj_add_fd_arg_members(v, &arg, &err); |+ if (err) { |+ goto out; | } | |- retval = qmp_add_fd(has_fdset_id, fdset_id, has_opaque, opaque, &err); |+ retval = qmp_add_fd(arg.has_fdset_id, arg.fdset_id, arg.has_opaque, arg.opaque, &err); | if (err) { | goto out; | } |@@ -88,12 +77,7 @@ out: | qmp_input_visitor_cleanup(qiv); | qdv = qapi_dealloc_visitor_new(); | v = qapi_dealloc_get_visitor(qdv); |- if (visit_optional(v, "fdset-id", &has_fdset_id)) { |- visit_type_int(v, "fdset-id", &fdset_id, NULL); |- } |- if (visit_optional(v, "opaque", &has_opaque)) { |- visit_type_str(v, "opaque", &opaque, NULL); |- } |+ visit_type_q_obj_add_fd_arg_members(v, &arg, NULL); | qapi_dealloc_visitor_cleanup(qdv); | } This also has the nice side effect of eliminating a chance of collision between argument QMP names and local variables. This patch also paves the way for some followup simplifications in the generator, in subsequent patches. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1458254921-17042-9-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-03-17 16:48:33 -06:00
argstr += 'arg.has_%s, ' % c_name(memb.name)
argstr += 'arg.%s, ' % c_name(memb.name)
lhs = ''
if ret_type:
lhs = 'retval = '
name = c_name(name)
upper = name.upper()
if gen_tracing:
ret += mcgen('''
if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) {
g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args));
trace_qmp_enter_%(name)s(req_json->str);
}
''',
upper=upper, name=name)
ret += mcgen('''
%(lhs)sqmp_%(name)s(%(args)s&err);
''',
name=name, args=argstr, lhs=lhs)
ret += mcgen('''
if (err) {
''')
if gen_tracing:
ret += mcgen('''
trace_qmp_exit_%(name)s(error_get_pretty(err), false);
''',
name=name)
ret += mcgen('''
error_propagate(errp, err);
goto out;
}
''')
if ret_type:
qapi/command: Avoid generating unused qmp_marshal_output_T() qmp_marshal_output_T() is only ever called by qmp_marshal_C() for a command C that returns type T. We've always generated it as a static function on demand, i.e. when we generate a call. Since we split up monolithic generated code into modules (commit 252dc3105fc "qapi: Generate separate .h, .c for each module"), we do this per module. As noted in the commit message, this can result in identical (static) qmp_marshal_output_T() in several modules. Was deemed not worth avoiding. A bit later, we added 'if' conditionals to the schema language (merge commit 5dafaf4fbce). When a conditional definition uses a type, then its condition must imply the type's condition. We made this the user's responsibility. Hasn't been an issue in practice. However, the sharing of qmp_marshal_output_T() among commands complicates matters. To avoid both undefined function errors and unused function warnings, qmp_marshal_output_T() must be defined exactly when it's used. It is used when any of the qmp_marshal_C() calling it is defined, i.e. when any C's condition holds. The generator uses T's condition instead. To avoid both error and warning, T's condition must be the conjunction of all C's conditions. Unfortunately, this can be impossible: * Conditional command returning a builtin type A builtin type cannot be conditional. This is noted in a FIXME comment. * Commands in multiple modules where the conjunction differs between modules An instance of this came up recently. we have unconditional commands returning HumanReadableText. If we add a conditional one to a module that does not have unconditional ones, compilation fails with "defined but not used". If we make HumanReadableText conditional to fix this module, we break the others. Instead of complicating the code to compute the conjunction, simplify it: generate the output marshaling code right into qmp_marshal_C(). This duplicates it when multiple commands return the same type. The impact on code size is negligible: qemu-system-x86_64's text segment grows by 1448 bytes. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20250804130602.903904-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [Commit message typos fixed]
2025-08-04 15:06:02 +02:00
ret += gen_marshal_output(ret_type)
if gen_tracing:
if ret_type:
ret += mcgen('''
if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) {
g_autoptr(GString) ret_json = qobject_to_json(*ret);
trace_qmp_exit_%(name)s(ret_json->str, true);
}
''',
upper=upper, name=name)
else:
ret += mcgen('''
trace_qmp_exit_%(name)s("{}", true);
''',
name=name)
return ret
def gen_marshal_output(ret_type: QAPISchemaType) -> str:
return mcgen('''
qapi/command: Avoid generating unused qmp_marshal_output_T() qmp_marshal_output_T() is only ever called by qmp_marshal_C() for a command C that returns type T. We've always generated it as a static function on demand, i.e. when we generate a call. Since we split up monolithic generated code into modules (commit 252dc3105fc "qapi: Generate separate .h, .c for each module"), we do this per module. As noted in the commit message, this can result in identical (static) qmp_marshal_output_T() in several modules. Was deemed not worth avoiding. A bit later, we added 'if' conditionals to the schema language (merge commit 5dafaf4fbce). When a conditional definition uses a type, then its condition must imply the type's condition. We made this the user's responsibility. Hasn't been an issue in practice. However, the sharing of qmp_marshal_output_T() among commands complicates matters. To avoid both undefined function errors and unused function warnings, qmp_marshal_output_T() must be defined exactly when it's used. It is used when any of the qmp_marshal_C() calling it is defined, i.e. when any C's condition holds. The generator uses T's condition instead. To avoid both error and warning, T's condition must be the conjunction of all C's conditions. Unfortunately, this can be impossible: * Conditional command returning a builtin type A builtin type cannot be conditional. This is noted in a FIXME comment. * Commands in multiple modules where the conjunction differs between modules An instance of this came up recently. we have unconditional commands returning HumanReadableText. If we add a conditional one to a module that does not have unconditional ones, compilation fails with "defined but not used". If we make HumanReadableText conditional to fix this module, we break the others. Instead of complicating the code to compute the conjunction, simplify it: generate the output marshaling code right into qmp_marshal_C(). This duplicates it when multiple commands return the same type. The impact on code size is negligible: qemu-system-x86_64's text segment grows by 1448 bytes. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20250804130602.903904-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [Commit message typos fixed]
2025-08-04 15:06:02 +02:00
ov = qobject_output_visitor_new_qmp(ret);
if (visit_type_%(c_name)s(ov, "unused", &retval, errp)) {
visit_complete(ov, ret);
}
qapi/command: Avoid generating unused qmp_marshal_output_T() qmp_marshal_output_T() is only ever called by qmp_marshal_C() for a command C that returns type T. We've always generated it as a static function on demand, i.e. when we generate a call. Since we split up monolithic generated code into modules (commit 252dc3105fc "qapi: Generate separate .h, .c for each module"), we do this per module. As noted in the commit message, this can result in identical (static) qmp_marshal_output_T() in several modules. Was deemed not worth avoiding. A bit later, we added 'if' conditionals to the schema language (merge commit 5dafaf4fbce). When a conditional definition uses a type, then its condition must imply the type's condition. We made this the user's responsibility. Hasn't been an issue in practice. However, the sharing of qmp_marshal_output_T() among commands complicates matters. To avoid both undefined function errors and unused function warnings, qmp_marshal_output_T() must be defined exactly when it's used. It is used when any of the qmp_marshal_C() calling it is defined, i.e. when any C's condition holds. The generator uses T's condition instead. To avoid both error and warning, T's condition must be the conjunction of all C's conditions. Unfortunately, this can be impossible: * Conditional command returning a builtin type A builtin type cannot be conditional. This is noted in a FIXME comment. * Commands in multiple modules where the conjunction differs between modules An instance of this came up recently. we have unconditional commands returning HumanReadableText. If we add a conditional one to a module that does not have unconditional ones, compilation fails with "defined but not used". If we make HumanReadableText conditional to fix this module, we break the others. Instead of complicating the code to compute the conjunction, simplify it: generate the output marshaling code right into qmp_marshal_C(). This duplicates it when multiple commands return the same type. The impact on code size is negligible: qemu-system-x86_64's text segment grows by 1448 bytes. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20250804130602.903904-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [Commit message typos fixed]
2025-08-04 15:06:02 +02:00
visit_free(ov);
ov = qapi_dealloc_visitor_new();
visit_type_%(c_name)s(ov, "unused", &retval, NULL);
visit_free(ov);
''',
qapi/command: Avoid generating unused qmp_marshal_output_T() qmp_marshal_output_T() is only ever called by qmp_marshal_C() for a command C that returns type T. We've always generated it as a static function on demand, i.e. when we generate a call. Since we split up monolithic generated code into modules (commit 252dc3105fc "qapi: Generate separate .h, .c for each module"), we do this per module. As noted in the commit message, this can result in identical (static) qmp_marshal_output_T() in several modules. Was deemed not worth avoiding. A bit later, we added 'if' conditionals to the schema language (merge commit 5dafaf4fbce). When a conditional definition uses a type, then its condition must imply the type's condition. We made this the user's responsibility. Hasn't been an issue in practice. However, the sharing of qmp_marshal_output_T() among commands complicates matters. To avoid both undefined function errors and unused function warnings, qmp_marshal_output_T() must be defined exactly when it's used. It is used when any of the qmp_marshal_C() calling it is defined, i.e. when any C's condition holds. The generator uses T's condition instead. To avoid both error and warning, T's condition must be the conjunction of all C's conditions. Unfortunately, this can be impossible: * Conditional command returning a builtin type A builtin type cannot be conditional. This is noted in a FIXME comment. * Commands in multiple modules where the conjunction differs between modules An instance of this came up recently. we have unconditional commands returning HumanReadableText. If we add a conditional one to a module that does not have unconditional ones, compilation fails with "defined but not used". If we make HumanReadableText conditional to fix this module, we break the others. Instead of complicating the code to compute the conjunction, simplify it: generate the output marshaling code right into qmp_marshal_C(). This duplicates it when multiple commands return the same type. The impact on code size is negligible: qemu-system-x86_64's text segment grows by 1448 bytes. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20250804130602.903904-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [Commit message typos fixed]
2025-08-04 15:06:02 +02:00
c_name=ret_type.c_name())
def build_marshal_proto(name: str,
coroutine: bool) -> str:
return ('void %(coroutine_fn)sqmp_marshal_%(c_name)s(%(params)s)' % {
'coroutine_fn': 'coroutine_fn ' if coroutine else '',
'c_name': c_name(name),
'params': 'QDict *args, QObject **ret, Error **errp',
})
def gen_marshal_decl(name: str,
coroutine: bool) -> str:
return mcgen('''
%(proto)s;
''',
proto=build_marshal_proto(name, coroutine))
def gen_trace(name: str) -> str:
return mcgen('''
qmp_enter_%(name)s(const char *json) "%%s"
qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d"
''',
name=c_name(name))
def gen_marshal(name: str,
arg_type: Optional[QAPISchemaObjectType],
boxed: bool,
ret_type: Optional[QAPISchemaType],
gen_tracing: bool,
coroutine: bool) -> str:
have_args = boxed or (arg_type and not arg_type.is_empty())
if have_args:
assert arg_type is not None
arg_type_c_name = arg_type.c_name()
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
ret = mcgen('''
%(proto)s
{
Error *err = NULL;
bool ok = false;
Visitor *v;
''',
proto=build_marshal_proto(name, coroutine))
if ret_type:
ret += mcgen('''
%(c_type)s retval;
qapi/command: Avoid generating unused qmp_marshal_output_T() qmp_marshal_output_T() is only ever called by qmp_marshal_C() for a command C that returns type T. We've always generated it as a static function on demand, i.e. when we generate a call. Since we split up monolithic generated code into modules (commit 252dc3105fc "qapi: Generate separate .h, .c for each module"), we do this per module. As noted in the commit message, this can result in identical (static) qmp_marshal_output_T() in several modules. Was deemed not worth avoiding. A bit later, we added 'if' conditionals to the schema language (merge commit 5dafaf4fbce). When a conditional definition uses a type, then its condition must imply the type's condition. We made this the user's responsibility. Hasn't been an issue in practice. However, the sharing of qmp_marshal_output_T() among commands complicates matters. To avoid both undefined function errors and unused function warnings, qmp_marshal_output_T() must be defined exactly when it's used. It is used when any of the qmp_marshal_C() calling it is defined, i.e. when any C's condition holds. The generator uses T's condition instead. To avoid both error and warning, T's condition must be the conjunction of all C's conditions. Unfortunately, this can be impossible: * Conditional command returning a builtin type A builtin type cannot be conditional. This is noted in a FIXME comment. * Commands in multiple modules where the conjunction differs between modules An instance of this came up recently. we have unconditional commands returning HumanReadableText. If we add a conditional one to a module that does not have unconditional ones, compilation fails with "defined but not used". If we make HumanReadableText conditional to fix this module, we break the others. Instead of complicating the code to compute the conjunction, simplify it: generate the output marshaling code right into qmp_marshal_C(). This duplicates it when multiple commands return the same type. The impact on code size is negligible: qemu-system-x86_64's text segment grows by 1448 bytes. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-ID: <20250804130602.903904-1-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> [Commit message typos fixed]
2025-08-04 15:06:02 +02:00
Visitor *ov;
''',
c_type=ret_type.c_type())
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
if have_args:
ret += mcgen('''
%(c_name)s arg = {0};
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
''',
c_name=arg_type_c_name)
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
ret += mcgen('''
v = qobject_input_visitor_new_qmp(QOBJECT(args));
if (!visit_start_struct(v, NULL, NULL, 0, errp)) {
qapi-commands: Wrap argument visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; among the culprit callers was the generated marshal code on the 'arguments' dictionary of a QMP command. But we are about to tighten the input visitor, at which point the generated marshal code MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. Generated code grows as follows: |@@ -515,7 +641,12 @@ void qmp_marshal_blockdev_backup(QDict * | BlockdevBackup arg = {0}; | | v = qmp_input_get_visitor(qiv); |+ visit_start_struct(v, NULL, NULL, 0, &err); |+ if (err) { |+ goto out; |+ } | visit_type_BlockdevBackup_members(v, &arg, &err); |+ visit_end_struct(v, err ? NULL : &err); | if (err) { | goto out; | } |@@ -527,7 +715,9 @@ out: | qmp_input_visitor_cleanup(qiv); | qdv = qapi_dealloc_visitor_new(); | v = qapi_dealloc_get_visitor(qdv); |+ visit_start_struct(v, NULL, NULL, 0, NULL); | visit_type_BlockdevBackup_members(v, &arg, NULL); |+ visit_end_struct(v, NULL); | qapi_dealloc_visitor_cleanup(qdv); | } The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Prior to this patch, the fact that there was no final visit_end_struct() meant that even though we are using a strict input visit, the marshalling code was not detecting excess input at the top level (only in nested levels). Fortunately, we have code in monitor.c:qmp_check_client_args() that also checks for no excess arguments at the top level. But as the generated code is more compact than the manual check, a later patch will clean up monitor.c to drop the redundancy added here. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-9-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-28 15:45:16 -06:00
goto out;
}
''')
if have_args:
ret += mcgen('''
if (visit_type_%(c_arg_type)s_members(v, &arg, errp)) {
ok = visit_check_struct(v, errp);
qapi: Split visit_end_struct() into pieces As mentioned in previous patches, we want to call visit_end_struct() functions unconditionally, so that visitors can release resources tied up since the matching visit_start_struct() without also having to worry about error priority if more than one error occurs. Even though error_propagate() can be safely used to ignore a second error during cleanup caused by a first error, it is simpler if the cleanup cannot set an error. So, split out the error checking portion (basically, input visitors checking for unvisited keys) into a new function visit_check_struct(), which can be safely skipped if any earlier errors are encountered, and leave the cleanup portion (which never fails, but must be called unconditionally if visit_start_struct() succeeded) in visit_end_struct(). Generated code in qapi-visit.c has diffs resembling: |@@ -59,10 +59,12 @@ void visit_type_ACPIOSTInfo(Visitor *v, | goto out_obj; | } | visit_type_ACPIOSTInfo_members(v, obj, &err); |- error_propagate(errp, err); |- err = NULL; |+ if (err) { |+ goto out_obj; |+ } |+ visit_check_struct(v, &err); | out_obj: |- visit_end_struct(v, &err); |+ visit_end_struct(v); | out: and in qapi-event.c: @@ -47,7 +47,10 @@ void qapi_event_send_acpi_device_ost(ACP | goto out; | } | visit_type_q_obj_ACPI_DEVICE_OST_arg_members(v, &param, &err); |- visit_end_struct(v, err ? NULL : &err); |+ if (!err) { |+ visit_check_struct(v, &err); |+ } |+ visit_end_struct(v); | if (err) { | goto out; Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-20-git-send-email-eblake@redhat.com> [Conflict with a doc fixup resolved] Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-28 15:45:27 -06:00
}
''',
c_arg_type=arg_type_c_name)
else:
ret += mcgen('''
ok = visit_check_struct(v, errp);
''')
ret += mcgen('''
2016-06-09 10:48:34 -06:00
visit_end_struct(v, NULL);
if (!ok) {
goto out;
}
''')
ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing)
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
ret += mcgen('''
out:
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
visit_free(v);
''')
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
ret += mcgen('''
v = qapi_dealloc_visitor_new();
qapi-commands: Wrap argument visit in visit_start_struct The qmp-input visitor was allowing callers to play rather fast and loose: when visiting a QDict, you could grab members of the root dictionary without first pushing into the dict; among the culprit callers was the generated marshal code on the 'arguments' dictionary of a QMP command. But we are about to tighten the input visitor, at which point the generated marshal code MUST follow the same paradigms as everyone else, of pushing into the struct before grabbing its keys. Generated code grows as follows: |@@ -515,7 +641,12 @@ void qmp_marshal_blockdev_backup(QDict * | BlockdevBackup arg = {0}; | | v = qmp_input_get_visitor(qiv); |+ visit_start_struct(v, NULL, NULL, 0, &err); |+ if (err) { |+ goto out; |+ } | visit_type_BlockdevBackup_members(v, &arg, &err); |+ visit_end_struct(v, err ? NULL : &err); | if (err) { | goto out; | } |@@ -527,7 +715,9 @@ out: | qmp_input_visitor_cleanup(qiv); | qdv = qapi_dealloc_visitor_new(); | v = qapi_dealloc_get_visitor(qdv); |+ visit_start_struct(v, NULL, NULL, 0, NULL); | visit_type_BlockdevBackup_members(v, &arg, NULL); |+ visit_end_struct(v, NULL); | qapi_dealloc_visitor_cleanup(qdv); | } The use of 'err ? NULL : &err' is temporary; a later patch will clean that up when it splits visit_end_struct(). Prior to this patch, the fact that there was no final visit_end_struct() meant that even though we are using a strict input visit, the marshalling code was not detecting excess input at the top level (only in nested levels). Fortunately, we have code in monitor.c:qmp_check_client_args() that also checks for no excess arguments at the top level. But as the generated code is more compact than the manual check, a later patch will clean up monitor.c to drop the redundancy added here. Signed-off-by: Eric Blake <eblake@redhat.com> Message-Id: <1461879932-9020-9-git-send-email-eblake@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-04-28 15:45:16 -06:00
visit_start_struct(v, NULL, NULL, 0, NULL);
''')
if have_args:
ret += mcgen('''
visit_type_%(c_arg_type)s_members(v, &arg, NULL);
''',
c_arg_type=arg_type_c_name)
ret += mcgen('''
2016-06-09 10:48:34 -06:00
visit_end_struct(v, NULL);
visit_free(v);
''')
qapi: check invalid arguments on no-args commands The generated marshal functions do not visit arguments from commands that take no arguments. Thus they fail to catch invalid members. Visit the arguments, if provided, to throw an error in case of invalid members. Currently, qmp_check_client_args() checks for invalid arguments and correctly catches this case. When switching to qmp_dispatch() we want to keep that behaviour. The commands using 'O' may have arbitrary arguments, and must have 'gen': false in the qapi schema to skip the generated checks. Old/new diff: void qmp_marshal_stop(QDict *args, QObject **ret, Error **errp) { Error *err = NULL; + Visitor *v = NULL; - (void)args; + if (args) { + v = qmp_input_visitor_new(QOBJECT(args), true); + visit_start_struct(v, NULL, NULL, 0, &err); + if (err) { + goto out; + } + + if (!err) { + visit_check_struct(v, &err); + } + visit_end_struct(v, NULL); + if (err) { + goto out; + } + } qmp_stop(&err); + +out: error_propagate(errp, err); + visit_free(v); + if (args) { + v = qapi_dealloc_visitor_new(); + visit_start_struct(v, NULL, NULL, 0, NULL); + + visit_end_struct(v, NULL); + visit_free(v); + } } The new code closely resembles code for a command with arguments. Differences: - the visit of the argument and its cleanup struct don't visit any members (because there are none). - the visit of the argument struct and its cleanup are conditional. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20160912091913.15831-14-marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com>
2016-09-12 13:19:08 +04:00
ret += mcgen('''
qmp: Wean off qerror_report() The traditional QMP command handler interface int qmp_FOO(Monitor *mon, const QDict *params, QObject **ret_data); doesn't provide for returning an Error object. Instead, the handler is expected to stash it in the monitor with qerror_report(). When we rebased QMP on top of QAPI, we didn't change this interface. Instead, commit 776574d introduced "middle mode" as a temporary aid for converting existing QMP commands to QAPI one by one. More than three years later, we're still using it. Middle mode has two effects: * Instead of the native input marshallers static void qmp_marshal_input_FOO(QDict *, QObject **, Error **) it generates input marshallers conforming to the traditional QMP command handler interface. * It suppresses generation of code to register them with qmp_register_command() This permits giving them internal linkage. As long as we need qmp-commands.hx, we can't use the registry behind qmp_register_command(), so the latter has to stay for now. The former has to go to get rid of qerror_report(). Changing all QMP commands to fit the QAPI mold in one go was impractical back when we started, but by now there are just a few stragglers left: do_qmp_capabilities(), qmp_qom_set(), qmp_qom_get(), qmp_object_add(), qmp_netdev_add(), do_device_add(). Switch middle mode to generate native input marshallers, and adapt the stragglers. Simplifies both the monitor code and the stragglers. Rename do_qmp_capabilities() to qmp_capabilities(), and do_device_add() to qmp_device_add, because that's how QMP command handlers are named today. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Luiz Capitulino <lcapitulino@redhat.com>
2015-03-13 17:25:50 +01:00
}
''')
return ret
def gen_register_command(name: str,
features: List[QAPISchemaFeature],
success_response: bool,
allow_oob: bool,
allow_preconfig: bool,
coroutine: bool) -> str:
options = []
if not success_response:
options += ['QCO_NO_SUCCESS_RESP']
if allow_oob:
options += ['QCO_ALLOW_OOB']
if allow_preconfig:
options += ['QCO_ALLOW_PRECONFIG']
if coroutine:
options += ['QCO_COROUTINE']
ret = mcgen('''
qmp_register_command(cmds, "%(name)s",
qmp_marshal_%(c_name)s, %(opts)s, %(feats)s);
''',
name=name, c_name=c_name(name),
opts=' | '.join(options) or 0,
feats=gen_features(features))
return ret
qapi: Generate separate .h, .c for each module Our qapi-schema.json is composed of modules connected by include directives, but the generated code is monolithic all the same: one qapi-types.h with all the types, one qapi-visit.h with all the visitors, and so forth. These monolithic headers get included all over the place. In my "build everything" tree, adding a QAPI type recompiles about 4800 out of 5100 objects. We wouldn't write such monolithic headers by hand. It stands to reason that we shouldn't generate them, either. Split up generated qapi-types.h to mirror the schema's modular structure: one header per module. Name the main module's header qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h. Mirror the schema's includes in the headers, so that qapi-types.h gets you everything exactly as before. If you need less, you can include one or more of the sub-module headers. To be exploited shortly. Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h, qmp-commands.c, qapi-event.h, qapi-event.c the same way. qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic. The split of qmp-commands.c duplicates static helper function qmp_marshal_output_str() in qapi-commands-char.c and qapi-commands-misc.c. This happens when commands returning the same type occur in multiple modules. Not worth avoiding. Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and qmp-commands.[ch] to qapi-commands.[ch], name the shards that way already, to reduce churn. This requires temporary hacks in commands.py and events.py. Similarly, c_name() must temporarily be taught to munge '/' in common.py. They'll go away with the rename. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20180211093607.27351-23-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> [eblake: declare a dummy variable in each .c file, to shut up OSX toolchain warnings about empty .o files, including hacking c_name()] Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 10:36:00 +01:00
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
def __init__(self, prefix: str, gen_tracing: bool):
super().__init__(
prefix, 'qapi-commands',
' * Schema-defined QAPI/QMP commands', None, __doc__,
gen_tracing=gen_tracing)
self._gen_tracing = gen_tracing
qapi: Generate separate .h, .c for each module Our qapi-schema.json is composed of modules connected by include directives, but the generated code is monolithic all the same: one qapi-types.h with all the types, one qapi-visit.h with all the visitors, and so forth. These monolithic headers get included all over the place. In my "build everything" tree, adding a QAPI type recompiles about 4800 out of 5100 objects. We wouldn't write such monolithic headers by hand. It stands to reason that we shouldn't generate them, either. Split up generated qapi-types.h to mirror the schema's modular structure: one header per module. Name the main module's header qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h. Mirror the schema's includes in the headers, so that qapi-types.h gets you everything exactly as before. If you need less, you can include one or more of the sub-module headers. To be exploited shortly. Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h, qmp-commands.c, qapi-event.h, qapi-event.c the same way. qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic. The split of qmp-commands.c duplicates static helper function qmp_marshal_output_str() in qapi-commands-char.c and qapi-commands-misc.c. This happens when commands returning the same type occur in multiple modules. Not worth avoiding. Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and qmp-commands.[ch] to qapi-commands.[ch], name the shards that way already, to reduce churn. This requires temporary hacks in commands.py and events.py. Similarly, c_name() must temporarily be taught to munge '/' in common.py. They'll go away with the rename. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20180211093607.27351-23-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> [eblake: declare a dummy variable in each .c file, to shut up OSX toolchain warnings about empty .o files, including hacking c_name()] Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 10:36:00 +01:00
def _begin_user_module(self, name: str) -> None:
commands = self._module_basename('qapi-commands', name)
types = self._module_basename('qapi-types', name)
visit = self._module_basename('qapi-visit', name)
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/visitor.h"
#include "qobject/qdict.h"
#include "qapi/dealloc-visitor.h"
#include "qapi/error.h"
#include "%(visit)s.h"
#include "%(commands)s.h"
''',
commands=commands, visit=visit))
if self._gen_tracing and commands != 'qapi-commands':
self._genc.add(mcgen('''
#include "qobject/qjson.h"
#include "trace/trace-%(nm)s_trace_events.h"
''',
nm=c_name(commands, protect=False)))
# We use c_name(commands, protect=False) to turn '-' into '_', to
# match .underscorify() in trace/meson.build
self._genh.add(mcgen('''
#include "%(types)s.h"
''',
types=types))
def visit_begin(self, schema: QAPISchema) -> None:
self._add_module('./init', ' * QAPI Commands initialization')
self._genh.add(mcgen('''
#include "qapi/qmp-registry.h"
qapi: Generate separate .h, .c for each module Our qapi-schema.json is composed of modules connected by include directives, but the generated code is monolithic all the same: one qapi-types.h with all the types, one qapi-visit.h with all the visitors, and so forth. These monolithic headers get included all over the place. In my "build everything" tree, adding a QAPI type recompiles about 4800 out of 5100 objects. We wouldn't write such monolithic headers by hand. It stands to reason that we shouldn't generate them, either. Split up generated qapi-types.h to mirror the schema's modular structure: one header per module. Name the main module's header qapi-types.h, and sub-module D/B.json's header D/qapi-types-B.h. Mirror the schema's includes in the headers, so that qapi-types.h gets you everything exactly as before. If you need less, you can include one or more of the sub-module headers. To be exploited shortly. Split up qapi-types.c, qapi-visit.h, qapi-visit.c, qmp-commands.h, qmp-commands.c, qapi-event.h, qapi-event.c the same way. qmp-introspect.h, qmp-introspect.c and qapi.texi remain monolithic. The split of qmp-commands.c duplicates static helper function qmp_marshal_output_str() in qapi-commands-char.c and qapi-commands-misc.c. This happens when commands returning the same type occur in multiple modules. Not worth avoiding. Since I'm going to rename qapi-event.[ch] to qapi-events.[ch], and qmp-commands.[ch] to qapi-commands.[ch], name the shards that way already, to reduce churn. This requires temporary hacks in commands.py and events.py. Similarly, c_name() must temporarily be taught to munge '/' in common.py. They'll go away with the rename. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20180211093607.27351-23-armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> [eblake: declare a dummy variable in each .c file, to shut up OSX toolchain warnings about empty .o files, including hacking c_name()] Signed-off-by: Eric Blake <eblake@redhat.com>
2018-02-11 10:36:00 +01:00
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''',
c_prefix=c_name(self._prefix, protect=False)))
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "%(prefix)sqapi-commands.h"
#include "%(prefix)sqapi-init-commands.h"
#include "%(prefix)sqapi-features.h"
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
{
QTAILQ_INIT(cmds);
''',
prefix=self._prefix,
c_prefix=c_name(self._prefix, protect=False)))
def visit_end(self) -> None:
with self._temp_module('./init'):
self._genc.add(mcgen('''
}
'''))
def visit_command(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
arg_type: Optional[QAPISchemaObjectType],
ret_type: Optional[QAPISchemaType],
gen: bool,
success_response: bool,
boxed: bool,
allow_oob: bool,
allow_preconfig: bool,
coroutine: bool) -> None:
if not gen:
return
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_command_decl(name, arg_type, boxed,
ret_type, coroutine))
self._genh.add(gen_marshal_decl(name, coroutine))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type,
self._gen_tracing, coroutine))
if self._gen_tracing:
self._gen_trace_events.add(gen_trace(name))
with self._temp_module('./init'):
with ifcontext(ifcond, self._genh, self._genc):
self._genc.add(gen_register_command(
name, features, success_response, allow_oob,
allow_preconfig, coroutine))
def gen_commands(schema: QAPISchema,
output_dir: str,
prefix: str,
gen_tracing: bool) -> None:
vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing)
schema.visit(vis)
vis.write(output_dir)