aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/devel/qapi-code-gen.txt10
-rw-r--r--qapi/introspect.json6
-rw-r--r--scripts/qapi/commands.py3
-rw-r--r--scripts/qapi/doc.py4
-rw-r--r--scripts/qapi/expr.py35
-rw-r--r--scripts/qapi/introspect.py7
-rw-r--r--scripts/qapi/schema.py22
-rwxr-xr-xtests/qapi-schema/test-qapi.py3
8 files changed, 62 insertions, 28 deletions
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 64d9e4c6a9..45c93a43cc 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -457,7 +457,8 @@ Syntax:
'*gen': false,
'*allow-oob': true,
'*allow-preconfig': true,
- '*if': COND }
+ '*if': COND,
+ '*features': FEATURES }
Member 'command' names the command.
@@ -640,9 +641,10 @@ change in the QMP syntax (usually by allowing values or operations
that previously resulted in an error). QMP clients may still need to
know whether the extension is available.
-For this purpose, a list of features can be specified for a struct type.
-This is exposed to the client as a list of string, where each string
-signals that this build of QEMU shows a certain behaviour.
+For this purpose, a list of features can be specified for a command or
+struct type. This is exposed to the client as a list of strings,
+where each string signals that this build of QEMU shows a certain
+behaviour.
Each member of the 'features' array defines a feature. It can either
be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 1843c1cb17..031a954fa9 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -266,13 +266,17 @@
# @allow-oob: whether the command allows out-of-band execution,
# defaults to false (Since: 2.12)
#
+# @features: names of features associated with the command, in no particular
+# order. (since 4.2)
+#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
'data': { 'arg-type': 'str', 'ret-type': 'str',
- '*allow-oob': 'bool' } }
+ '*allow-oob': 'bool',
+ '*features': [ 'str' ] } }
##
# @SchemaInfoEvent:
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 898516b086..ab98e504f3 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -277,7 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
genc.add(gen_registry(self._regy.get_content(), self._prefix))
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
if not gen:
return
# FIXME: If T is a user-defined type, the user is responsible
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index dc8919bab7..6d5726cf6e 100644
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -249,12 +249,14 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
body=texi_entity(doc, 'Members', ifcond)))
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
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_features(doc)
body += texi_sections(doc, ifcond)
else:
body = texi_entity(doc, 'Arguments', ifcond)
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 67cb2c2b6c..7c7394f835 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -185,6 +185,22 @@ def normalize_features(features):
for f in features]
+def check_features(features, info):
+ if features is None:
+ return
+ if not isinstance(features, list):
+ raise QAPISemError(info, "'features' must be an array")
+ for f in features:
+ source = "'features' member"
+ assert isinstance(f, dict)
+ check_keys(f, info, source, ['name'], ['if'])
+ check_name_is_str(f['name'], info, source)
+ source = "%s '%s'" % (source, f['name'])
+ check_name_str(f['name'], info, source)
+ check_if(f, info, source)
+ normalize_if(f)
+
+
def normalize_enum(expr):
if isinstance(expr['data'], list):
expr['data'] = [m if isinstance(m, dict) else {'name': m}
@@ -217,23 +233,10 @@ def check_enum(expr, info):
def check_struct(expr, info):
name = expr['struct']
members = expr['data']
- features = expr.get('features')
check_type(members, info, "'data'", allow_dict=name)
check_type(expr.get('base'), info, "'base'")
-
- if features:
- if not isinstance(features, list):
- raise QAPISemError(info, "'features' must be an array")
- for f in features:
- source = "'features' member"
- assert isinstance(f, dict)
- check_keys(f, info, source, ['name'], ['if'])
- check_name_is_str(f['name'], info, source)
- source = "%s '%s'" % (source, f['name'])
- check_name_str(f['name'], info, source)
- check_if(f, info, source)
- normalize_if(f)
+ check_features(expr.get('features'), info)
def check_union(expr, info):
@@ -283,6 +286,7 @@ def check_command(expr, info):
raise QAPISemError(info, "'boxed': true requires 'data'")
check_type(args, info, "'data'", allow_dict=not boxed)
check_type(rets, info, "'returns'", allow_array=True)
+ check_features(expr.get('features'), info)
def check_event(expr, info):
@@ -358,10 +362,11 @@ def check_exprs(exprs):
elif meta == 'command':
check_keys(expr, info, meta,
['command'],
- ['data', 'returns', 'boxed', 'if',
+ ['data', 'returns', 'boxed', 'if', 'features',
'gen', 'success-response', 'allow-oob',
'allow-preconfig'])
normalize_members(expr.get('data'))
+ normalize_features(expr.get('features'))
check_command(expr, info)
elif meta == 'event':
check_keys(expr, info, meta,
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 4f257591de..b3a463dd8b 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -211,13 +211,18 @@ const QLitObject %(c_name)s = %(c_string)s;
for m in variants.variants]}, ifcond)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
obj = {'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type)}
if allow_oob:
obj['allow-oob'] = allow_oob
+
+ if features:
+ obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
+
self._gen_qlit(name, 'command', obj, ifcond)
def visit_event(self, name, info, ifcond, arg_type, boxed):
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 2913a0fef0..f7d68a35f4 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -110,7 +110,8 @@ class QAPISchemaVisitor(object):
pass
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
pass
def visit_event(self, name, info, ifcond, arg_type, boxed):
@@ -659,10 +660,14 @@ class QAPISchemaCommand(QAPISchemaEntity):
meta = 'command'
def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
- gen, success_response, boxed, allow_oob, allow_preconfig):
+ gen, success_response, boxed, allow_oob, allow_preconfig,
+ features):
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)
+ for f in features:
+ assert isinstance(f, QAPISchemaFeature)
+ f.set_defined_in(name)
self._arg_type_name = arg_type
self.arg_type = None
self._ret_type_name = ret_type
@@ -672,6 +677,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.boxed = boxed
self.allow_oob = allow_oob
self.allow_preconfig = allow_preconfig
+ self.features = features
def check(self, schema):
QAPISchemaEntity.check(self, schema)
@@ -701,13 +707,19 @@ class QAPISchemaCommand(QAPISchemaEntity):
"command's 'returns' cannot take %s"
% self.ret_type.describe())
+ # Features are in a name space separate from members
+ seen = {}
+ for f in self.features:
+ f.check_clash(self.info, seen)
+
def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor)
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,
- self.allow_preconfig)
+ self.allow_preconfig,
+ self.features)
class QAPISchemaEvent(QAPISchemaEntity):
@@ -984,6 +996,7 @@ class QAPISchema(object):
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
ifcond = expr.get('if')
+ features = expr.get('features', [])
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, ifcond, 'arg', self._make_members(data, info))
@@ -992,7 +1005,8 @@ class QAPISchema(object):
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
gen, success_response,
- boxed, allow_oob, allow_preconfig))
+ boxed, allow_oob, allow_preconfig,
+ self._make_features(features, info)))
def _def_event(self, expr, info, doc):
name = expr['event']
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 29d9435bf7..d31ac4bbb7 100755
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -72,7 +72,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_if(ifcond)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
print('command %s %s -> %s'
% (name, arg_type and arg_type.name,
ret_type and ret_type.name))