...
...
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