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.
|
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 ==
|
== Client JSON Protocol introspection ==
|
||||||
|
|
||||||
Clients of a Client JSON Protocol commonly need to figure out what
|
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
|
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,
|
def check_type(info, source, value, allow_array=False,
|
||||||
allow_dict=False, allow_optional=False,
|
allow_dict=False, allow_optional=False,
|
||||||
allow_metas=[]):
|
allow_metas=[]):
|
||||||
@ -871,6 +892,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
|
|||||||
raise QAPISemError(info,
|
raise QAPISemError(info,
|
||||||
"'%s' of %s '%s' should only use true value"
|
"'%s' of %s '%s' should only use true value"
|
||||||
% (key, meta, name))
|
% (key, meta, name))
|
||||||
|
if key == 'if':
|
||||||
|
check_if(expr, info)
|
||||||
for key in required:
|
for key in required:
|
||||||
if key not in expr:
|
if key not in expr:
|
||||||
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
|
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
|
||||||
@ -899,28 +922,28 @@ def check_exprs(exprs):
|
|||||||
|
|
||||||
if 'enum' in expr:
|
if 'enum' in expr:
|
||||||
meta = 'enum'
|
meta = 'enum'
|
||||||
check_keys(expr_elem, 'enum', ['data'], ['prefix'])
|
check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
|
||||||
enum_types[expr[meta]] = expr
|
enum_types[expr[meta]] = expr
|
||||||
elif 'union' in expr:
|
elif 'union' in expr:
|
||||||
meta = 'union'
|
meta = 'union'
|
||||||
check_keys(expr_elem, 'union', ['data'],
|
check_keys(expr_elem, 'union', ['data'],
|
||||||
['base', 'discriminator'])
|
['base', 'discriminator', 'if'])
|
||||||
union_types[expr[meta]] = expr
|
union_types[expr[meta]] = expr
|
||||||
elif 'alternate' in expr:
|
elif 'alternate' in expr:
|
||||||
meta = 'alternate'
|
meta = 'alternate'
|
||||||
check_keys(expr_elem, 'alternate', ['data'])
|
check_keys(expr_elem, 'alternate', ['data'], ['if'])
|
||||||
elif 'struct' in expr:
|
elif 'struct' in expr:
|
||||||
meta = 'struct'
|
meta = 'struct'
|
||||||
check_keys(expr_elem, 'struct', ['data'], ['base'])
|
check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
|
||||||
struct_types[expr[meta]] = expr
|
struct_types[expr[meta]] = expr
|
||||||
elif 'command' in expr:
|
elif 'command' in expr:
|
||||||
meta = 'command'
|
meta = 'command'
|
||||||
check_keys(expr_elem, 'command', [],
|
check_keys(expr_elem, 'command', [],
|
||||||
['data', 'returns', 'gen', 'success-response',
|
['data', 'returns', 'gen', 'success-response',
|
||||||
'boxed', 'allow-oob', 'allow-preconfig'])
|
'boxed', 'allow-oob', 'allow-preconfig', 'if'])
|
||||||
elif 'event' in expr:
|
elif 'event' in expr:
|
||||||
meta = 'event'
|
meta = 'event'
|
||||||
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
|
check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
|
||||||
else:
|
else:
|
||||||
raise QAPISemError(expr_elem['info'],
|
raise QAPISemError(expr_elem['info'],
|
||||||
"Expression is missing metatype")
|
"Expression is missing metatype")
|
||||||
|
@ -442,6 +442,10 @@ qapi-schema += args-unknown.json
|
|||||||
qapi-schema += bad-base.json
|
qapi-schema += bad-base.json
|
||||||
qapi-schema += bad-data.json
|
qapi-schema += bad-data.json
|
||||||
qapi-schema += bad-ident.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-bool.json
|
||||||
qapi-schema += bad-type-dict.json
|
qapi-schema += bad-type-dict.json
|
||||||
qapi-schema += bad-type-int.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',
|
'data': { 'string0': 'str',
|
||||||
'dict1': 'UserDefTwoDict' } }
|
'dict1': 'UserDefTwoDict' } }
|
||||||
|
|
||||||
|
{ 'struct': 'UserDefThree',
|
||||||
|
'data': { 'string0': 'str' } }
|
||||||
|
|
||||||
# dummy struct to force generation of array types not otherwise mentioned
|
# dummy struct to force generation of array types not otherwise mentioned
|
||||||
{ 'struct': 'ForceArrays',
|
{ 'struct': 'ForceArrays',
|
||||||
'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
|
'data': { 'unused1':['UserDefOne'], 'unused2':['UserDefTwo'],
|
||||||
@ -193,3 +196,26 @@
|
|||||||
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
|
'data': { 'a': ['__org.qemu_x-Enum'], 'b': ['__org.qemu_x-Struct'],
|
||||||
'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
|
'c': '__org.qemu_x-Union2', 'd': '__org.qemu_x-Alt' },
|
||||||
'returns': '__org.qemu_x-Union1' }
|
'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
|
object UserDefTwo
|
||||||
member string0: str optional=False
|
member string0: str optional=False
|
||||||
member dict1: UserDefTwoDict optional=False
|
member dict1: UserDefTwoDict optional=False
|
||||||
|
object UserDefThree
|
||||||
|
member string0: str optional=False
|
||||||
object ForceArrays
|
object ForceArrays
|
||||||
member unused1: UserDefOneList optional=False
|
member unused1: UserDefOneList optional=False
|
||||||
member unused2: UserDefTwoList 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
|
member d: __org.qemu_x-Alt optional=False
|
||||||
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
|
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
|
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;
|
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)
|
void qmp_user_def_cmd(Error **errp)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user