In v4.1.0, we added feature flags just to struct types (commit 6a8c0b5102^..f3ed93d545), to satisfy an immediate need (commit c9d4070991 "file-posix: Add dynamic-auto-read-only QAPI feature"). We just added them to commands (commit 23394b4c39 "qapi: Add feature flags to commands") to satisfy another immediate need (commit d76744e65e "qapi: Allow introspecting fix for savevm's cooperation with blockdev"). Add them to the remaining definitions: enumeration types, union types, alternate types, and events. Signed-off-by: Markus Armbruster <armbru@xxxxxxxxxx> --- docs/devel/qapi-code-gen.txt | 15 ++-- tests/qapi-schema/doc-good.texi | 32 ++++++++- qapi/introspect.json | 28 +++++--- tests/test-qmp-cmds.c | 6 +- scripts/qapi/doc.py | 6 +- scripts/qapi/events.py | 2 +- scripts/qapi/expr.py | 11 ++- scripts/qapi/introspect.py | 31 ++++---- scripts/qapi/schema.py | 96 ++++++++++++++----------- scripts/qapi/types.py | 4 +- scripts/qapi/visit.py | 4 +- tests/qapi-schema/alternate-base.err | 2 +- tests/qapi-schema/doc-good.json | 18 ++++- tests/qapi-schema/doc-good.out | 20 +++++- tests/qapi-schema/qapi-schema-test.json | 29 ++++++-- tests/qapi-schema/qapi-schema-test.out | 27 +++++-- tests/qapi-schema/test-qapi.py | 9 ++- 17 files changed, 226 insertions(+), 114 deletions(-) diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index 45c93a43cc..eaeedc7bd3 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -172,7 +172,8 @@ Syntax: ENUM = { 'enum': STRING, 'data': [ ENUM-VALUE, ... ], '*prefix': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } ENUM-VALUE = STRING | { 'name': STRING, '*if': COND } @@ -279,12 +280,14 @@ below for more on this. Syntax: UNION = { 'union': STRING, 'data': BRANCHES, - '*if': COND } + '*if': COND, + '*features': FEATURES } | { 'union': STRING, 'data': BRANCHES, 'base': ( MEMBERS | STRING ), 'discriminator': STRING, - '*if': COND } + '*if': COND, + '*features': FEATURES } BRANCHES = { BRANCH, ... } BRANCH = STRING : TYPE-REF | STRING : { 'type': TYPE-REF, '*if': COND } @@ -397,7 +400,8 @@ the schema" below for more on this. Syntax: ALTERNATE = { 'alternate': STRING, 'data': ALTERNATIVES, - '*if': COND } + '*if': COND, + '*features': FEATURES } ALTERNATIVES = { ALTERNATIVE, ... } ALTERNATIVE = STRING : TYPE-REF | STRING : { 'type': STRING, '*if': COND } @@ -595,7 +599,8 @@ Syntax: 'data': STRING, 'boxed': true, ) - '*if': COND } + '*if': COND, + '*features': FEATURES } Member 'event' names the event. This is the event name used in the Client JSON Protocol. diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index d4b15dabf0..5ef7fea436 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -84,11 +84,17 @@ Examples: @table @asis @item @code{one} The @emph{one} @{and only@} +@code{two} is undocumented @*@b{If:} @code{defined(IFONE)} @item @code{two} Not documented @end table -@code{two} is undocumented + +@b{Features:} +@table @asis +@item @code{enum-feat} +Also @emph{one} @{and only@} +@end table @b{If:} @code{defined(IFCOND)} @end deftp @@ -151,6 +157,12 @@ a feature @item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat1} +a feature +@end table + @end deftp @@ -167,6 +179,12 @@ One of @t{"one"}, @t{"two"} @item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) @end table +@b{Features:} +@table @asis +@item @code{union-feat2} +a feature +@end table + @end deftp @@ -184,6 +202,12 @@ an integer Not documented @end table +@b{Features:} +@table @asis +@item @code{alt-feat} +a feature +@end table + @end deftp @@ -283,5 +307,11 @@ another feature @b{Arguments:} the members of @code{Object} +@b{Features:} +@table @asis +@item @code{feat3} +a feature +@end table + @end deftypefn diff --git a/qapi/introspect.json b/qapi/introspect.json index 031a954fa9..7322ab3f59 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -105,6 +105,17 @@ 'command': 'SchemaInfoCommand', 'event': 'SchemaInfoEvent' } } +## +# @SchemaInfoFeatures: +# +# @features: names of features associated with the entity, in no particular +# order. +# +# Since: 4.2 +## +{ 'struct': 'SchemaInfoFeatures', + 'data': { '*features': [ 'str' ] } } + ## # @SchemaInfoBuiltin: # @@ -142,6 +153,7 @@ # Since: 2.5 ## { 'struct': 'SchemaInfoEnum', + 'base': 'SchemaInfoFeatures', 'data': { 'values': ['str'] } } ## @@ -174,18 +186,15 @@ # and may even differ from the order of the values of the # enum type of the @tag. # -# @features: names of features associated with the type, in no particular -# order. (since: 4.1) -# # Values of this type are JSON object on the wire. # # Since: 2.5 ## { 'struct': 'SchemaInfoObject', + 'base': 'SchemaInfoFeatures', 'data': { 'members': [ 'SchemaInfoObjectMember' ], '*tag': 'str', - '*variants': [ 'SchemaInfoObjectVariant' ], - '*features': [ 'str' ] } } + '*variants': [ 'SchemaInfoObjectVariant' ] } } ## # @SchemaInfoObjectMember: @@ -239,6 +248,7 @@ # Since: 2.5 ## { 'struct': 'SchemaInfoAlternate', + 'base': 'SchemaInfoFeatures', 'data': { 'members': [ 'SchemaInfoAlternateMember' ] } } ## @@ -266,17 +276,14 @@ # @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', + 'base': 'SchemaInfoFeatures', 'data': { 'arg-type': 'str', 'ret-type': 'str', - '*allow-oob': 'bool', - '*features': [ 'str' ] } } + '*allow-oob': 'bool' } } ## # @SchemaInfoEvent: @@ -289,4 +296,5 @@ # Since: 2.5 ## { 'struct': 'SchemaInfoEvent', + 'base': 'SchemaInfoFeatures', 'data': { 'arg-type': 'str' } } diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index 3798ba1b16..cf4fa1a091 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -44,7 +44,7 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp) { } -void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, +void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1, FeatureStruct2 *fs2, FeatureStruct3 *fs3, FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1, CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3, @@ -52,10 +52,6 @@ void qmp_test_features(FeatureStruct0 *fs0, FeatureStruct1 *fs1, { } -void qmp_test_command_features0(Error **errp) -{ -} - void qmp_test_command_features1(Error **errp) { } diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 6f1c17f71f..53a1f8e952 100644 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -244,7 +244,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): doc = self.cur_doc self._gen.add(texi_type('Enum', doc, ifcond, texi_members(doc, 'Values', @@ -258,7 +258,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): self._gen.add(texi_type('Object', doc, ifcond, texi_members(doc, 'Members', base, variants))) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): doc = self.cur_doc self._gen.add(texi_type('Alternate', doc, ifcond, texi_members(doc, 'Members'))) @@ -271,7 +271,7 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor): texi_arguments(doc, arg_type if boxed else None))) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): doc = self.cur_doc self._gen.add(texi_msg('Event', doc, ifcond, texi_arguments(doc, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 10fc509fa9..f64e61076e 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -189,7 +189,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict); event_emit=self._event_emit_name, event_enum=self._event_enum_name)) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, 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, diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py index d7a289eded..92b2407315 100644 --- a/scripts/qapi/expr.py +++ b/scripts/qapi/expr.py @@ -220,7 +220,6 @@ def check_struct(expr, info): check_type(members, info, "'data'", allow_dict=name) check_type(expr.get('base'), info, "'base'") - check_features(expr.get('features'), info) def check_union(expr, info): @@ -268,7 +267,6 @@ 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): @@ -320,18 +318,18 @@ def check_exprs(exprs): if meta == 'enum': check_keys(expr, info, meta, - ['enum', 'data'], ['if', 'prefix']) + ['enum', 'data'], ['if', 'features', 'prefix']) check_enum(expr, info) elif meta == 'union': check_keys(expr, info, meta, ['union', 'data'], - ['base', 'discriminator', 'if']) + ['base', 'discriminator', 'if', 'features']) normalize_members(expr.get('base')) normalize_members(expr['data']) check_union(expr, info) elif meta == 'alternate': check_keys(expr, info, meta, - ['alternate', 'data'], ['if']) + ['alternate', 'data'], ['if', 'features']) normalize_members(expr['data']) check_alternate(expr, info) elif meta == 'struct': @@ -349,13 +347,14 @@ def check_exprs(exprs): check_command(expr, info) elif meta == 'event': check_keys(expr, info, meta, - ['event'], ['data', 'boxed', 'if']) + ['event'], ['data', 'boxed', 'if', 'features']) normalize_members(expr.get('data')) check_event(expr, info) else: assert False, 'unexpected meta type' check_if(expr, info, meta) + check_features(expr.get('features'), info) check_flags(expr, info) return exprs diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index b3a463dd8b..ba493977cf 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -146,7 +146,7 @@ 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, ifcond): + def _gen_qlit(self, name, mtype, obj, ifcond, features): extra = {} if mtype not in ('command', 'event', 'builtin', 'array'): if not self._unmask: @@ -156,6 +156,8 @@ const QLitObject %(c_name)s = %(c_string)s; name = self._name(name) obj['name'] = name obj['meta-type'] = mtype + if features: + obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] if ifcond: extra['if'] = ifcond if extra: @@ -180,18 +182,18 @@ const QLitObject %(c_name)s = %(c_string)s; {'if': variant.ifcond}) 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}, [], None) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): self._gen_qlit(name, 'enum', {'values': [(m.name, {'if': m.ifcond}) for m in members]}, - ifcond) + ifcond, features) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, - ifcond) + ifcond, None) def visit_object_type_flat(self, name, info, ifcond, members, variants, features): @@ -199,16 +201,15 @@ const QLitObject %(c_name)s = %(c_string)s; if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - if features: - obj['features'] = [(f.name, {'if': f.ifcond}) for f in features] - self._gen_qlit(name, 'object', obj, ifcond) + self._gen_qlit(name, 'object', obj, ifcond, features) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): self._gen_qlit(name, 'alternate', {'members': [ ({'type': self._use_type(m.type)}, {'if': m.ifcond}) - for m in variants.variants]}, ifcond) + for m in variants.variants]}, + ifcond, features) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, @@ -219,16 +220,12 @@ const QLitObject %(c_name)s = %(c_string)s; 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob + self._gen_qlit(name, 'command', obj, ifcond, features) - 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): + def visit_event(self, name, info, ifcond, features, 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)}, - ifcond) + ifcond, features) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cf0045f34e..f13f442896 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -109,7 +109,7 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): @@ -123,7 +123,7 @@ class QAPISchemaVisitor(object): features): pass - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): pass def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, @@ -131,7 +131,7 @@ class QAPISchemaVisitor(object): features): pass - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): pass @@ -220,8 +220,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): meta = 'enum' - def __init__(self, name, info, doc, ifcond, members, prefix): - QAPISchemaType.__init__(self, name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, members, prefix): + QAPISchemaType.__init__(self, name, info, doc, ifcond, features) for m in members: assert isinstance(m, QAPISchemaEnumMember) m.set_defined_in(name) @@ -256,15 +256,16 @@ class QAPISchemaEnumType(QAPISchemaType): def visit(self, visitor): QAPISchemaType.visit(self, visitor) - visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.members, self.prefix) + visitor.visit_enum_type( + self.name, self.info, self.ifcond, self.features, + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): meta = 'array' def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None, None) + QAPISchemaType.__init__(self, name, info, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None @@ -312,8 +313,8 @@ class QAPISchemaArrayType(QAPISchemaType): class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, - base, local_members, variants, features): + def __init__(self, name, info, doc, ifcond, features, + 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 @@ -609,8 +610,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): class QAPISchemaAlternateType(QAPISchemaType): meta = 'alternate' - def __init__(self, name, info, doc, ifcond, variants): - QAPISchemaType.__init__(self, name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, variants): + QAPISchemaType.__init__(self, name, info, doc, ifcond, features) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert variants.tag_member variants.set_defined_in(name) @@ -669,16 +670,16 @@ class QAPISchemaAlternateType(QAPISchemaType): def visit(self, visitor): QAPISchemaType.visit(self, visitor) - visitor.visit_alternate_type(self.name, self.info, self.ifcond, - self.variants) + visitor.visit_alternate_type( + self.name, self.info, self.ifcond, self.features, self.variants) class QAPISchemaCommand(QAPISchemaEntity): meta = 'command' - def __init__(self, name, info, doc, ifcond, arg_type, ret_type, - gen, success_response, boxed, allow_oob, allow_preconfig, - features): + def __init__(self, name, info, doc, ifcond, features, + arg_type, ret_type, + gen, success_response, boxed, allow_oob, allow_preconfig): QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) @@ -739,8 +740,8 @@ class QAPISchemaCommand(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity): meta = 'event' - def __init__(self, name, info, doc, ifcond, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc, ifcond) + def __init__(self, name, info, doc, ifcond, features, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond, features) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None @@ -770,8 +771,9 @@ class QAPISchemaEvent(QAPISchemaEntity): def visit(self, visitor): QAPISchemaEntity.visit(self, visitor) - visitor.visit_event(self.name, self.info, self.ifcond, - self.arg_type, self.boxed) + visitor.visit_event( + self.name, self.info, self.ifcond, self.features, + self.arg_type, self.boxed) class QAPISchema(object): @@ -860,7 +862,7 @@ 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, [], None, []) + 'q_empty', None, None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', @@ -868,10 +870,11 @@ class QAPISchema(object): qtype_values = self._make_enum_members( [{'name': n} for n in qtypes], None) - self._def_entity(QAPISchemaEnumType('QType', None, None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, None, qtype_values, 'QTYPE')) - def _make_features(self, features, info): + def _make_features(self, expr, info): + features = expr.get('features', []) return [QAPISchemaFeature(f['name'], info, f.get('if')) for f in features] @@ -883,7 +886,8 @@ class QAPISchema(object): # See also QAPISchemaObjectTypeMember.describe() name = name + 'Kind' # reserved by check_defn_name_str() self._def_entity(QAPISchemaEnumType( - name, info, None, ifcond, self._make_enum_members(values, info), + name, info, None, ifcond, None, + self._make_enum_members(values, info), None)) return name @@ -911,8 +915,8 @@ class QAPISchema(object): # TODO kill simple unions or implement the disjunction assert (ifcond or []) == typ._ifcond # pylint: disable=protected-access else: - self._def_entity(QAPISchemaObjectType(name, info, None, ifcond, - None, members, None, [])) + self._def_entity(QAPISchemaObjectType( + name, info, None, ifcond, None, None, members, None)) return name def _def_enum_type(self, expr, info, doc): @@ -920,8 +924,9 @@ class QAPISchema(object): data = expr['data'] prefix = expr.get('prefix') ifcond = expr.get('if') + features = self._make_features(expr, info) self._def_entity(QAPISchemaEnumType( - name, info, doc, ifcond, + name, info, doc, ifcond, features, self._make_enum_members(data, info), prefix)) def _make_member(self, name, typ, ifcond, info): @@ -943,12 +948,11 @@ class QAPISchema(object): base = expr.get('base') data = expr['data'] ifcond = expr.get('if') - features = expr.get('features', []) + features = self._make_features(expr, info) self._def_entity(QAPISchemaObjectType( - name, info, doc, ifcond, base, + name, info, doc, ifcond, features, base, self._make_members(data, info), - None, - self._make_features(features, info))) + None)) def _make_variant(self, case, typ, ifcond, info): return QAPISchemaObjectTypeVariant(case, info, typ, ifcond) @@ -967,6 +971,7 @@ class QAPISchema(object): data = expr['data'] base = expr.get('base') ifcond = expr.get('if') + features = self._make_features(expr, info) tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): @@ -987,21 +992,22 @@ class QAPISchema(object): tag_member = QAPISchemaObjectTypeMember('type', info, typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, ifcond, base, members, + QAPISchemaObjectType(name, info, doc, ifcond, features, + base, members, QAPISchemaObjectTypeVariants( - tag_name, info, tag_member, variants), - [])) + tag_name, info, tag_member, variants))) def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') + features = self._make_features(expr, info) variants = [self._make_variant(key, value['type'], value.get('if'), info) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, ifcond, + QAPISchemaAlternateType(name, info, doc, ifcond, features, QAPISchemaObjectTypeVariants( None, info, tag_member, variants))) @@ -1015,27 +1021,31 @@ 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', []) + features = self._make_features(expr, info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) + name, info, 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, ifcond, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, features, + data, rets, gen, success_response, - boxed, allow_oob, allow_preconfig, - self._make_features(features, info))) + boxed, allow_oob, allow_preconfig)) def _def_event(self, expr, info, doc): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) ifcond = expr.get('if') + features = self._make_features(expr, info) if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, ifcond, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) + name, info, ifcond, + 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, features, + data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index d8751daa04..2a108b6911 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -277,7 +277,7 @@ 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, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.preamble_add(gen_enum(name, members, prefix)) self._genc.add(gen_enum_lookup(name, members, prefix)) @@ -305,7 +305,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): # implicit types won't be directly allocated/freed self._gen_type_cleanup(name) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh): self._genh.preamble_add(gen_fwd_object_or_array(name)) self._genh.add(gen_object(name, ifcond, None, diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index c72f2bc5c0..b21e1340a2 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -316,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) @@ -342,7 +342,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_object(name, base, members, variants)) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) self._genc.add(gen_visit_alternate(name, variants)) diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 31ebe56bbf..970a08ab26 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1,3 +1,3 @@ alternate-base.json: In alternate 'Alt': alternate-base.json:4: alternate has unknown key 'base' -Valid keys are 'alternate', 'data', 'if'. +Valid keys are 'alternate', 'data', 'features', 'if'. diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index d992e713d9..01c930c474 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -52,11 +52,14 @@ ## # @Enum: # @one: The _one_ {and only} -# # @two is undocumented +# +# Features: +# @enum-feat: Also _one_ {and only} ## { 'enum': 'Enum', 'data': [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + 'features': [ 'enum-feat' ], 'if': 'defined(IFCOND)' } ## @@ -86,24 +89,34 @@ ## # @Object: +# Features: +# @union-feat1: a feature ## { 'union': 'Object', + 'features': [ 'union-feat1' ], 'base': 'Base', 'discriminator': 'base1', 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @SugaredUnion: +# Features: +# @union-feat2: a feature ## { 'union': 'SugaredUnion', + 'features': [ 'union-feat2' ], 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @Alternate: # @i: an integer # @b is undocumented +# +# Features: +# @alt-feat: a feature ## { 'alternate': 'Alternate', + 'features': [ 'alt-feat' ], 'data': { 'i': 'int', 'b': 'bool' } } ## @@ -160,6 +173,9 @@ ## # @EVT-BOXED: +# Features: +# @feat3: a feature ## { 'event': 'EVT-BOXED', 'boxed': true, + 'features': [ 'feat3' ], 'data': 'Object' } diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 4c9406a464..f5d9dc969c 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -15,6 +15,7 @@ enum Enum if ['defined(IFONE)'] member two if ['defined(IFCOND)'] + feature enum-feat object Base member base1: Enum optional=False object Variant1 @@ -28,6 +29,7 @@ object Object case one: Variant1 case two: Variant2 if ['IFTWO'] + feature union-feat1 object q_obj_Variant1-wrapper member data: Variant1 optional=False object q_obj_Variant2-wrapper @@ -42,10 +44,12 @@ object SugaredUnion case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper if ['IFTWO'] + feature union-feat2 alternate Alternate tag type case i: int case b: bool + feature alt-feat object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True @@ -60,6 +64,7 @@ command cmd-boxed Object -> None feature cmd-feat2 event EVT-BOXED Object boxed=True + feature feat3 doc freeform body= = Section @@ -110,10 +115,11 @@ doc symbol=Enum arg=one The _one_ {and only} - arg=two - - section=None @two is undocumented + arg=two + + feature=enum-feat +Also _one_ {and only} doc symbol=Base body= @@ -134,11 +140,15 @@ doc symbol=Variant2 doc symbol=Object body= + feature=union-feat1 +a feature doc symbol=SugaredUnion body= arg=type + feature=union-feat2 +a feature doc symbol=Alternate body= @@ -147,6 +157,8 @@ an integer @b is undocumented arg=b + feature=alt-feat +a feature doc freeform body= == Another subsection @@ -197,3 +209,5 @@ another feature doc symbol=EVT-BOXED body= + feature=feat3 +a feature diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 9abf175fe0..fa4f3a15da 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -252,7 +252,7 @@ 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } -# test 'features' for structs +# test 'features' { 'struct': 'FeatureStruct0', 'data': { 'foo': 'int' }, @@ -281,7 +281,22 @@ 'data': { 'foo': 'int' }, 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } -{ 'command': 'test-features', + +{ 'enum': 'FeatureEnum1', + 'data': [ 'eins', 'zwei', 'drei' ], + 'features': [ 'feature1' ] } + +{ 'union': 'FeatureUnion1', + 'base': { 'tag': 'FeatureEnum1' }, + 'discriminator': 'tag', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'alternate': 'FeatureAlternate1', + 'data': { 'eins': 'FeatureStruct1' }, + 'features': [ 'feature1' ] } + +{ 'command': 'test-features0', 'data': { 'fs0': 'FeatureStruct0', 'fs1': 'FeatureStruct1', 'fs2': 'FeatureStruct2', @@ -289,12 +304,9 @@ 'fs4': 'FeatureStruct4', 'cfs1': 'CondFeatureStruct1', 'cfs2': 'CondFeatureStruct2', - 'cfs3': 'CondFeatureStruct3' } } - -# test 'features' for command - -{ 'command': 'test-command-features0', + 'cfs3': 'CondFeatureStruct3' }, 'features': [] } + { 'command': 'test-command-features1', 'features': [ 'feature1' ] } { 'command': 'test-command-features3', @@ -308,3 +320,6 @@ { 'command': 'test-command-cond-features3', 'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] } ] } + +{ 'event': 'TEST-EVENT-FEATURES1', + 'features': [ 'feature1' ] } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3660e75a48..1ece836d9b 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -401,7 +401,25 @@ object CondFeatureStruct3 member foo: int optional=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] -object q_obj_test-features-arg +enum FeatureEnum1 + member eins + member zwei + member drei + feature feature1 +object q_obj_FeatureUnion1-base + member tag: FeatureEnum1 optional=False +object FeatureUnion1 + base q_obj_FeatureUnion1-base + tag tag + case eins: FeatureStruct1 + case zwei: q_empty + case drei: q_empty + feature feature1 +alternate FeatureAlternate1 + tag type + case eins: FeatureStruct1 + feature feature1 +object q_obj_test-features0-arg member fs0: FeatureStruct0 optional=False member fs1: FeatureStruct1 optional=False member fs2: FeatureStruct2 optional=False @@ -410,9 +428,7 @@ object q_obj_test-features-arg member cfs1: CondFeatureStruct1 optional=False member cfs2: CondFeatureStruct2 optional=False member cfs3: CondFeatureStruct3 optional=False -command test-features q_obj_test-features-arg -> None - gen=True success_response=True boxed=False oob=False preconfig=False -command test-command-features0 None -> None +command test-features0 q_obj_test-features0-arg -> None gen=True success_response=True boxed=False oob=False preconfig=False command test-command-features1 None -> None gen=True success_response=True boxed=False oob=False preconfig=False @@ -435,3 +451,6 @@ command test-command-cond-features3 None -> None gen=True success_response=True boxed=False oob=False preconfig=False feature feature1 if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)'] +event TEST-EVENT-FEATURES1 None + boxed=False + feature feature1 diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index bad14edb47..078fc63f97 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -35,7 +35,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, ifcond, members, prefix): + def visit_enum_type(self, name, info, ifcond, features, members, prefix): print('enum %s' % name) if prefix: print(' prefix %s' % prefix) @@ -43,6 +43,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' member %s' % m.name) self._print_if(m.ifcond, indent=8) self._print_if(ifcond) + self._print_features(features) def visit_array_type(self, name, info, ifcond, element_type): if not info: @@ -63,10 +64,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_alternate_type(self, name, info, ifcond, variants): + def visit_alternate_type(self, name, info, ifcond, features, variants): print('alternate %s' % name) self._print_variants(variants) self._print_if(ifcond) + self._print_features(features) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, @@ -79,10 +81,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): self._print_if(ifcond) self._print_features(features) - def visit_event(self, name, info, ifcond, arg_type, boxed): + def visit_event(self, name, info, ifcond, features, arg_type, boxed): print('event %s %s' % (name, arg_type and arg_type.name)) print(' boxed=%s' % boxed) self._print_if(ifcond) + self._print_features(features) @staticmethod def _print_variants(variants): -- 2.21.0 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list