<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Oct 25, 2021 at 12:24 AM Markus Armbruster <<a href="mailto:armbru@redhat.com">armbru@redhat.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">This is quite similar to commit 84ab008687 "qapi: Add feature flags to<br>
struct members", only for enums instead of structs.<br>
<br>
Special feature flag 'deprecated' is silently ignored there. This is<br>
okay only because it will be implemented shortly.<br>
<br>
Signed-off-by: Markus Armbruster <<a href="mailto:armbru@redhat.com" target="_blank">armbru@redhat.com</a>><br>
Reviewed-by: Eric Blake <<a href="mailto:eblake@redhat.com" target="_blank">eblake@redhat.com</a>><br></blockquote><div><br></div><div>Reviewed-by: John Snow <<a href="mailto:jsnow@redhat.com">jsnow@redhat.com</a>><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
---<br>
docs/devel/qapi-code-gen.rst | 16 +++++++++-----<br>
qapi/compat.json | 2 ++<br>
qapi/introspect.json | 5 ++++-<br>
scripts/qapi/expr.py | 3 ++-<br>
scripts/qapi/introspect.py | 5 +++--<br>
scripts/qapi/schema.py | 22 +++++++++++++++++--<br>
tests/qapi-schema/doc-good.json | 5 ++++-<br>
tests/qapi-schema/doc-good.out | 3 +++<br>
tests/qapi-schema/doc-good.txt | 3 +++<br>
.../qapi-schema/enum-dict-member-unknown.err | 2 +-<br>
tests/qapi-schema/qapi-schema-test.json | 3 ++-<br>
tests/qapi-schema/qapi-schema-test.out | 1 +<br>
tests/qapi-schema/test-qapi.py | 1 +<br>
13 files changed, 57 insertions(+), 14 deletions(-)<br>
<br>
diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst<br>
index d267889d2c..4071c9074a 100644<br>
--- a/docs/devel/qapi-code-gen.rst<br>
+++ b/docs/devel/qapi-code-gen.rst<br>
@@ -200,7 +200,9 @@ Syntax::<br>
'*if': COND,<br>
'*features': FEATURES }<br>
ENUM-VALUE = STRING<br>
- | { 'name': STRING, '*if': COND }<br>
+ | { 'name': STRING,<br>
+ '*if': COND,<br>
+ '*features': FEATURES }<br>
<br>
Member 'enum' names the enum type.<br>
<br>
@@ -706,8 +708,10 @@ QEMU shows a certain behaviour.<br>
Special features<br>
~~~~~~~~~~~~~~~~<br>
<br>
-Feature "deprecated" marks a command, event, or struct member as<br>
-deprecated. It is not supported elsewhere so far.<br>
+Feature "deprecated" marks a command, event, enum value, or struct<br>
+member as deprecated. It is not supported elsewhere so far.<br>
+Interfaces so marked may be withdrawn in future releases in accordance<br>
+with QEMU's deprecation policy.<br>
<br>
<br>
Naming rules and reserved names<br>
@@ -1157,7 +1161,8 @@ and "variants".<br>
<br>
"members" is a JSON array describing the object's common members, if<br>
any. Each element is a JSON object with members "name" (the member's<br>
-name), "type" (the name of its type), and optionally "default". The<br>
+name), "type" (the name of its type), "features" (a JSON array of<br>
+feature strings), and "default". The latter two are optional. The<br>
member is optional if "default" is present. Currently, "default" can<br>
only have value null. Other values are reserved for future<br>
extensions. The "members" array is in no particular order; clients<br>
@@ -1234,7 +1239,8 @@ The SchemaInfo for an enumeration type has meta-type "enum" and<br>
variant member "members".<br>
<br>
"members" is a JSON array describing the enumeration values. Each<br>
-element is a JSON object with member "name" (the member's name). The<br>
+element is a JSON object with member "name" (the member's name), and<br>
+optionally "features" (a JSON array of feature strings). The<br>
"members" array is in no particular order; clients must search the<br>
entire array when learning whether a particular value is supported.<br>
<br>
diff --git a/qapi/compat.json b/qapi/compat.json<br>
index ae3afc22df..1d2b76f00c 100644<br>
--- a/qapi/compat.json<br>
+++ b/qapi/compat.json<br>
@@ -42,6 +42,8 @@<br>
# with feature 'deprecated'. We may want to extend it to cover<br>
# semantic aspects, CLI, and experimental features.<br>
#<br>
+# Limitation: not implemented for deprecated enumeration values.<br>
+#<br>
# @deprecated-input: how to handle deprecated input (default 'accept')<br>
# @deprecated-output: how to handle deprecated output (default 'accept')<br>
#<br>
diff --git a/qapi/introspect.json b/qapi/introspect.json<br>
index 9683e884f8..183148b2e9 100644<br>
--- a/qapi/introspect.json<br>
+++ b/qapi/introspect.json<br>
@@ -167,10 +167,13 @@<br>
#<br>
# @name: the member's name, as defined in the QAPI schema.<br>
#<br>
+# @features: names of features associated with the member, in no<br>
+# particular order.<br>
+#<br>
# Since: 6.2<br>
##<br>
{ 'struct': 'SchemaInfoEnumMember',<br>
- 'data': { 'name': 'str' } }<br>
+ 'data': { 'name': 'str', '*features': [ 'str' ] } }<br>
<br>
##<br>
# @SchemaInfoArray:<br>
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py<br>
index 819ea6ad97..3cb389e875 100644<br>
--- a/scripts/qapi/expr.py<br>
+++ b/scripts/qapi/expr.py<br>
@@ -472,7 +472,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:<br>
for m in members]<br>
for member in members:<br>
source = "'data' member"<br>
- check_keys(member, info, source, ['name'], ['if'])<br>
+ check_keys(member, info, source, ['name'], ['if', 'features'])<br>
member_name = member['name']<br>
check_name_is_str(member_name, info, source)<br>
source = "%s '%s'" % (source, member_name)<br>
@@ -483,6 +483,7 @@ def check_enum(expr: _JSONObject, info: QAPISourceInfo) -> None:<br>
permit_upper=permissive,<br>
permit_underscore=permissive)<br>
check_if(member, info, source)<br>
+ check_features(member.get('features'), info)<br>
<br>
<br>
def check_struct(expr: _JSONObject, info: QAPISourceInfo) -> None:<br>
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py<br>
index 6334546363..67c7d89aae 100644<br>
--- a/scripts/qapi/introspect.py<br>
+++ b/scripts/qapi/introspect.py<br>
@@ -275,12 +275,13 @@ def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object],<br>
obj['features'] = self._gen_features(features)<br>
self._trees.append(Annotated(obj, ifcond, comment))<br>
<br>
- @staticmethod<br>
- def _gen_enum_member(member: QAPISchemaEnumMember<br>
+ def _gen_enum_member(self, member: QAPISchemaEnumMember<br>
) -> Annotated[SchemaInfoEnumMember]:<br>
obj: SchemaInfoEnumMember = {<br>
'name': <a href="http://member.name" rel="noreferrer" target="_blank">member.name</a>,<br>
}<br>
+ if member.features:<br>
+ obj['features'] = self._gen_features(member.features)<br>
return Annotated(obj, member.ifcond)<br>
<br>
def _gen_object_member(self, member: QAPISchemaObjectTypeMember<br>
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py<br>
index 004d7095ff..6d5f46509a 100644<br>
--- a/scripts/qapi/schema.py<br>
+++ b/scripts/qapi/schema.py<br>
@@ -708,6 +708,19 @@ def describe(self, info):<br>
class QAPISchemaEnumMember(QAPISchemaMember):<br>
role = 'value'<br>
<br>
+ def __init__(self, name, info, ifcond=None, features=None):<br>
+ super().__init__(name, info, ifcond)<br>
+ for f in features or []:<br>
+ assert isinstance(f, QAPISchemaFeature)<br>
+ f.set_defined_in(name)<br>
+ self.features = features or []<br>
+<br>
+ def connect_doc(self, doc):<br>
+ super().connect_doc(doc)<br>
+ if doc:<br>
+ for f in self.features:<br>
+ doc.connect_feature(f)<br>
+<br>
<br>
class QAPISchemaFeature(QAPISchemaMember):<br>
role = 'feature'<br>
@@ -980,9 +993,14 @@ def _make_features(self, features, info):<br>
QAPISchemaIfCond(f.get('if')))<br>
for f in features]<br>
<br>
+ def _make_enum_member(self, name, ifcond, features, info):<br>
+ return QAPISchemaEnumMember(name, info,<br>
+ QAPISchemaIfCond(ifcond),<br>
+ self._make_features(features, info))<br>
+<br>
def _make_enum_members(self, values, info):<br>
- return [QAPISchemaEnumMember(v['name'], info,<br>
- QAPISchemaIfCond(v.get('if')))<br>
+ return [self._make_enum_member(v['name'], v.get('if'),<br>
+ v.get('features'), info)<br>
for v in values]<br>
<br>
def _make_array_type(self, element_type, info):<br>
diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json<br>
index 86dc25d2bd..74745fb405 100644<br>
--- a/tests/qapi-schema/doc-good.json<br>
+++ b/tests/qapi-schema/doc-good.json<br>
@@ -58,11 +58,14 @@<br>
#<br>
# Features:<br>
# @enum-feat: Also _one_ {and only}<br>
+# @enum-member-feat: a member feature<br>
#<br>
# @two is undocumented<br>
##<br>
{ 'enum': 'Enum',<br>
- 'data': [ { 'name': 'one', 'if': 'IFONE' }, 'two' ],<br>
+ 'data': [ { 'name': 'one', 'if': 'IFONE',<br>
+ 'features': [ 'enum-member-feat' ] },<br>
+ 'two' ],<br>
'features': [ 'enum-feat' ],<br>
'if': 'IFCOND' }<br>
<br>
diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out<br>
index 5a324e2627..9dd65b9d92 100644<br>
--- a/tests/qapi-schema/doc-good.out<br>
+++ b/tests/qapi-schema/doc-good.out<br>
@@ -13,6 +13,7 @@ module doc-good.json<br>
enum Enum<br>
member one<br>
if IFONE<br>
+ feature enum-member-feat<br>
member two<br>
if IFCOND<br>
feature enum-feat<br>
@@ -108,6 +109,8 @@ The _one_ {and only}<br>
<br>
feature=enum-feat<br>
Also _one_ {and only}<br>
+ feature=enum-member-feat<br>
+a member feature<br>
section=None<br>
@two is undocumented<br>
doc symbol=Base<br>
diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt<br>
index 701402ee5e..b3b76bd43f 100644<br>
--- a/tests/qapi-schema/doc-good.txt<br>
+++ b/tests/qapi-schema/doc-good.txt<br>
@@ -56,6 +56,9 @@ Features<br>
"enum-feat"<br>
Also _one_ {and only}<br>
<br>
+"enum-member-feat"<br>
+ a member feature<br>
+<br>
"two" is undocumented<br>
<br>
<br>
diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err<br>
index f8617ea179..235cde0c49 100644<br>
--- a/tests/qapi-schema/enum-dict-member-unknown.err<br>
+++ b/tests/qapi-schema/enum-dict-member-unknown.err<br>
@@ -1,3 +1,3 @@<br>
enum-dict-member-unknown.json: In enum 'MyEnum':<br>
enum-dict-member-unknown.json:2: 'data' member has unknown key 'bad-key'<br>
-Valid keys are 'if', 'name'.<br>
+Valid keys are 'features', 'if', 'name'.<br>
diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json<br>
index 2ec50109cb..b677ab861d 100644<br>
--- a/tests/qapi-schema/qapi-schema-test.json<br>
+++ b/tests/qapi-schema/qapi-schema-test.json<br>
@@ -301,7 +301,8 @@<br>
'TEST_IF_COND_2'] } } ] }<br>
<br>
{ 'enum': 'FeatureEnum1',<br>
- 'data': [ 'eins', 'zwei', 'drei' ],<br>
+ 'data': [ 'eins', 'zwei',<br>
+ { 'name': 'drei', 'features': [ 'deprecated' ] } ],<br>
'features': [ 'feature1' ] }<br>
<br>
{ 'union': 'FeatureUnion1',<br>
diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out<br>
index 9337adc9ea..16846dbeb8 100644<br>
--- a/tests/qapi-schema/qapi-schema-test.out<br>
+++ b/tests/qapi-schema/qapi-schema-test.out<br>
@@ -341,6 +341,7 @@ enum FeatureEnum1<br>
member eins<br>
member zwei<br>
member drei<br>
+ feature deprecated<br>
feature feature1<br>
object q_obj_FeatureUnion1-base<br>
member tag: FeatureEnum1 optional=False<br>
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py<br>
index c717a7a90b..2160cef082 100755<br>
--- a/tests/qapi-schema/test-qapi.py<br>
+++ b/tests/qapi-schema/test-qapi.py<br>
@@ -37,6 +37,7 @@ def visit_enum_type(self, name, info, ifcond, features, members, prefix):<br>
for m in members:<br>
print(' member %s' % <a href="http://m.name" rel="noreferrer" target="_blank">m.name</a>)<br>
self._print_if(m.ifcond, indent=8)<br>
+ self._print_features(m.features, indent=8)<br>
self._print_if(ifcond)<br>
self._print_features(features)<br>
<br>
-- <br>
2.31.1<br>
<br>
</blockquote></div></div>