... | ... | ||
---|---|---|---|
12 | I've changed impl such that we expose all features to the code | 12 | I've changed impl such that we expose all features to the code |
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 | |||
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()' | ||
17 | 25 | ||
18 | Changed in v2: | 26 | Changed in v2: |
19 | 27 | ||
20 | * Reference QapiSpecialFeature enums constants when defining | 28 | * Reference QapiSpecialFeature enums constants when defining |
21 | QapiFeature enums constants for deprecated & unstable | 29 | QapiFeature enums constants for deprecated & unstable |
... | ... | ||
33 | qapi: cope with feature names containing a '-' | 41 | qapi: cope with feature names containing a '-' |
34 | qapi: change 'unsigned special_features' to 'uint64_t features' | 42 | qapi: change 'unsigned special_features' to 'uint64_t features' |
35 | qapi: rename 'special_features' to 'features' | 43 | qapi: rename 'special_features' to 'features' |
36 | qapi: expose all schema features to code | 44 | qapi: expose all schema features to code |
37 | 45 | ||
38 | include/qapi/compat-policy.h | 2 +- | 46 | include/qapi/compat-policy.h | 2 +- |
39 | include/qapi/qmp/dispatch.h | 4 +-- | 47 | include/qapi/qmp/dispatch.h | 4 +- |
40 | include/qapi/util.h | 2 +- | 48 | include/qapi/util.h | 2 +- |
41 | include/qapi/visitor-impl.h | 4 +-- | 49 | include/qapi/visitor-impl.h | 4 +- |
42 | include/qapi/visitor.h | 12 +++---- | 50 | include/qapi/visitor.h | 12 +++--- |
43 | meson.build | 1 + | 51 | meson.build | 1 + |
44 | qapi/qapi-forward-visitor.c | 8 ++--- | 52 | qapi/qapi-forward-visitor.c | 8 ++-- |
45 | qapi/qapi-util.c | 6 ++-- | 53 | qapi/qapi-util.c | 6 +-- |
46 | qapi/qapi-visit-core.c | 12 +++---- | 54 | qapi/qapi-visit-core.c | 12 +++--- |
47 | qapi/qmp-dispatch.c | 2 +- | 55 | qapi/qmp-dispatch.c | 2 +- |
48 | qapi/qmp-registry.c | 4 +-- | 56 | qapi/qmp-registry.c | 4 +- |
49 | qapi/qobject-input-visitor.c | 4 +-- | 57 | qapi/qobject-input-visitor.c | 4 +- |
50 | qapi/qobject-output-visitor.c | 6 ++-- | 58 | qapi/qobject-output-visitor.c | 6 +-- |
51 | scripts/qapi/commands.py | 5 +-- | 59 | scripts/qapi/commands.py | 5 ++- |
52 | scripts/qapi/features.py | 62 +++++++++++++++++++++++++++++++++++ | 60 | scripts/qapi/features.py | 51 ++++++++++++++++++++++++ |
53 | scripts/qapi/gen.py | 9 ++--- | 61 | scripts/qapi/gen.py | 9 +++-- |
54 | scripts/qapi/main.py | 2 ++ | 62 | scripts/qapi/main.py | 2 + |
55 | scripts/qapi/schema.py | 19 ++++++++++- | 63 | scripts/qapi/schema.py | 30 +++++++++++++- |
56 | scripts/qapi/types.py | 18 +++++----- | 64 | scripts/qapi/types.py | 19 +++++---- |
57 | scripts/qapi/visit.py | 17 +++++----- | 65 | scripts/qapi/visit.py | 17 ++++---- |
58 | 20 files changed, 143 insertions(+), 56 deletions(-) | 66 | tests/meson.build | 2 + |
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(-) | ||
59 | 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 | ||
60 | 76 | ||
61 | -- | 77 | -- |
62 | 2.46.0 | 78 | 2.46.0 |
63 | 79 | ||
64 | 80 | diff view generated by jsdifflib |
1 | When we shortly expose all feature names to code, it will be valid to | 1 | When we shortly expose all feature names to code, it will be valid to |
---|---|---|---|
2 | include a '-', which must be translated to a '_' for the enum constants. | 2 | include a '-', which must be translated to a '_' for the enum constants. |
3 | 3 | ||
4 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | 4 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> |
5 | --- | 5 | --- |
6 | scripts/qapi/gen.py | 3 ++- | 6 | scripts/qapi/gen.py | 3 ++- |
7 | 1 file changed, 2 insertions(+), 1 deletion(-) | 7 | 1 file changed, 2 insertions(+), 1 deletion(-) |
8 | 8 | ||
9 | diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py | 9 | diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py |
10 | index XXXXXXX..XXXXXXX 100644 | 10 | index XXXXXXX..XXXXXXX 100644 |
11 | --- a/scripts/qapi/gen.py | 11 | --- a/scripts/qapi/gen.py |
12 | +++ b/scripts/qapi/gen.py | 12 | +++ b/scripts/qapi/gen.py |
13 | @@ -XXX,XX +XXX,XX @@ | 13 | @@ -XXX,XX +XXX,XX @@ |
14 | ) | 14 | ) |
15 | 15 | ||
16 | from .common import ( | 16 | from .common import ( |
17 | + c_enum_const, | 17 | + c_enum_const, |
18 | c_fname, | 18 | c_fname, |
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_special_features(features: Sequence[QAPISchemaFeature]) -> str: | 24 | def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str: |
25 | - special_features = [f"1u << QAPI_{feat.name.upper()}" | 25 | - special_features = [f"1u << QAPI_{feat.name.upper()}" |
26 | + special_features = [f"1u << {c_enum_const('qapi', 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(special_features) or '0' | 28 | return ' | '.join(special_features) or '0' |
29 | 29 | ||
30 | -- | 30 | -- |
31 | 2.46.0 | 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 | 2 +- | 14 | include/qapi/util.h | 2 +- |
... | ... | diff view generated by jsdifflib |
1 | This updates the QAPI code generation to refer to 'features' instead | 1 | This updates the QAPI code generation to refer to 'features' instead |
---|---|---|---|
2 | of 'special_features', in preparation for generalizing their exposure. | 2 | of 'special_features', in preparation for generalizing their exposure. |
3 | 3 | ||
4 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | 4 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> |
5 | --- | 5 | --- |
6 | scripts/qapi/commands.py | 4 ++-- | 6 | scripts/qapi/commands.py | 4 ++-- |
7 | scripts/qapi/gen.py | 6 +++--- | 7 | scripts/qapi/gen.py | 8 ++++---- |
8 | scripts/qapi/types.py | 10 +++++----- | 8 | scripts/qapi/types.py | 10 +++++----- |
9 | scripts/qapi/visit.py | 14 +++++++------- | 9 | scripts/qapi/visit.py | 14 +++++++------- |
10 | 4 files changed, 17 insertions(+), 17 deletions(-) | 10 | 4 files changed, 18 insertions(+), 18 deletions(-) |
11 | 11 | ||
12 | diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py | 12 | diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py |
13 | index XXXXXXX..XXXXXXX 100644 | 13 | index XXXXXXX..XXXXXXX 100644 |
14 | --- a/scripts/qapi/commands.py | 14 | --- a/scripts/qapi/commands.py |
15 | +++ b/scripts/qapi/commands.py | 15 | +++ b/scripts/qapi/commands.py |
... | ... | ||
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 << {c_enum_const('qapi', feat.name)}" | 43 | - special_features = [f"1u << {c_enum_const('qapi', feat.name)}" |
44 | - for feat in features if feat.is_special()] | ||
45 | - return ' | '.join(special_features) or '0' | ||
44 | +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | 46 | +def gen_features(features: Sequence[QAPISchemaFeature]) -> str: |
45 | + features = [f"1u << {c_enum_const('qapi', feat.name)}" | 47 | + featenum = [f"1u << {c_enum_const('qapi', feat.name)}" |
46 | for feat in features if feat.is_special()] | 48 | + for feat in features if feat.is_special()] |
47 | - return ' | '.join(special_features) or '0' | 49 | + return ' | '.join(featenum) or '0' |
48 | + return ' | '.join(features) or '0' | ||
49 | 50 | ||
50 | 51 | ||
51 | class QAPIGen: | 52 | class QAPIGen: |
52 | 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 |
53 | index XXXXXXX..XXXXXXX 100644 | 54 | index XXXXXXX..XXXXXXX 100644 |
... | ... | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
8 | retains compatibility with common code that references the features | 8 | retains compatibility with common code that references the features |
9 | via the QapiSpecialFeatures constants. | 9 | via the QapiSpecialFeatures constants. |
10 | 10 | ||
11 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> | 11 | Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> |
12 | --- | 12 | --- |
13 | meson.build | 1 + | 13 | meson.build | 1 + |
14 | scripts/qapi/commands.py | 1 + | 14 | scripts/qapi/commands.py | 1 + |
15 | scripts/qapi/features.py | 62 ++++++++++++++++++++++++++++++++++++++++ | 15 | scripts/qapi/features.py | 51 ++++++++++++++++++++++++ |
16 | scripts/qapi/gen.py | 4 +-- | 16 | scripts/qapi/gen.py | 6 +-- |
17 | scripts/qapi/main.py | 2 ++ | 17 | scripts/qapi/main.py | 2 + |
18 | scripts/qapi/schema.py | 19 +++++++++++- | 18 | scripts/qapi/schema.py | 30 +++++++++++++- |
19 | scripts/qapi/types.py | 6 ++-- | 19 | scripts/qapi/types.py | 7 +++- |
20 | scripts/qapi/visit.py | 3 +- | 20 | scripts/qapi/visit.py | 3 +- |
21 | 8 files changed, 92 insertions(+), 6 deletions(-) | 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(-) | ||
22 | create mode 100644 scripts/qapi/features.py | 27 | create mode 100644 scripts/qapi/features.py |
28 | create mode 100644 tests/qapi-schema/features-too-many.err | ||
29 | create mode 100644 tests/qapi-schema/features-too-many.json | ||
30 | create mode 100644 tests/qapi-schema/features-too-many.out | ||
23 | 31 | ||
24 | diff --git a/meson.build b/meson.build | 32 | diff --git a/meson.build b/meson.build |
25 | index XXXXXXX..XXXXXXX 100644 | 33 | index XXXXXXX..XXXXXXX 100644 |
26 | --- a/meson.build | 34 | --- a/meson.build |
27 | +++ b/meson.build | 35 | +++ b/meson.build |
... | ... | ||
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 | + QAPISchemaFeature, | 77 | + QAPISchemaFeature, |
70 | +) | 78 | +) |
71 | +from .source import QAPISourceInfo | ||
72 | + | 79 | + |
73 | + | 80 | + |
74 | +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): | 81 | +class QAPISchemaGenFeatureVisitor(QAPISchemaMonolithicCVisitor): |
75 | + | 82 | + |
76 | + def __init__(self, prefix: str): | 83 | + def __init__(self, prefix: str): |
77 | + super().__init__( | 84 | + super().__init__( |
78 | + prefix, 'qapi-features', | 85 | + prefix, 'qapi-features', |
79 | + ' * Schema-defined QAPI features', | 86 | + ' * Schema-defined QAPI features', |
80 | + __doc__) | 87 | + __doc__) |
81 | + | 88 | + |
82 | + self.features = {} | 89 | + self.features: Dict[str, QAPISchemaFeature] = {} |
83 | + | 90 | + |
84 | + def visit_begin(self, schema: QAPISchema): | 91 | + def visit_begin(self, schema: QAPISchema) -> None: |
85 | + self.features = schema._feature_dict | 92 | + self.features = schema.features() |
93 | + self._genh.add("#include \"qapi/util.h\"\n\n") | ||
86 | + | 94 | + |
87 | + def visit_end(self) -> None: | 95 | + def visit_end(self) -> None: |
88 | + features = [ | ||
89 | + self.features[f] | ||
90 | + for f in QAPISchemaFeature.SPECIAL_NAMES | ||
91 | + ] | ||
92 | + | ||
93 | + features.extend( | ||
94 | + sorted( | ||
95 | + filter(lambda f: not f.is_special(), | ||
96 | + self.features.values()), | ||
97 | + key=lambda f: f.name) | ||
98 | + ) | ||
99 | + | ||
100 | + self._genh.add("typedef enum {\n") | 96 | + self._genh.add("typedef enum {\n") |
101 | + for f in features: | 97 | + for f in self.features: |
102 | + self._genh.add(f" {c_enum_const('qapi_feature', f.name)}") | 98 | + self._genh.add(f" {c_enum_const('qapi_feature', f.name)}") |
103 | + if f.name in QAPISchemaFeature.SPECIAL_NAMES: | 99 | + if f.name in QAPISchemaFeature.SPECIAL_NAMES: |
104 | + self._genh.add(f" = {c_enum_const('qapi', f.name)},\n" ) | 100 | + self._genh.add(f" = {c_enum_const('qapi', f.name)},\n") |
105 | + else: | 101 | + else: |
106 | + self._genh.add(",\n") | 102 | + self._genh.add(",\n") |
107 | + | 103 | + |
108 | + self._genh.add("} " + c_name('QapiFeature') + ";\n") | 104 | + self._genh.add("} " + c_name('QapiFeature') + ";\n") |
105 | + | ||
109 | + | 106 | + |
110 | +def gen_features(schema: QAPISchema, | 107 | +def gen_features(schema: QAPISchema, |
111 | + output_dir: str, | 108 | + output_dir: str, |
112 | + prefix: str) -> None: | 109 | + prefix: str) -> None: |
113 | + vis = QAPISchemaGenFeatureVisitor(prefix) | 110 | + vis = QAPISchemaGenFeatureVisitor(prefix) |
... | ... | ||
119 | +++ b/scripts/qapi/gen.py | 116 | +++ b/scripts/qapi/gen.py |
120 | @@ -XXX,XX +XXX,XX @@ | 117 | @@ -XXX,XX +XXX,XX @@ |
121 | 118 | ||
122 | 119 | ||
123 | def gen_features(features: Sequence[QAPISchemaFeature]) -> str: | 120 | def gen_features(features: Sequence[QAPISchemaFeature]) -> str: |
124 | - features = [f"1u << {c_enum_const('qapi', feat.name)}" | 121 | - featenum = [f"1u << {c_enum_const('qapi', feat.name)}" |
125 | - for feat in features if feat.is_special()] | 122 | - for feat in features if feat.is_special()] |
126 | + features = [f"1u << {c_enum_const('qapi_feature', feat.name)}" | 123 | - return ' | '.join(featenum) or '0' |
127 | + for feat in features] | 124 | + feats = [f"1u << {c_enum_const('qapi_feature', feat.name)}" |
128 | return ' | '.join(features) or '0' | 125 | + for feat in features] |
129 | 126 | + return ' | '.join(feats) or '0' | |
130 | 127 | ||
128 | |||
129 | class QAPIGen: | ||
131 | 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 |
132 | index XXXXXXX..XXXXXXX 100644 | 131 | index XXXXXXX..XXXXXXX 100644 |
133 | --- a/scripts/qapi/main.py | 132 | --- a/scripts/qapi/main.py |
134 | +++ b/scripts/qapi/main.py | 133 | +++ b/scripts/qapi/main.py |
135 | @@ -XXX,XX +XXX,XX @@ | 134 | @@ -XXX,XX +XXX,XX @@ |
... | ... | ||
167 | class QAPISchemaObjectTypeMember(QAPISchemaMember): | 166 | class QAPISchemaObjectTypeMember(QAPISchemaMember): |
168 | @@ -XXX,XX +XXX,XX @@ def __init__(self, fname: str): | 167 | @@ -XXX,XX +XXX,XX @@ def __init__(self, fname: str): |
169 | self._entity_list: List[QAPISchemaEntity] = [] | 168 | self._entity_list: List[QAPISchemaEntity] = [] |
170 | self._entity_dict: Dict[str, QAPISchemaDefinition] = {} | 169 | self._entity_dict: Dict[str, QAPISchemaDefinition] = {} |
171 | self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() | 170 | self._module_dict: Dict[str, QAPISchemaModule] = OrderedDict() |
172 | + self._feature_dict: Dict[str, QAPISchemaFeature] = {} | 171 | + # NB, values in the dict will identify the first encountered |
173 | + | 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. | ||
174 | + for f in QAPISchemaFeature.SPECIAL_NAMES: | 178 | + for f in QAPISchemaFeature.SPECIAL_NAMES: |
175 | + self._feature_dict[f] = QAPISchemaFeature(f, "special feature") | 179 | + self._feature_dict[f] = QAPISchemaFeature(f, None) |
176 | + | 180 | + |
177 | self._schema_dir = os.path.dirname(fname) | 181 | self._schema_dir = os.path.dirname(fname) |
178 | self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) | 182 | self._make_module(QAPISchemaModule.BUILTIN_MODULE_NAME) |
179 | self._make_module(fname) | 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 | |||
180 | @@ -XXX,XX +XXX,XX @@ def _make_features( | 194 | @@ -XXX,XX +XXX,XX @@ def _make_features( |
181 | ) -> List[QAPISchemaFeature]: | 195 | ) -> List[QAPISchemaFeature]: |
182 | if features is None: | 196 | if features is None: |
183 | return [] | 197 | return [] |
184 | + | 198 | + |
185 | + for f in features: | 199 | + for f in features: |
186 | + feat = QAPISchemaFeature(f['name'], info) | 200 | + feat = QAPISchemaFeature(f['name'], info) |
187 | + if feat.name not in self._feature_dict: | 201 | + if feat.name not in self._feature_dict: |
188 | + if len(self._feature_dict) == 64: | ||
189 | + raise Exception("Maximum of 64 schema features is permitted") | ||
190 | + | ||
191 | + self._feature_dict[feat.name] = feat | 202 | + self._feature_dict[feat.name] = feat |
192 | + | 203 | + |
193 | return [QAPISchemaFeature(f['name'], info, | 204 | return [QAPISchemaFeature(f['name'], info, |
194 | QAPISchemaIfCond(f.get('if'))) | 205 | QAPISchemaIfCond(f.get('if'))) |
195 | for f in features] | 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(): | ||
196 | 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 |
197 | index XXXXXXX..XXXXXXX 100644 | 221 | index XXXXXXX..XXXXXXX 100644 |
198 | --- a/scripts/qapi/types.py | 222 | --- a/scripts/qapi/types.py |
199 | +++ b/scripts/qapi/types.py | 223 | +++ b/scripts/qapi/types.py |
200 | @@ -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: |
201 | #include "qapi/dealloc-visitor.h" | 225 | #include "qapi/dealloc-visitor.h" |
202 | #include "%(types)s.h" | 226 | #include "%(types)s.h" |
203 | #include "%(visit)s.h" | 227 | #include "%(visit)s.h" |
204 | +#include "%(prefix)sqapi-features.h" | 228 | +#include "%(prefix)sqapi-features.h" |
205 | ''', | 229 | ''', |
206 | - types=types, visit=visit)) | 230 | - types=types, visit=visit)) |
207 | + types=types, visit=visit, prefix=self._prefix)) | 231 | + types=types, visit=visit, |
232 | + prefix=self._prefix)) | ||
208 | self._genh.preamble_add(mcgen(''' | 233 | self._genh.preamble_add(mcgen(''' |
209 | #include "qapi/qapi-builtin-types.h" | 234 | #include "qapi/qapi-builtin-types.h" |
210 | -''')) | 235 | -''')) |
211 | +''', | 236 | +''', |
212 | + prefix=self._prefix)) | 237 | + prefix=self._prefix)) |
... | ... | ||
226 | - visit=visit)) | 251 | - visit=visit)) |
227 | + visit=visit, prefix=self._prefix)) | 252 | + visit=visit, prefix=self._prefix)) |
228 | self._genh.preamble_add(mcgen(''' | 253 | self._genh.preamble_add(mcgen(''' |
229 | #include "qapi/qapi-builtin-visit.h" | 254 | #include "qapi/qapi-builtin-visit.h" |
230 | #include "%(types)s.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', | ||
231 | -- | 311 | -- |
232 | 2.46.0 | 312 | 2.46.0 |
233 | 313 | ||
234 | 314 | diff view generated by jsdifflib |