qapi: add 'if' to top-level expressions
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. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20180703155648.11933-2-marcandre.lureau@redhat.com> [Commit message and Documentation improved] Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
b07cd3e748
commit
967c885108
@ -744,6 +744,35 @@ Example: Red Hat, Inc. controls redhat.com, and may therefore add a
|
||||
downstream command __com.redhat_drive-mirror.
|
||||
|
||||
|
||||
=== Configuring the schema ===
|
||||
|
||||
The 'struct', 'enum', 'union', 'alternate', 'command' and 'event'
|
||||
top-level expressions can take an 'if' key. Its value must be a string
|
||||
or a list of strings. A string is shorthand for a list containing just
|
||||
that string. The code generated for the top-level expression will then
|
||||
be guarded by #if COND for each COND in the list.
|
||||
|
||||
Example: a conditional struct
|
||||
|
||||
{ 'struct': 'IfStruct', 'data': { 'foo': 'int' },
|
||||
'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] }
|
||||
|
||||
gets its generated code guarded like this:
|
||||
|
||||
#if defined(CONFIG_FOO)
|
||||
#if defined(HAVE_BAR)
|
||||
... generated code ...
|
||||
#endif /* defined(HAVE_BAR) */
|
||||
#endif /* defined(CONFIG_FOO) */
|
||||
|
||||
Please note that you are responsible to ensure that the C code will
|
||||
compile with an arbitrary combination of conditions, since the
|
||||
generators are unable to check it at this point.
|
||||
|
||||
The presence of 'if' keys in the schema is reflected through to the
|
||||
introspection output depending on the build configuration.
|
||||
|
||||
|
||||
== Client JSON Protocol introspection ==
|
||||
|
||||
Clients of a Client JSON Protocol commonly need to figure out what
|
||||
|
@ -638,6 +638,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
|
||||
if 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=[]):
|
||||
@ -871,6 +892,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'"
|
||||
@ -899,28 +922,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', 'allow-oob', 'allow-preconfig'])
|
||||
'boxed', 'allow-oob', 'allow-preconfig', '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")
|
||||
|
@ -442,6 +442,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
|
||||
|
1
tests/qapi-schema/bad-if-empty-list.err
Normal file
1
tests/qapi-schema/bad-if-empty-list.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-if-empty-list.json:2: 'if' condition [] is useless
|
1
tests/qapi-schema/bad-if-empty-list.exit
Normal file
1
tests/qapi-schema/bad-if-empty-list.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-if-empty-list.json
Normal file
3
tests/qapi-schema/bad-if-empty-list.json
Normal file
@ -0,0 +1,3 @@
|
||||
# check empty 'if' list
|
||||
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
|
||||
'if': [] }
|
0
tests/qapi-schema/bad-if-empty-list.out
Normal file
0
tests/qapi-schema/bad-if-empty-list.out
Normal file
1
tests/qapi-schema/bad-if-empty.err
Normal file
1
tests/qapi-schema/bad-if-empty.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-if-empty.json:2: 'if' condition '' makes no sense
|
1
tests/qapi-schema/bad-if-empty.exit
Normal file
1
tests/qapi-schema/bad-if-empty.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-if-empty.json
Normal file
3
tests/qapi-schema/bad-if-empty.json
Normal file
@ -0,0 +1,3 @@
|
||||
# check empty 'if'
|
||||
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
|
||||
'if': '' }
|
0
tests/qapi-schema/bad-if-empty.out
Normal file
0
tests/qapi-schema/bad-if-empty.out
Normal file
1
tests/qapi-schema/bad-if-list.err
Normal file
1
tests/qapi-schema/bad-if-list.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-if-list.json:2: 'if' condition '' makes no sense
|
1
tests/qapi-schema/bad-if-list.exit
Normal file
1
tests/qapi-schema/bad-if-list.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-if-list.json
Normal file
3
tests/qapi-schema/bad-if-list.json
Normal file
@ -0,0 +1,3 @@
|
||||
# check invalid 'if' content
|
||||
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
|
||||
'if': ['foo', ''] }
|
0
tests/qapi-schema/bad-if-list.out
Normal file
0
tests/qapi-schema/bad-if-list.out
Normal file
1
tests/qapi-schema/bad-if.err
Normal file
1
tests/qapi-schema/bad-if.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-if.json:2: 'if' condition must be a string or a list of strings
|
1
tests/qapi-schema/bad-if.exit
Normal file
1
tests/qapi-schema/bad-if.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-if.json
Normal file
3
tests/qapi-schema/bad-if.json
Normal file
@ -0,0 +1,3 @@
|
||||
# check invalid 'if' type
|
||||
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
|
||||
'if': { 'value': 'defined(TEST_IF_STRUCT)' } }
|
0
tests/qapi-schema/bad-if.out
Normal file
0
tests/qapi-schema/bad-if.out
Normal file
@ -56,6 +56,9 @@
|
||||
'data': { 'string0': 'str',
|
||||
'dict1': 'UserDefTwoDict' } }
|
||||
|
||||
{ 'struct': 'UserDefThree',
|
||||
'data': { 'string0': 'str' } }
|
||||
|
||||
# dummy struct to force generation of array types not otherwise mentioned
|
||||
{ 'struct': 'ForceArrays',
|
||||
'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
|
||||
@ -193,3 +196,26 @@
|
||||
'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' },
|
||||
'returns': 'UserDefThree',
|
||||
'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
|
||||
|
||||
{ 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' }
|
||||
|
||||
{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' },
|
||||
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
|
||||
|
@ -36,6 +36,8 @@ object UserDefTwoDict
|
||||
object UserDefTwo
|
||||
member string0: str optional=False
|
||||
member dict1: UserDefTwoDict optional=False
|
||||
object UserDefThree
|
||||
member string0: str optional=False
|
||||
object ForceArrays
|
||||
member unused1: UserDefOneList optional=False
|
||||
member unused2: UserDefTwoList optional=False
|
||||
@ -233,3 +235,27 @@ object q_obj___org.qemu_x-command-arg
|
||||
member d: __org.qemu_x-Alt optional=False
|
||||
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
object TestIfStruct
|
||||
member foo: int optional=False
|
||||
enum TestIfEnum ['foo', 'bar']
|
||||
object q_obj_TestStruct-wrapper
|
||||
member data: TestStruct optional=False
|
||||
enum TestIfUnionKind ['foo']
|
||||
object TestIfUnion
|
||||
member type: TestIfUnionKind optional=False
|
||||
tag type
|
||||
case foo: q_obj_TestStruct-wrapper
|
||||
alternate TestIfAlternate
|
||||
tag type
|
||||
case foo: int
|
||||
case bar: TestStruct
|
||||
object q_obj_TestIfCmd-arg
|
||||
member foo: TestIfStruct optional=False
|
||||
command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
command TestCmdReturnDefThree None -> UserDefThree
|
||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||
object q_obj_TestIfEvent-arg
|
||||
member foo: TestIfStruct optional=False
|
||||
event TestIfEvent q_obj_TestIfEvent-arg
|
||||
boxed=False
|
||||
|
@ -12,6 +12,18 @@
|
||||
|
||||
static QmpCommandList qmp_commands;
|
||||
|
||||
/* #if defined(TEST_IF_STRUCT) && defined(TEST_IF_CMD) */
|
||||
UserDefThree *qmp_TestIfCmd(TestIfStruct *foo, Error **errp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
UserDefThree *qmp_TestCmdReturnDefThree(Error **errp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void qmp_user_def_cmd(Error **errp)
|
||||
{
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user