Message ID | 20180111213250.16511-6-marcandre.lureau@redhat.com |
---|---|
State | New |
Headers | show |
Series | Hi, | expand |
Marc-André Lureau <marcandre.lureau@redhat.com> writes: > Accept 'if' key in top-level elements, accepted as string or list of > string type. The following patches will modify the test visitor to > check the value is correctly saved, and generate #if/#endif code (as a > single #if/endif line or a series for a list). > > Example of 'if' key: > { 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, > 'if': 'defined(TEST_IF_STRUCT)' } > > The generated code is for now *unconditional*. Later patches generate > the conditionals. > > A following patch for qapi-code-gen.txt will provide more complete > documentation for 'if' usage. > > Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> > Reviewed-by: Markus Armbruster <armbru@redhat.com> > --- > scripts/qapi.py | 36 ++++++++++++++++++++++++++------ > tests/test-qmp-commands.c | 6 ++++++ > tests/Makefile.include | 4 ++++ > tests/qapi-schema/bad-if-empty-list.err | 1 + > tests/qapi-schema/bad-if-empty-list.exit | 1 + > tests/qapi-schema/bad-if-empty-list.json | 3 +++ > tests/qapi-schema/bad-if-empty-list.out | 0 > tests/qapi-schema/bad-if-empty.err | 1 + > tests/qapi-schema/bad-if-empty.exit | 1 + > tests/qapi-schema/bad-if-empty.json | 3 +++ > tests/qapi-schema/bad-if-empty.out | 0 > tests/qapi-schema/bad-if-list.err | 1 + > tests/qapi-schema/bad-if-list.exit | 1 + > tests/qapi-schema/bad-if-list.json | 3 +++ > tests/qapi-schema/bad-if-list.out | 0 > tests/qapi-schema/bad-if.err | 1 + > tests/qapi-schema/bad-if.exit | 1 + > tests/qapi-schema/bad-if.json | 3 +++ > tests/qapi-schema/bad-if.out | 0 > tests/qapi-schema/qapi-schema-test.json | 20 ++++++++++++++++++ > tests/qapi-schema/qapi-schema-test.out | 22 +++++++++++++++++++ > 21 files changed, 102 insertions(+), 6 deletions(-) > create mode 100644 tests/qapi-schema/bad-if-empty-list.err > create mode 100644 tests/qapi-schema/bad-if-empty-list.exit > create mode 100644 tests/qapi-schema/bad-if-empty-list.json > create mode 100644 tests/qapi-schema/bad-if-empty-list.out > create mode 100644 tests/qapi-schema/bad-if-empty.err > create mode 100644 tests/qapi-schema/bad-if-empty.exit > create mode 100644 tests/qapi-schema/bad-if-empty.json > create mode 100644 tests/qapi-schema/bad-if-empty.out > create mode 100644 tests/qapi-schema/bad-if-list.err > create mode 100644 tests/qapi-schema/bad-if-list.exit > create mode 100644 tests/qapi-schema/bad-if-list.json > create mode 100644 tests/qapi-schema/bad-if-list.out > create mode 100644 tests/qapi-schema/bad-if.err > create mode 100644 tests/qapi-schema/bad-if.exit > create mode 100644 tests/qapi-schema/bad-if.json > create mode 100644 tests/qapi-schema/bad-if.out > > diff --git a/scripts/qapi.py b/scripts/qapi.py > index 43a54bf40f..27df0fcf48 100644 > --- a/scripts/qapi.py > +++ b/scripts/qapi.py > @@ -630,6 +630,27 @@ def add_name(name, info, meta, implicit=False): > all_names[name] = meta > > > +def check_if(expr, info): > + > + def check_if_str(ifcond, info): > + if not isinstance(ifcond, str): > + raise QAPISemError( > + info, "'if' condition must be a string or a list of strings") > + if ifcond == '': > + raise QAPISemError(info, "'if' condition '' makes no sense") > + > + ifcond = expr.get('if') > + if ifcond is None: > + return > + elif isinstance(ifcond, list): Suggest s/elif/if/ > + if ifcond == []: > + raise QAPISemError(info, "'if' condition [] is useless") > + for elt in ifcond: > + check_if_str(elt, info) > + else: > + check_if_str(ifcond, info) > + > + > def check_type(info, source, value, allow_array=False, > allow_dict=False, allow_optional=False, > allow_metas=[]): R-by stands.
diff --git a/scripts/qapi.py b/scripts/qapi.py index 43a54bf40f..27df0fcf48 100644 --- a/scripts/qapi.py +++ b/scripts/qapi.py @@ -630,6 +630,27 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta +def check_if(expr, info): + + def check_if_str(ifcond, info): + if not isinstance(ifcond, str): + raise QAPISemError( + info, "'if' condition must be a string or a list of strings") + if ifcond == '': + raise QAPISemError(info, "'if' condition '' makes no sense") + + ifcond = expr.get('if') + if ifcond is None: + return + elif isinstance(ifcond, list): + if ifcond == []: + raise QAPISemError(info, "'if' condition [] is useless") + for elt in ifcond: + check_if_str(elt, info) + else: + check_if_str(ifcond, info) + + def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -869,6 +890,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) + if key == 'if': + check_if(expr, info) for key in required: if key not in expr: raise QAPISemError(info, "Key '%s' is missing from %s '%s'" @@ -894,27 +917,28 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' - check_keys(expr_elem, 'enum', ['data'], ['prefix']) + check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], - ['base', 'discriminator']) + ['base', 'discriminator', 'if']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' - check_keys(expr_elem, 'alternate', ['data']) + check_keys(expr_elem, 'alternate', ['data'], ['if']) elif 'struct' in expr: meta = 'struct' - check_keys(expr_elem, 'struct', ['data'], ['base']) + check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], - ['data', 'returns', 'gen', 'success-response', 'boxed']) + ['data', 'returns', 'gen', 'success-response', 'boxed', + 'if']) elif 'event' in expr: meta = 'event' - check_keys(expr_elem, 'event', [], ['data', 'boxed']) + check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 904c89d4d4..9b9a7ffee7 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -10,6 +10,12 @@ static QmpCommandList qmp_commands; +/* #if defined(TEST_IF_CMD) */ +void qmp_TestIfCmd(TestIfStruct *foo, Error **errp) +{ +} +/* #endif */ + void qmp_user_def_cmd(Error **errp) { } diff --git a/tests/Makefile.include b/tests/Makefile.include index 39a4b5359d..501a5713b5 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -410,6 +410,10 @@ qapi-schema += args-unknown.json qapi-schema += bad-base.json qapi-schema += bad-data.json qapi-schema += bad-ident.json +qapi-schema += bad-if.json +qapi-schema += bad-if-empty.json +qapi-schema += bad-if-empty-list.json +qapi-schema += bad-if-list.json qapi-schema += bad-type-bool.json qapi-schema += bad-type-dict.json qapi-schema += bad-type-int.json diff --git a/tests/qapi-schema/bad-if-empty-list.err b/tests/qapi-schema/bad-if-empty-list.err new file mode 100644 index 0000000000..75fe6497bc --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless diff --git a/tests/qapi-schema/bad-if-empty-list.exit b/tests/qapi-schema/bad-if-empty-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty-list.json b/tests/qapi-schema/bad-if-empty-list.json new file mode 100644 index 0000000000..94f2eb8670 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty-list.json @@ -0,0 +1,3 @@ +# check empty 'if' list +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': [] } diff --git a/tests/qapi-schema/bad-if-empty-list.out b/tests/qapi-schema/bad-if-empty-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-empty.err b/tests/qapi-schema/bad-if-empty.err new file mode 100644 index 0000000000..358bdc3e51 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-empty.exit b/tests/qapi-schema/bad-if-empty.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-empty.json b/tests/qapi-schema/bad-if-empty.json new file mode 100644 index 0000000000..fe1dd4eca6 --- /dev/null +++ b/tests/qapi-schema/bad-if-empty.json @@ -0,0 +1,3 @@ +# check empty 'if' +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': '' } diff --git a/tests/qapi-schema/bad-if-empty.out b/tests/qapi-schema/bad-if-empty.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if-list.err b/tests/qapi-schema/bad-if-list.err new file mode 100644 index 0000000000..0af6316f78 --- /dev/null +++ b/tests/qapi-schema/bad-if-list.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense diff --git a/tests/qapi-schema/bad-if-list.exit b/tests/qapi-schema/bad-if-list.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if-list.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if-list.json b/tests/qapi-schema/bad-if-list.json new file mode 100644 index 0000000000..49ced9b9ca --- /dev/null +++ b/tests/qapi-schema/bad-if-list.json @@ -0,0 +1,3 @@ +# check invalid 'if' content +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': ['foo', ''] } diff --git a/tests/qapi-schema/bad-if-list.out b/tests/qapi-schema/bad-if-list.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/bad-if.err b/tests/qapi-schema/bad-if.err new file mode 100644 index 0000000000..c2e3f5f44c --- /dev/null +++ b/tests/qapi-schema/bad-if.err @@ -0,0 +1 @@ +tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings diff --git a/tests/qapi-schema/bad-if.exit b/tests/qapi-schema/bad-if.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/bad-if.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/bad-if.json b/tests/qapi-schema/bad-if.json new file mode 100644 index 0000000000..3edd1a0bf2 --- /dev/null +++ b/tests/qapi-schema/bad-if.json @@ -0,0 +1,3 @@ +# check invalid 'if' type +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': { 'value': 'defined(TEST_IF_STRUCT)' } } diff --git a/tests/qapi-schema/bad-if.out b/tests/qapi-schema/bad-if.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index c72dbd8050..b997b2d43d 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -188,3 +188,23 @@ 'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'], 'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' }, 'returns': '__org.qemu_x-Union1' } + +# test 'if' condition handling + +{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, + 'if': 'defined(TEST_IF_STRUCT)' } + +{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ], + 'if': 'defined(TEST_IF_ENUM)' } + +{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' }, + 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' } + +{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' }, + 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' } + +{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' }, + 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } + +{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' }, + 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 3b1e9082d3..7fbaea19bc 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -52,6 +52,22 @@ enum QEnumTwo ['value1', 'value2'] prefix QENUM_TWO enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] prefix QTYPE +alternate TestIfAlternate + tag type + case foo: int + case bar: TestStruct +command TestIfCmd q_obj_TestIfCmd-arg -> None + gen=True success_response=True boxed=False +enum TestIfEnum ['foo', 'bar'] +event TestIfEvent q_obj_TestIfEvent-arg + boxed=False +object TestIfStruct + member foo: int optional=False +object TestIfUnion + member type: TestIfUnionKind optional=False + tag type + case foo: q_obj_TestStruct-wrapper +enum TestIfUnionKind ['foo'] object TestStruct member integer: int optional=False member boolean: bool optional=False @@ -172,6 +188,12 @@ object q_obj_EVENT_D-arg member b: str optional=False member c: str optional=True member enum3: EnumOne optional=True +object q_obj_TestIfCmd-arg + member foo: TestIfStruct optional=False +object q_obj_TestIfEvent-arg + member foo: TestIfStruct optional=False +object q_obj_TestStruct-wrapper + member data: TestStruct optional=False object q_obj_UserDefFlatUnion2-base member integer: int optional=True member string: str optional=False