... | ... | ||
---|---|---|---|
13 | regardless of whether they are special, and don't require any pragma. | 13 | regardless of whether they are special, and don't require any pragma. |
14 | 14 | ||
15 | I've split it from the QGA patches since it makes more sense to work | 15 | I've split it from the QGA patches since it makes more sense to work |
16 | on this bit in isolation. | 16 | on this bit in isolation. |
17 | 17 | ||
18 | Daniel P. Berrangé (6): | 18 | Changed in v3: |
19 | |||
20 | * "make -C python check-tox" is now clean | ||
21 | * Added test case for "too many features" error scenario | ||
22 | * Don't sort the features enum, just rely on ordered dict | ||
23 | * Add 'features' accessor on QAPISchema instead of touching | ||
24 | private data via 'schema._features.values()' | ||
25 | |||
26 | Changed in v2: | ||
27 | |||
28 | * Reference QapiSpecialFeature enums constants when defining | ||
29 | QapiFeature enums constants for deprecated & unstable | ||
30 | |||
31 | * Don't expose qapi-features.h in qapi-types.h, to avoid | ||
32 | global namespace pollution, instead pull it into only the | ||
33 | .c files that need it. This avoids the need to add a 'prefix' | ||
34 | to enum constants | ||
35 | |||
36 | * Collect all features during parse time, rather than | ||
37 | generate time, to allow earlier error reporting of the | ||
38 | 64 feature limit | ||
39 | |||
40 | Daniel P. Berrangé (4): | ||
41 | qapi: cope with feature names containing a '-' | ||
19 | qapi: change 'unsigned special_features' to 'uint64_t features' | 42 | qapi: change 'unsigned special_features' to 'uint64_t features' |
20 | scripts/qapi: rename 'special_features' to 'features' | 43 | qapi: rename 'special_features' to 'features' |
21 | qapi: use "QAPI_FEATURE" as namespace for features | ||
22 | qapi: cope with feature names containing a '-' | ||
23 | qapi: apply schema prefix to QAPI feature enum constants | ||
24 | qapi: expose all schema features to code | 44 | qapi: expose all schema features to code |
25 | 45 | ||
26 | include/qapi/compat-policy.h | 2 +- | 46 | include/qapi/compat-policy.h | 2 +- |
27 | include/qapi/qmp/dispatch.h | 4 +- | 47 | include/qapi/qmp/dispatch.h | 4 +- |
28 | include/qapi/util.h | 7 +- | 48 | include/qapi/util.h | 2 +- |
29 | include/qapi/visitor-impl.h | 4 +- | 49 | include/qapi/visitor-impl.h | 4 +- |
30 | include/qapi/visitor.h | 12 +-- | 50 | include/qapi/visitor.h | 12 +++--- |
31 | meson.build | 1 + | 51 | meson.build | 1 + |
32 | qapi/qapi-forward-visitor.c | 8 +- | 52 | qapi/qapi-forward-visitor.c | 8 ++-- |
33 | qapi/qapi-util.c | 6 +- | 53 | qapi/qapi-util.c | 6 +-- |
34 | qapi/qapi-visit-core.c | 12 +-- | 54 | qapi/qapi-visit-core.c | 12 +++--- |
35 | qapi/qmp-dispatch.c | 2 +- | 55 | qapi/qmp-dispatch.c | 2 +- |
36 | qapi/qmp-registry.c | 4 +- | 56 | qapi/qmp-registry.c | 4 +- |
37 | qapi/qobject-input-visitor.c | 4 +- | 57 | qapi/qobject-input-visitor.c | 4 +- |
38 | qapi/qobject-output-visitor.c | 6 +- | 58 | qapi/qobject-output-visitor.c | 6 +-- |
39 | scripts/qapi/commands.py | 9 ++- | 59 | scripts/qapi/commands.py | 5 ++- |
40 | scripts/qapi/events.py | 3 +- | 60 | scripts/qapi/features.py | 51 ++++++++++++++++++++++++ |
41 | scripts/qapi/features.py | 134 ++++++++++++++++++++++++++++++++++ | 61 | scripts/qapi/gen.py | 9 +++-- |
42 | scripts/qapi/gen.py | 9 ++- | 62 | scripts/qapi/main.py | 2 + |
43 | scripts/qapi/main.py | 2 + | 63 | scripts/qapi/schema.py | 30 +++++++++++++- |
44 | scripts/qapi/schema.py | 5 +- | 64 | scripts/qapi/types.py | 19 +++++---- |
45 | scripts/qapi/types.py | 19 +++-- | 65 | scripts/qapi/visit.py | 17 ++++---- |
46 | scripts/qapi/visit.py | 17 +++-- | 66 | tests/meson.build | 2 + |
47 | 21 files changed, 206 insertions(+), 64 deletions(-) | 67 | tests/qapi-schema/features-too-many.err | 2 + |
68 | tests/qapi-schema/features-too-many.json | 13 ++++++ | ||
69 | tests/qapi-schema/features-too-many.out | 0 | ||
70 | tests/qapi-schema/meson.build | 1 + | ||
71 | 25 files changed, 162 insertions(+), 56 deletions(-) | ||
48 | create mode 100644 scripts/qapi/features.py | 72 | create mode 100644 scripts/qapi/features.py |
73 | create mode 100644 tests/qapi-schema/features-too-many.err | ||
74 | create mode 100644 tests/qapi-schema/features-too-many.json | ||
75 | create mode 100644 tests/qapi-schema/features-too-many.out | ||
49 | 76 | ||
50 | -- | 77 | -- |
51 | 2.45.2 | 78 | 2.46.0 |
52 | 79 | ||
53 | 80 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
19 | c_name, | 19 | c_name, |
20 | guardend, | 20 | guardend, |
21 | @@ -XXX,XX +XXX,XX @@ | 21 | @@ -XXX,XX +XXX,XX @@ |
22 | 22 | ||
23 | 23 | ||
24 | def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | 24 | def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: |
25 | - features = [f"1u << QAPI_FEATURE_{feat.name.upper()}" | 25 | - special_features = [f"1u << QAPI_{feat.name.upper()}" |
26 | + features = [f"1u << {c_enum_const('QAPI_FEATURE', feat.name)}" | 26 | + special_features = [f"1u << {c_enum_const('qapi', feat.name)}" |
27 | for feat in features if feat.is_special()] | 27 | for feat in features if feat.is_special()] |
28 | return ' | '.join(features) or '0' | 28 | return ' | '.join(special_features) or '0' |
29 | 29 | ||
30 | -- | 30 | -- |
31 | 2.45.2 | 31 | 2.46.0 |
32 | 32 | ||
33 | 33 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
4 | 4 | ||
5 | This special casing of internal features is going to be removed, so | 5 | This special casing of internal features is going to be removed, so |
6 | prepare for that by renaming to 'features'. Using a fixed size type | 6 | prepare for that by renaming to 'features'. Using a fixed size type |
7 | is also best practice for bit fields. | 7 | is also best practice for bit fields. |
8 | 8 | ||
9 | Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> | ||
9 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | 10 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> |
10 | --- | 11 | --- |
11 | include/qapi/compat-policy.h | 2 +- | 12 | include/qapi/compat-policy.h | 2 +- |
12 | include/qapi/qmp/dispatch.h | 4 ++-- | 13 | include/qapi/qmp/dispatch.h | 4 ++-- |
13 | include/qapi/util.h | 4 ++-- | 14 | include/qapi/util.h | 2 +- |
14 | include/qapi/visitor-impl.h | 4 ++-- | 15 | include/qapi/visitor-impl.h | 4 ++-- |
15 | include/qapi/visitor.h | 12 ++++++------ | 16 | include/qapi/visitor.h | 12 ++++++------ |
16 | qapi/qapi-forward-visitor.c | 8 ++++---- | 17 | qapi/qapi-forward-visitor.c | 8 ++++---- |
17 | qapi/qapi-util.c | 6 +++--- | 18 | qapi/qapi-util.c | 6 +++--- |
18 | qapi/qapi-visit-core.c | 12 ++++++------ | 19 | qapi/qapi-visit-core.c | 12 ++++++------ |
19 | qapi/qmp-dispatch.c | 2 +- | 20 | qapi/qmp-dispatch.c | 2 +- |
20 | qapi/qmp-registry.c | 4 ++-- | 21 | qapi/qmp-registry.c | 4 ++-- |
21 | qapi/qobject-input-visitor.c | 4 ++-- | 22 | qapi/qobject-input-visitor.c | 4 ++-- |
22 | qapi/qobject-output-visitor.c | 6 +++--- | 23 | qapi/qobject-output-visitor.c | 6 +++--- |
23 | scripts/qapi/types.py | 2 +- | 24 | scripts/qapi/types.py | 2 +- |
24 | 13 files changed, 35 insertions(+), 35 deletions(-) | 25 | 13 files changed, 34 insertions(+), 34 deletions(-) |
25 | 26 | ||
26 | diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h | 27 | diff --git a/include/qapi/compat-policy.h b/include/qapi/compat-policy.h |
27 | index XXXXXXX..XXXXXXX 100644 | 28 | index XXXXXXX..XXXXXXX 100644 |
28 | --- a/include/qapi/compat-policy.h | 29 | --- a/include/qapi/compat-policy.h |
29 | +++ b/include/qapi/compat-policy.h | 30 | +++ b/include/qapi/compat-policy.h |
... | ... | ||
60 | void qmp_disable_command(QmpCommandList *cmds, const char *name, | 61 | void qmp_disable_command(QmpCommandList *cmds, const char *name, |
61 | diff --git a/include/qapi/util.h b/include/qapi/util.h | 62 | diff --git a/include/qapi/util.h b/include/qapi/util.h |
62 | index XXXXXXX..XXXXXXX 100644 | 63 | index XXXXXXX..XXXXXXX 100644 |
63 | --- a/include/qapi/util.h | 64 | --- a/include/qapi/util.h |
64 | +++ b/include/qapi/util.h | 65 | +++ b/include/qapi/util.h |
65 | @@ -XXX,XX +XXX,XX @@ | 66 | @@ -XXX,XX +XXX,XX @@ typedef enum { |
66 | typedef enum { | ||
67 | QAPI_DEPRECATED, | ||
68 | QAPI_UNSTABLE, | ||
69 | -} QapiSpecialFeature; | ||
70 | +} QapiFeature; | ||
71 | 67 | ||
72 | typedef struct QEnumLookup { | 68 | typedef struct QEnumLookup { |
73 | const char *const *array; | 69 | const char *const *array; |
74 | - const unsigned char *const special_features; | 70 | - const unsigned char *const special_features; |
75 | + const uint64_t *const features; | 71 | + const uint64_t *const features; |
... | ... | ||
322 | + .features = (const uint64_t[%(max_index)s]) { | 318 | + .features = (const uint64_t[%(max_index)s]) { |
323 | ''', | 319 | ''', |
324 | max_index=max_index) | 320 | max_index=max_index) |
325 | ret += feats | 321 | ret += feats |
326 | -- | 322 | -- |
327 | 2.45.2 | 323 | 2.46.0 |
328 | 324 | ||
329 | 325 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
38 | @@ -XXX,XX +XXX,XX @@ | 38 | @@ -XXX,XX +XXX,XX @@ |
39 | from .source import QAPISourceInfo | 39 | from .source import QAPISourceInfo |
40 | 40 | ||
41 | 41 | ||
42 | -def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: | 42 | -def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: |
43 | - special_features = [f"1u << QAPI_{feat.name.upper()}" | 43 | - special_features = [f"1u << {c_enum_const('qapi', feat.name)}" |
44 | - for feat in features if feat.is_special()] | 44 | - for feat in features if feat.is_special()] |
45 | - return ' | '.join(special_features) or '0' | 45 | - return ' | '.join(special_features) or '0' |
46 | +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | 46 | +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: |
47 | + features = [f"1u << QAPI_{feat.name.upper()}" | 47 | + featenum = [f"1u << {c_enum_const('qapi', feat.name)}" |
48 | + for feat in features if feat.is_special()] | 48 | + for feat in features if feat.is_special()] |
49 | + return ' | '.join(features) or '0' | 49 | + return ' | '.join(featenum) or '0' |
50 | 50 | ||
51 | 51 | ||
52 | class QAPIGen: | 52 | class QAPIGen: |
53 | diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py | 53 | diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py |
54 | index XXXXXXX..XXXXXXX 100644 | 54 | index XXXXXXX..XXXXXXX 100644 |
... | ... | ||
122 | + if features != '0': | 122 | + if features != '0': |
123 | indent.decrease() | 123 | indent.decrease() |
124 | ret += mcgen(''' | 124 | ret += mcgen(''' |
125 | } | 125 | } |
126 | -- | 126 | -- |
127 | 2.45.2 | 127 | 2.46.0 |
128 | 128 | ||
129 | 129 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This more clearly distinguishes the feature constants from other | ||
2 | QAPI constants. | ||
3 | 1 | ||
4 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | ||
5 | --- | ||
6 | include/qapi/util.h | 4 ++-- | ||
7 | qapi/qapi-util.c | 4 ++-- | ||
8 | qapi/qobject-output-visitor.c | 4 ++-- | ||
9 | scripts/qapi/gen.py | 2 +- | ||
10 | 4 files changed, 7 insertions(+), 7 deletions(-) | ||
11 | |||
12 | diff --git a/include/qapi/util.h b/include/qapi/util.h | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/include/qapi/util.h | ||
15 | +++ b/include/qapi/util.h | ||
16 | @@ -XXX,XX +XXX,XX @@ | ||
17 | #define QAPI_UTIL_H | ||
18 | |||
19 | typedef enum { | ||
20 | - QAPI_DEPRECATED, | ||
21 | - QAPI_UNSTABLE, | ||
22 | + QAPI_FEATURE_DEPRECATED, | ||
23 | + QAPI_FEATURE_UNSTABLE, | ||
24 | } QapiFeature; | ||
25 | |||
26 | typedef struct QEnumLookup { | ||
27 | diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c | ||
28 | index XXXXXXX..XXXXXXX 100644 | ||
29 | --- a/qapi/qapi-util.c | ||
30 | +++ b/qapi/qapi-util.c | ||
31 | @@ -XXX,XX +XXX,XX @@ bool compat_policy_input_ok(uint64_t features, | ||
32 | const char *kind, const char *name, | ||
33 | Error **errp) | ||
34 | { | ||
35 | - if ((features & 1u << QAPI_DEPRECATED) | ||
36 | + if ((features & 1u << QAPI_FEATURE_DEPRECATED) | ||
37 | && !compat_policy_input_ok1("Deprecated", | ||
38 | policy->deprecated_input, | ||
39 | error_class, kind, name, errp)) { | ||
40 | return false; | ||
41 | } | ||
42 | - if ((features & (1u << QAPI_UNSTABLE)) | ||
43 | + if ((features & (1u << QAPI_FEATURE_UNSTABLE)) | ||
44 | && !compat_policy_input_ok1("Unstable", | ||
45 | policy->unstable_input, | ||
46 | error_class, kind, name, errp)) { | ||
47 | diff --git a/qapi/qobject-output-visitor.c b/qapi/qobject-output-visitor.c | ||
48 | index XXXXXXX..XXXXXXX 100644 | ||
49 | --- a/qapi/qobject-output-visitor.c | ||
50 | +++ b/qapi/qobject-output-visitor.c | ||
51 | @@ -XXX,XX +XXX,XX @@ static bool qobject_output_policy_skip(Visitor *v, const char *name, | ||
52 | { | ||
53 | CompatPolicy *pol = &v->compat_policy; | ||
54 | |||
55 | - return ((features & 1u << QAPI_DEPRECATED) | ||
56 | + return ((features & 1u << QAPI_FEATURE_DEPRECATED) | ||
57 | && pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) | ||
58 | - || ((features & 1u << QAPI_UNSTABLE) | ||
59 | + || ((features & 1u << QAPI_FEATURE_UNSTABLE) | ||
60 | && pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE); | ||
61 | } | ||
62 | |||
63 | diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py | ||
64 | index XXXXXXX..XXXXXXX 100644 | ||
65 | --- a/scripts/qapi/gen.py | ||
66 | +++ b/scripts/qapi/gen.py | ||
67 | @@ -XXX,XX +XXX,XX @@ | ||
68 | |||
69 | |||
70 | def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | ||
71 | - features = [f"1u << QAPI_{feat.name.upper()}" | ||
72 | + features = [f"1u << QAPI_FEATURE_{feat.name.upper()}" | ||
73 | for feat in features if feat.is_special()] | ||
74 | return ' | '.join(features) or '0' | ||
75 | |||
76 | -- | ||
77 | 2.45.2 | ||
78 | |||
79 | diff view generated by jsdifflib |
Deleted patch | |||
---|---|---|---|
1 | This allows us to include multiple QAPI schemas in the same file. | ||
2 | 1 | ||
3 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | ||
4 | --- | ||
5 | scripts/qapi/commands.py | 7 ++++--- | ||
6 | scripts/qapi/events.py | 3 ++- | ||
7 | scripts/qapi/gen.py | 6 +++--- | ||
8 | scripts/qapi/types.py | 5 +++-- | ||
9 | scripts/qapi/visit.py | 5 +++-- | ||
10 | 5 files changed, 15 insertions(+), 11 deletions(-) | ||
11 | |||
12 | diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py | ||
13 | index XXXXXXX..XXXXXXX 100644 | ||
14 | --- a/scripts/qapi/commands.py | ||
15 | +++ b/scripts/qapi/commands.py | ||
16 | @@ -XXX,XX +XXX,XX @@ def gen_register_command(name: str, | ||
17 | success_response: bool, | ||
18 | allow_oob: bool, | ||
19 | allow_preconfig: bool, | ||
20 | - coroutine: bool) -> str: | ||
21 | + coroutine: bool, | ||
22 | + prefix: str) -> str: | ||
23 | options = [] | ||
24 | |||
25 | if not success_response: | ||
26 | @@ -XXX,XX +XXX,XX @@ def gen_register_command(name: str, | ||
27 | ''', | ||
28 | name=name, c_name=c_name(name), | ||
29 | opts=' | '.join(options) or 0, | ||
30 | - feats=gen_features(features)) | ||
31 | + feats=gen_features(prefix, features)) | ||
32 | return ret | ||
33 | |||
34 | |||
35 | @@ -XXX,XX +XXX,XX @@ def visit_command(self, | ||
36 | with ifcontext(ifcond, self._genh, self._genc): | ||
37 | self._genc.add(gen_register_command( | ||
38 | name, features, success_response, allow_oob, | ||
39 | - allow_preconfig, coroutine)) | ||
40 | + allow_preconfig, coroutine, self._prefix)) | ||
41 | |||
42 | |||
43 | def gen_commands(schema: QAPISchema, | ||
44 | diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/scripts/qapi/events.py | ||
47 | +++ b/scripts/qapi/events.py | ||
48 | @@ -XXX,XX +XXX,XX @@ def visit_end(self) -> None: | ||
49 | self._genh.add(gen_enum(self._event_enum_name, | ||
50 | self._event_enum_members)) | ||
51 | self._genc.add(gen_enum_lookup(self._event_enum_name, | ||
52 | - self._event_enum_members)) | ||
53 | + self._event_enum_members, | ||
54 | + self._prefix)) | ||
55 | self._genh.add(mcgen(''' | ||
56 | |||
57 | void %(event_emit)s(%(event_enum)s event, QDict *qdict); | ||
58 | diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py | ||
59 | index XXXXXXX..XXXXXXX 100644 | ||
60 | --- a/scripts/qapi/gen.py | ||
61 | +++ b/scripts/qapi/gen.py | ||
62 | @@ -XXX,XX +XXX,XX @@ | ||
63 | from .source import QAPISourceInfo | ||
64 | |||
65 | |||
66 | -def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | ||
67 | - features = [f"1u << {c_enum_const('QAPI_FEATURE', feat.name)}" | ||
68 | - for feat in features if feat.is_special()] | ||
69 | +def gen_features(prefix, features: Sequence[QAPISchemaFeature]) -> str: | ||
70 | + features = [f"1u << {c_enum_const(prefix + 'QAPI_FEATURE', feat.name)}" | ||
71 | + for feat in features] | ||
72 | return ' | '.join(features) or '0' | ||
73 | |||
74 | |||
75 | diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py | ||
76 | index XXXXXXX..XXXXXXX 100644 | ||
77 | --- a/scripts/qapi/types.py | ||
78 | +++ b/scripts/qapi/types.py | ||
79 | @@ -XXX,XX +XXX,XX @@ | ||
80 | |||
81 | def gen_enum_lookup(name: str, | ||
82 | members: List[QAPISchemaEnumMember], | ||
83 | + modprefix: str, | ||
84 | prefix: Optional[str] = None) -> str: | ||
85 | max_index = c_enum_const(name, '_MAX', prefix) | ||
86 | feats = '' | ||
87 | @@ -XXX,XX +XXX,XX @@ def gen_enum_lookup(name: str, | ||
88 | index=index, name=memb.name) | ||
89 | ret += memb.ifcond.gen_endif() | ||
90 | |||
91 | - features = gen_features(memb.features) | ||
92 | + features = gen_features(modprefix, memb.features) | ||
93 | if features != '0': | ||
94 | feats += mcgen(''' | ||
95 | [%(index)s] = %(features)s, | ||
96 | @@ -XXX,XX +XXX,XX @@ def visit_enum_type(self, | ||
97 | prefix: Optional[str]) -> None: | ||
98 | with ifcontext(ifcond, self._genh, self._genc): | ||
99 | self._genh.preamble_add(gen_enum(name, members, prefix)) | ||
100 | - self._genc.add(gen_enum_lookup(name, members, prefix)) | ||
101 | + self._genc.add(gen_enum_lookup(name, members, self._prefix, prefix)) | ||
102 | |||
103 | def visit_array_type(self, | ||
104 | name: str, | ||
105 | diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py | ||
106 | index XXXXXXX..XXXXXXX 100644 | ||
107 | --- a/scripts/qapi/visit.py | ||
108 | +++ b/scripts/qapi/visit.py | ||
109 | @@ -XXX,XX +XXX,XX @@ def gen_visit_members_decl(name: str) -> str: | ||
110 | |||
111 | |||
112 | def gen_visit_object_members(name: str, | ||
113 | + prefix: str, | ||
114 | base: Optional[QAPISchemaObjectType], | ||
115 | members: List[QAPISchemaObjectTypeMember], | ||
116 | branches: Optional[QAPISchemaBranches]) -> str: | ||
117 | @@ -XXX,XX +XXX,XX @@ def gen_visit_object_members(name: str, | ||
118 | ''', | ||
119 | name=memb.name, has=has) | ||
120 | indent.increase() | ||
121 | - features = gen_features(memb.features) | ||
122 | + features = gen_features(prefix, memb.features) | ||
123 | if features != '0': | ||
124 | ret += mcgen(''' | ||
125 | if (visit_policy_reject(v, "%(name)s", %(features)s, errp)) { | ||
126 | @@ -XXX,XX +XXX,XX @@ def visit_object_type(self, | ||
127 | return | ||
128 | with ifcontext(ifcond, self._genh, self._genc): | ||
129 | self._genh.add(gen_visit_members_decl(name)) | ||
130 | - self._genc.add(gen_visit_object_members(name, base, | ||
131 | + self._genc.add(gen_visit_object_members(name, self._prefix, base, | ||
132 | members, branches)) | ||
133 | # TODO Worth changing the visitor signature, so we could | ||
134 | # directly use rather than repeat type.is_implicit()? | ||
135 | -- | ||
136 | 2.45.2 | ||
137 | |||
138 | diff view generated by jsdifflib |
1 | This removed the QapiFeatures enum and auto-generates an enum which | 1 | This replaces use of the constants from the QapiSpecialFeatures |
---|---|---|---|
2 | exposes all features defined by the schema to code. | 2 | enum, with constants from the auto-generate QapiFeatures enum |
3 | in qapi-features.h | ||
3 | 4 | ||
4 | The 'deprecated' and 'unstable' features still have a little bit of | 5 | The 'deprecated' and 'unstable' features still have a little bit of |
5 | special handling, being force defined to be the 1st + 2nd features | 6 | special handling, being force defined to be the 1st + 2nd features |
6 | in the enum, regardless of whether they're used in the schema. This | 7 | in the enum, regardless of whether they're used in the schema. This |
7 | is because QAPI common code references these features. | 8 | retains compatibility with common code that references the features |
9 | via the QapiSpecialFeatures constants. | ||
8 | 10 | ||
9 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | 11 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> |
10 | --- | 12 | --- |
11 | include/qapi/util.h | 5 -- | 13 | meson.build | 1 + |
12 | meson.build | 1 + | 14 | scripts/qapi/commands.py | 1 + |
13 | scripts/qapi/features.py | 134 +++++++++++++++++++++++++++++++++++++++ | 15 | scripts/qapi/features.py | 51 ++++++++++++++++++++++++ |
14 | scripts/qapi/main.py | 2 + | 16 | scripts/qapi/gen.py | 6 +-- |
15 | scripts/qapi/schema.py | 5 +- | 17 | scripts/qapi/main.py | 2 + |
16 | scripts/qapi/types.py | 4 +- | 18 | scripts/qapi/schema.py | 30 +++++++++++++- |
17 | 6 files changed, 144 insertions(+), 7 deletions(-) | 19 | scripts/qapi/types.py | 7 +++- |
20 | scripts/qapi/visit.py | 3 +- | ||
21 | tests/meson.build | 2 + | ||
22 | tests/qapi-schema/features-too-many.err | 2 + | ||
23 | tests/qapi-schema/features-too-many.json | 13 ++++++ | ||
24 | tests/qapi-schema/features-too-many.out | 0 | ||
25 | tests/qapi-schema/meson.build | 1 + | ||
26 | 13 files changed, 112 insertions(+), 7 deletions(-) | ||
18 | create mode 100644 scripts/qapi/features.py | 27 | create mode 100644 scripts/qapi/features.py |
19 | 28 | create mode 100644 tests/qapi-schema/features-too-many.err | |
20 | diff --git a/include/qapi/util.h b/include/qapi/util.h | 29 | create mode 100644 tests/qapi-schema/features-too-many.json |
21 | index XXXXXXX..XXXXXXX 100644 | 30 | create mode 100644 tests/qapi-schema/features-too-many.out |
22 | --- a/include/qapi/util.h | 31 | |
23 | +++ b/include/qapi/util.h | ||
24 | @@ -XXX,XX +XXX,XX @@ | ||
25 | #ifndef QAPI_UTIL_H | ||
26 | #define QAPI_UTIL_H | ||
27 | |||
28 | -typedef enum { | ||
29 | - QAPI_FEATURE_DEPRECATED, | ||
30 | - QAPI_FEATURE_UNSTABLE, | ||
31 | -} QapiFeature; | ||
32 | - | ||
33 | typedef struct QEnumLookup { | ||
34 | const char *const *array; | ||
35 | const uint64_t *const features; | ||
36 | diff --git a/meson.build b/meson.build | 32 | diff --git a/meson.build b/meson.build |
37 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
38 | --- a/meson.build | 34 | --- a/meson.build |
39 | +++ b/meson.build | 35 | +++ b/meson.build |
40 | @@ -XXX,XX +XXX,XX @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', | 36 | @@ -XXX,XX +XXX,XX @@ qapi_gen_depends = [ meson.current_source_dir() / 'scripts/qapi/__init__.py', |
... | ... | ||
43 | meson.current_source_dir() / 'scripts/qapi/types.py', | 39 | meson.current_source_dir() / 'scripts/qapi/types.py', |
44 | + meson.current_source_dir() / 'scripts/qapi/features.py', | 40 | + meson.current_source_dir() / 'scripts/qapi/features.py', |
45 | meson.current_source_dir() / 'scripts/qapi/visit.py', | 41 | meson.current_source_dir() / 'scripts/qapi/visit.py', |
46 | meson.current_source_dir() / 'scripts/qapi-gen.py' | 42 | meson.current_source_dir() / 'scripts/qapi-gen.py' |
47 | ] | 43 | ] |
44 | diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/scripts/qapi/commands.py | ||
47 | +++ b/scripts/qapi/commands.py | ||
48 | @@ -XXX,XX +XXX,XX @@ def visit_begin(self, schema: QAPISchema) -> None: | ||
49 | #include "qemu/osdep.h" | ||
50 | #include "%(prefix)sqapi-commands.h" | ||
51 | #include "%(prefix)sqapi-init-commands.h" | ||
52 | +#include "%(prefix)sqapi-features.h" | ||
53 | |||
54 | void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) | ||
55 | { | ||
48 | diff --git a/scripts/qapi/features.py b/scripts/qapi/features.py | 56 | diff --git a/scripts/qapi/features.py b/scripts/qapi/features.py |
49 | new file mode 100644 | 57 | new file mode 100644 |
50 | index XXXXXXX..XXXXXXX | 58 | index XXXXXXX..XXXXXXX |
51 | --- /dev/null | 59 | --- /dev/null |
52 | +++ b/scripts/qapi/features.py | 60 | +++ b/scripts/qapi/features.py |
53 | @@ -XXX,XX +XXX,XX @@ | 61 | @@ -XXX,XX +XXX,XX @@ |
54 | +""" | 62 | +""" |
55 | +QAPI types generator | 63 | +QAPI features generator |
56 | + | 64 | + |
57 | +Copyright 2024 Red Hat | 65 | +Copyright 2024 Red Hat |
58 | + | 66 | + |
59 | +This work is licensed under the terms of the GNU GPL, version 2. | 67 | +This work is licensed under the terms of the GNU GPL, version 2. |
60 | +# See the COPYING file in the top-level directory. | 68 | +# See the COPYING file in the top-level directory. |
61 | +""" | 69 | +""" |
62 | + | 70 | + |
63 | +from typing import List, Optional | 71 | +from typing import Dict |
64 | + | 72 | + |
65 | +from .common import c_enum_const, mcgen, c_name | 73 | +from .common import c_enum_const, c_name |
66 | +from .gen import QAPISchemaMonolithicCVisitor | 74 | +from .gen import QAPISchemaMonolithicCVisitor |
67 | +from .schema import ( | 75 | +from .schema import ( |
68 | + QAPISchema, | 76 | + QAPISchema, |
69 | + QAPISchemaAlternatives, | ||
70 | + QAPISchemaBranches, | ||
71 | + QAPISchemaEntity, | ||
72 | + QAPISchemaEnumMember, | ||
73 | + QAPISchemaFeature, | 77 | + QAPISchemaFeature, |
74 | + QAPISchemaIfCond, | ||
75 | + QAPISchemaObjectType, | ||
76 | + QAPISchemaObjectTypeMember, | ||
77 | + QAPISchemaType, | ||
78 | + QAPISchemaVariants, | ||
79 | +) | 78 | +) |
80 | +from .source import QAPISourceInfo | ||
81 | + | 79 | + |
82 | + | 80 | + |
83 | +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): | 81 | +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): |
84 | + | 82 | + |
85 | + def __init__(self, prefix: str): | 83 | + def __init__(self, prefix: str): |
86 | + super().__init__( | 84 | + super().__init__( |
87 | + prefix, 'qapi-features', | 85 | + prefix, 'qapi-features', |
88 | + ' * Schema-defined QAPI features', | 86 | + ' * Schema-defined QAPI features', |
89 | + __doc__) | 87 | + __doc__) |
90 | + | 88 | + |
91 | + self.features = {} | 89 | + self.features: Dict[str, QAPISchemaFeature] = {} |
90 | + | ||
91 | + def visit_begin(self, schema: QAPISchema) -> None: | ||
92 | + self.features = schema.features() | ||
93 | + self._genh.add("#include \"qapi/util.h\"\n\n") | ||
92 | + | 94 | + |
93 | + def visit_end(self) -> None: | 95 | + def visit_end(self) -> None: |
94 | + features = [] | ||
95 | + | ||
96 | + # We always want special features to have the same | ||
97 | + # enum value across all schemas, since they're | ||
98 | + # referenced from common code. Put them at the | ||
99 | + # start of the list, regardless of whether they | ||
100 | + # are actually referenced in the schema | ||
101 | + for name in QAPISchemaFeature.SPECIAL_NAMES: | ||
102 | + features.append(name) | ||
103 | + | ||
104 | + features.extend(sorted(self.features.keys())) | ||
105 | + | ||
106 | + if len(features) > 64: | ||
107 | + raise Exception("Maximum of 64 schema features is permitted") | ||
108 | + | ||
109 | + self._genh.add("typedef enum {\n") | 96 | + self._genh.add("typedef enum {\n") |
110 | + for name in features: | 97 | + for f in self.features: |
111 | + self._genh.add(f" {c_enum_const(self._prefix + 'QAPI_FEATURE', name)},\n") | 98 | + self._genh.add(f" {c_enum_const('qapi_feature', f.name)}") |
112 | + | ||
113 | + self._genh.add("} " + c_name(self._prefix + 'QapiFeature') + ";\n") | ||
114 | + | ||
115 | + def _record(self, features: List[QAPISchemaFeature]): | ||
116 | + for f in features: | ||
117 | + # Special features are handled separately | ||
118 | + if f.name in QAPISchemaFeature.SPECIAL_NAMES: | 99 | + if f.name in QAPISchemaFeature.SPECIAL_NAMES: |
119 | + continue | 100 | + self._genh.add(f" = {c_enum_const('qapi', f.name)},\n") |
120 | + self.features[f.name] = True | 101 | + else: |
121 | + | 102 | + self._genh.add(",\n") |
122 | + def visit_enum_type(self, | 103 | + |
123 | + name: str, | 104 | + self._genh.add("} " + c_name('QapiFeature') + ";\n") |
124 | + info: Optional[QAPISourceInfo], | 105 | + |
125 | + ifcond: QAPISchemaIfCond, | ||
126 | + features: List[QAPISchemaFeature], | ||
127 | + members: List[QAPISchemaEnumMember], | ||
128 | + prefix: Optional[str]) -> None: | ||
129 | + self._record(features) | ||
130 | + | ||
131 | + def visit_object_type_flat(self, | ||
132 | + name: str, | ||
133 | + info: Optional[QAPISourceInfo], | ||
134 | + ifcond: QAPISchemaIfCond, | ||
135 | + features: List[QAPISchemaFeature], | ||
136 | + members: List[QAPISchemaObjectTypeMember], | ||
137 | + branches: Optional[QAPISchemaBranches]) -> None: | ||
138 | + self._record(features) | ||
139 | + | ||
140 | + def visit_object_type(self, | ||
141 | + name: str, | ||
142 | + info: Optional[QAPISourceInfo], | ||
143 | + ifcond: QAPISchemaIfCond, | ||
144 | + features: List[QAPISchemaFeature], | ||
145 | + base: Optional[QAPISchemaObjectType], | ||
146 | + members: List[QAPISchemaObjectTypeMember], | ||
147 | + branches: Optional[QAPISchemaBranches]) -> None: | ||
148 | + self._record(features) | ||
149 | + | ||
150 | + def visit_alternate_type(self, | ||
151 | + name: str, | ||
152 | + info: Optional[QAPISourceInfo], | ||
153 | + ifcond: QAPISchemaIfCond, | ||
154 | + features: List[QAPISchemaFeature], | ||
155 | + alternatives: QAPISchemaAlternatives) -> None: | ||
156 | + self._record(features) | ||
157 | + | ||
158 | + def visit_command(self, | ||
159 | + name: str, | ||
160 | + info: Optional[QAPISourceInfo], | ||
161 | + ifcond: QAPISchemaIfCond, | ||
162 | + features: List[QAPISchemaFeature], | ||
163 | + arg_type: Optional[QAPISchemaObjectType], | ||
164 | + ret_type: Optional[QAPISchemaType], | ||
165 | + gen: bool, | ||
166 | + success_response: bool, | ||
167 | + boxed: bool, | ||
168 | + allow_oob: bool, | ||
169 | + allow_preconfig: bool, | ||
170 | + coroutine: bool) -> None: | ||
171 | + self._record(features) | ||
172 | + | ||
173 | + def visit_event(self, | ||
174 | + name: str, | ||
175 | + info: Optional[QAPISourceInfo], | ||
176 | + ifcond: QAPISchemaIfCond, | ||
177 | + features: List[QAPISchemaFeature], | ||
178 | + arg_type: Optional[QAPISchemaObjectType], | ||
179 | + boxed: bool) -> None: | ||
180 | + self._record(features) | ||
181 | + | 106 | + |
182 | +def gen_features(schema: QAPISchema, | 107 | +def gen_features(schema: QAPISchema, |
183 | + output_dir: str, | 108 | + output_dir: str, |
184 | + prefix: str) -> None: | 109 | + prefix: str) -> None: |
185 | + vis = QAPISchemaGenFeatureVisitor(prefix) | 110 | + vis = QAPISchemaGenFeatureVisitor(prefix) |
186 | + schema.visit(vis) | 111 | + schema.visit(vis) |
187 | + vis.write(output_dir) | 112 | + vis.write(output_dir) |
113 | diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py | ||
114 | index XXXXXXX..XXXXXXX 100644 | ||
115 | --- a/scripts/qapi/gen.py | ||
116 | +++ b/scripts/qapi/gen.py | ||
117 | @@ -XXX,XX +XXX,XX @@ | ||
118 | |||
119 | |||
120 | def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | ||
121 | - featenum = [f"1u << {c_enum_const('qapi', feat.name)}" | ||
122 | - for feat in features if feat.is_special()] | ||
123 | - return ' | '.join(featenum) or '0' | ||
124 | + feats = [f"1u << {c_enum_const('qapi_feature', feat.name)}" | ||
125 | + for feat in features] | ||
126 | + return ' | '.join(feats) or '0' | ||
127 | |||
128 | |||
129 | class QAPIGen: | ||
188 | diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py | 130 | diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py |
189 | index XXXXXXX..XXXXXXX 100644 | 131 | index XXXXXXX..XXXXXXX 100644 |
190 | --- a/scripts/qapi/main.py | 132 | --- a/scripts/qapi/main.py |
191 | +++ b/scripts/qapi/main.py | 133 | +++ b/scripts/qapi/main.py |
192 | @@ -XXX,XX +XXX,XX @@ | 134 | @@ -XXX,XX +XXX,XX @@ |
... | ... | ||
220 | - return self.name in ('deprecated', 'unstable') | 162 | - return self.name in ('deprecated', 'unstable') |
221 | + return self.name in QAPISchemaFeature.SPECIAL_NAMES | 163 | + return self.name in QAPISchemaFeature.SPECIAL_NAMES |
222 | 164 | ||
223 | 165 | ||
224 | class QAPISchemaObjectTypeMember(QAPISchemaMember): | 166 | class QAPISchemaObjectTypeMember(QAPISchemaMember): |
167 | @@ -XXX,XX +XXX,XX @@ def __init__(self, fname: str): | ||
168 | self._entity_list: List[QAPISchemaEntity] = [] | ||
169 | self._entity_dict: Dict[str, QAPISchemaDefinition] = {} | ||
170 | self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() | ||
171 | + # NB, values in the dict will identify the first encountered | ||
172 | + # usage of a named feature only | ||
173 | + self._feature_dict: Dict[str, QAPISchemaFeature] = OrderedDict() | ||
174 | + | ||
175 | + # All schemas get the names defined in the QapiSpecialFeature enum. | ||
176 | + # Use of OrderedDict ensures they are emitted first when generating | ||
177 | + # the enum definition, thus matching QapiSpecialFeature. | ||
178 | + for f in QAPISchemaFeature.SPECIAL_NAMES: | ||
179 | + self._feature_dict[f] = QAPISchemaFeature(f, None) | ||
180 | + | ||
181 | self._schema_dir = os.path.dirname(fname) | ||
182 | self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) | ||
183 | self._make_module(fname) | ||
184 | @@ -XXX,XX +XXX,XX @@ def __init__(self, fname: str): | ||
185 | self._def_exprs(exprs) | ||
186 | self.check() | ||
187 | |||
188 | + def features(self) -> List[QAPISchemaFeature]: | ||
189 | + return self._feature_dict.values() | ||
190 | + | ||
191 | def _def_entity(self, ent: QAPISchemaEntity) -> None: | ||
192 | self._entity_list.append(ent) | ||
193 | |||
194 | @@ -XXX,XX +XXX,XX @@ def _make_features( | ||
195 | ) -> List[QAPISchemaFeature]: | ||
196 | if features is None: | ||
197 | return [] | ||
198 | + | ||
199 | + for f in features: | ||
200 | + feat = QAPISchemaFeature(f['name'], info) | ||
201 | + if feat.name not in self._feature_dict: | ||
202 | + self._feature_dict[feat.name] = feat | ||
203 | + | ||
204 | return [QAPISchemaFeature(f['name'], info, | ||
205 | QAPISchemaIfCond(f.get('if'))) | ||
206 | for f in features] | ||
207 | @@ -XXX,XX +XXX,XX @@ def check(self) -> None: | ||
208 | for doc in self.docs: | ||
209 | doc.check() | ||
210 | |||
211 | + features = list(self._feature_dict.values()) | ||
212 | + if len(features) > 64: | ||
213 | + raise QAPISemError( | ||
214 | + features[64].info, | ||
215 | + "Maximum of 64 schema features is permitted") | ||
216 | + | ||
217 | def visit(self, visitor: QAPISchemaVisitor) -> None: | ||
218 | visitor.visit_begin(self) | ||
219 | for mod in self._module_dict.values(): | ||
225 | diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py | 220 | diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py |
226 | index XXXXXXX..XXXXXXX 100644 | 221 | index XXXXXXX..XXXXXXX 100644 |
227 | --- a/scripts/qapi/types.py | 222 | --- a/scripts/qapi/types.py |
228 | +++ b/scripts/qapi/types.py | 223 | +++ b/scripts/qapi/types.py |
229 | @@ -XXX,XX +XXX,XX @@ def _begin_user_module(self, name: str) -> None: | 224 | @@ -XXX,XX +XXX,XX @@ def _begin_user_module(self, name: str) -> None: |
230 | types=types, visit=visit)) | 225 | #include "qapi/dealloc-visitor.h" |
226 | #include "%(types)s.h" | ||
227 | #include "%(visit)s.h" | ||
228 | +#include "%(prefix)sqapi-features.h" | ||
229 | ''', | ||
230 | - types=types, visit=visit)) | ||
231 | + types=types, visit=visit, | ||
232 | + prefix=self._prefix)) | ||
231 | self._genh.preamble_add(mcgen(''' | 233 | self._genh.preamble_add(mcgen(''' |
232 | #include "qapi/qapi-builtin-types.h" | 234 | #include "qapi/qapi-builtin-types.h" |
233 | -''')) | 235 | -''')) |
234 | +#include "%(prefix)sqapi-features.h" | ||
235 | +''', | 236 | +''', |
236 | + prefix=self._prefix)) | 237 | + prefix=self._prefix)) |
237 | 238 | ||
238 | def visit_begin(self, schema: QAPISchema) -> None: | 239 | def visit_begin(self, schema: QAPISchema) -> None: |
239 | # gen_object() is recursive, ensure it doesn't visit the empty type | 240 | # gen_object() is recursive, ensure it doesn't visit the empty type |
241 | diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py | ||
242 | index XXXXXXX..XXXXXXX 100644 | ||
243 | --- a/scripts/qapi/visit.py | ||
244 | +++ b/scripts/qapi/visit.py | ||
245 | @@ -XXX,XX +XXX,XX @@ def _begin_user_module(self, name: str) -> None: | ||
246 | #include "qemu/osdep.h" | ||
247 | #include "qapi/error.h" | ||
248 | #include "%(visit)s.h" | ||
249 | +#include "%(prefix)sqapi-features.h" | ||
250 | ''', | ||
251 | - visit=visit)) | ||
252 | + visit=visit, prefix=self._prefix)) | ||
253 | self._genh.preamble_add(mcgen(''' | ||
254 | #include "qapi/qapi-builtin-visit.h" | ||
255 | #include "%(types)s.h" | ||
256 | diff --git a/tests/meson.build b/tests/meson.build | ||
257 | index XXXXXXX..XXXXXXX 100644 | ||
258 | --- a/tests/meson.build | ||
259 | +++ b/tests/meson.build | ||
260 | @@ -XXX,XX +XXX,XX @@ test_qapi_outputs = [ | ||
261 | 'test-qapi-events-sub-sub-module.h', | ||
262 | 'test-qapi-events.c', | ||
263 | 'test-qapi-events.h', | ||
264 | + 'test-qapi-features.c', | ||
265 | + 'test-qapi-features.h', | ||
266 | 'test-qapi-init-commands.c', | ||
267 | 'test-qapi-init-commands.h', | ||
268 | 'test-qapi-introspect.c', | ||
269 | diff --git a/tests/qapi-schema/features-too-many.err b/tests/qapi-schema/features-too-many.err | ||
270 | new file mode 100644 | ||
271 | index XXXXXXX..XXXXXXX | ||
272 | --- /dev/null | ||
273 | +++ b/tests/qapi-schema/features-too-many.err | ||
274 | @@ -XXX,XX +XXX,XX @@ | ||
275 | +features-too-many.json: In command 'go-fish': | ||
276 | +features-too-many.json:2: Maximum of 64 schema features is permitted | ||
277 | diff --git a/tests/qapi-schema/features-too-many.json b/tests/qapi-schema/features-too-many.json | ||
278 | new file mode 100644 | ||
279 | index XXXXXXX..XXXXXXX | ||
280 | --- /dev/null | ||
281 | +++ b/tests/qapi-schema/features-too-many.json | ||
282 | @@ -XXX,XX +XXX,XX @@ | ||
283 | +# Max 64 features, with 2 specials, so 63rd custom is invalid | ||
284 | +{ 'command': 'go-fish', | ||
285 | + 'features': [ | ||
286 | + 'f00', 'f01', 'f02', 'f03', 'f04', 'f05', 'f06', 'f07', | ||
287 | + 'f08', 'f09', 'f0a', 'f0b', 'f0c', 'f0d', 'f0e', 'f0f', | ||
288 | + 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', | ||
289 | + 'f18', 'f19', 'f1a', 'f1b', 'f1c', 'f1d', 'f1e', 'f1f', | ||
290 | + 'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f26', 'f27', | ||
291 | + 'f28', 'f29', 'f2a', 'f2b', 'f2c', 'f2d', 'f2e', 'f2f', | ||
292 | + 'f30', 'f31', 'f32', 'f33', 'f34', 'f35', 'f36', 'f37', | ||
293 | + 'f38', 'f39', 'f3a', 'f3b', 'f3c', 'f3d', 'f3e' | ||
294 | + ] | ||
295 | +} | ||
296 | diff --git a/tests/qapi-schema/features-too-many.out b/tests/qapi-schema/features-too-many.out | ||
297 | new file mode 100644 | ||
298 | index XXXXXXX..XXXXXXX | ||
299 | diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build | ||
300 | index XXXXXXX..XXXXXXX 100644 | ||
301 | --- a/tests/qapi-schema/meson.build | ||
302 | +++ b/tests/qapi-schema/meson.build | ||
303 | @@ -XXX,XX +XXX,XX @@ schemas = [ | ||
304 | 'event-case.json', | ||
305 | 'event-member-invalid-dict.json', | ||
306 | 'event-nest-struct.json', | ||
307 | + 'features-too-many.json', | ||
308 | 'features-bad-type.json', | ||
309 | 'features-deprecated-type.json', | ||
310 | 'features-duplicate-name.json', | ||
240 | -- | 311 | -- |
241 | 2.45.2 | 312 | 2.46.0 |
242 | 313 | ||
243 | 314 | diff view generated by jsdifflib |