QAPI patches patches for 2021-08-26

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmEnhpoSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZT07MP/RC4fEc4Ov3ozKgUusEFDqPcZ+/yD5k4
 hLHZxqMpJDqrlgQ2Pu4i53sbqlSKnZNuGkmlz3Nv8bz8CWwlfxvuMM6urGQjbOms
 O5MdLivka/giBNasxIUcE/kRWz2WF8/LovanS73gKP3jMkouYJarvMm0PGcOyb33
 hXU/NbURmq8TyR67OK4ymtqGYwI04hGf3TSFx2qWc5dx50C4BAOyfHencG2OY1lI
 taG+aubLLS14eoVTFQyQOrABCv5tgZaO04DFc1XOP+L0dnuH6VEK8z6xwe6GWKmI
 kAeRL3R0g8UsvnkyxNRkdME/ZOuRmd5ianX0PnXl7iSWDmWpLAr8OWq7AEnlBb8w
 NuDia3jc85uHRXEyHy4+030MM7OvuB2cdr0mqnebn2gMBSg8E2KR+y+A/oHEUJfD
 FJ4und6LsGDoMHW2WQiYGN9qCGsyGpkx037BoS+HSpiVNEOSMCmGzSqQsbUGVIsK
 fOM2SuffrgH555kHaB5OL3l50SOO/oHXn5riyItJFuv1yGioxVAUYoerwiZPhNXd
 dltWQtARHnGnIW5u/BQufIAvYyT5wvLEXYZt1NzyjUQ5Eb0G8OmVkdsEh0NQcbv3
 gVsj/2I+VofX/dZBWPv7/L4azKHqDPdCZ00OiKaRs44X1DBPoXToh4KBFL/HG6fL
 z/+5pczSpwSc
 =+x2Q
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-08-26' into staging

QAPI patches patches for 2021-08-26

# gpg: Signature made Thu 26 Aug 2021 13:18:34 BST
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2021-08-26:
  qapi: make 'if' condition strings simple identifiers
  qapi: add 'not' condition operation
  qapi: Use 'if': { 'any': ... } where appropriate
  qapi: add 'any' condition
  qapi: replace if condition list with dict {'all': [...]}
  qapidoc: introduce QAPISchemaIfCond.docgen()
  qapi: introduce QAPISchemaIfCond.cgen()
  qapi: add QAPISchemaIfCond.is_present()
  qapi: wrap Sequence[str] in an object
  docs: update the documentation upfront about schema configuration
  qapi: Fix crash on redefinition with a different condition

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
staging
Peter Maydell 2021-08-26 13:42:34 +01:00
commit c83fcfaf8a
53 changed files with 505 additions and 344 deletions

View File

@ -826,25 +826,31 @@ Configuring the schema
Syntax::
COND = STRING
| [ STRING, ... ]
| { 'all: [ COND, ... ] }
| { 'any: [ COND, ... ] }
| { 'not': COND }
All definitions take an optional 'if' member. 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 definition
will then be guarded by #if STRING for each STRING in the COND list.
string, or an object with a single member 'all', 'any' or 'not'.
The C code generated for the definition will then be guarded by an #if
preprocessing directive with an operand generated from that condition:
* STRING will generate defined(STRING)
* { 'all': [COND, ...] } will generate (COND && ...)
* { 'any': [COND, ...] } will generate (COND || ...)
* { 'not': COND } will generate !COND
Example: a conditional struct ::
{ 'struct': 'IfStruct', 'data': { 'foo': 'int' },
'if': ['defined(CONFIG_FOO)', 'defined(HAVE_BAR)'] }
'if': { 'all': [ 'CONFIG_FOO', 'HAVE_BAR' ] } }
gets its generated code guarded like this::
#if defined(CONFIG_FOO)
#if defined(HAVE_BAR)
#if defined(CONFIG_FOO) && defined(HAVE_BAR)
... generated code ...
#endif /* defined(HAVE_BAR) */
#endif /* defined(CONFIG_FOO) */
#endif /* defined(HAVE_BAR) && defined(CONFIG_FOO) */
Individual members of complex types, commands arguments, and
event-specific data can also be made conditional. This requires the
@ -855,7 +861,7 @@ member 'bar' ::
{ 'struct': 'IfStruct', 'data':
{ 'foo': 'int',
'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } }
'bar': { 'type': 'int', 'if': 'IFCOND'} } }
A union's discriminator may not be conditional.
@ -867,7 +873,7 @@ value 'bar' ::
{ 'enum': 'IfEnum', 'data':
[ 'foo',
{ 'name' : 'bar', 'if': 'defined(IFCOND)' } ] }
{ 'name' : 'bar', 'if': 'IFCOND' } ] }
Likewise, features can be conditional. This requires the longhand
form of FEATURE_.
@ -877,7 +883,7 @@ Example: a struct with conditional feature 'allow-negative-numbers' ::
{ 'struct': 'TestType',
'data': { 'number': 'int' },
'features': [ { 'name': 'allow-negative-numbers',
'if': 'defined(IFCOND)' } ] }
'if': 'IFCOND' } ] }
Please note that you are responsible to ensure that the C code will
compile with an arbitrary combination of conditions, since the

View File

@ -112,17 +112,19 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
def _nodes_for_ifcond(self, ifcond, with_if=True):
"""Return list of Text, literal nodes for the ifcond
Return a list which gives text like ' (If: cond1, cond2, cond3)', where
the conditions are in literal-text and the commas are not.
Return a list which gives text like ' (If: condition)'.
If with_if is False, we don't return the "(If: " and ")".
"""
condlist = intersperse([nodes.literal('', c) for c in ifcond],
nodes.Text(', '))
doc = ifcond.docgen()
if not doc:
return []
doc = nodes.literal('', doc)
if not with_if:
return condlist
return [doc]
nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')]
nodelist.extend(condlist)
nodelist.append(doc)
nodelist.append(nodes.Text(')'))
return nodelist
@ -139,7 +141,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
term.append(nodes.literal('', member.type.doc_type()))
if member.optional:
term.append(nodes.Text(' (optional)'))
if member.ifcond:
if member.ifcond.is_present():
term.extend(self._nodes_for_ifcond(member.ifcond))
return term
@ -154,7 +156,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
nodes.literal('', variants.tag_member.name),
nodes.Text(' is '),
nodes.literal('', '"%s"' % variant.name)]
if variant.ifcond:
if variant.ifcond.is_present():
term.extend(self._nodes_for_ifcond(variant.ifcond))
return term
@ -209,7 +211,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
dlnode = nodes.definition_list()
for section in doc.args.values():
termtext = [nodes.literal('', section.member.name)]
if section.member.ifcond:
if section.member.ifcond.is_present():
termtext.extend(self._nodes_for_ifcond(section.member.ifcond))
# TODO drop fallbacks when undocumented members are outlawed
if section.text:
@ -277,7 +279,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
def _nodes_for_if_section(self, ifcond):
"""Return list of doctree nodes for the "If" section"""
nodelist = []
if ifcond:
if ifcond.is_present():
snode = self._make_section('If')
snode += nodes.paragraph(
'', '', *self._nodes_for_ifcond(ifcond, with_if=False)

View File

@ -914,7 +914,7 @@
'data': {
'file': 'BlockStatsSpecificFile',
'host_device': { 'type': 'BlockStatsSpecificFile',
'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' },
'if': 'HAVE_HOST_BLOCK_DEVICE' },
'nvme': 'BlockStatsSpecificNvme' } }
##
@ -2796,7 +2796,7 @@
##
{ 'enum': 'BlockdevAioOptions',
'data': [ 'threads', 'native',
{ 'name': 'io_uring', 'if': 'defined(CONFIG_LINUX_IO_URING)' } ] }
{ 'name': 'io_uring', 'if': 'CONFIG_LINUX_IO_URING' } ] }
##
# @BlockdevCacheOptions:
@ -2832,12 +2832,12 @@
'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps',
'gluster',
{'name': 'host_cdrom', 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' },
{'name': 'host_device', 'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' },
{'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
{'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
'http', 'https', 'iscsi',
'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
##
@ -2879,10 +2879,10 @@
'*locking': 'OnOffAuto',
'*aio': 'BlockdevAioOptions',
'*drop-cache': {'type': 'bool',
'if': 'defined(CONFIG_LINUX)'},
'if': 'CONFIG_LINUX'},
'*x-check-cache-dropped': 'bool' },
'features': [ { 'name': 'dynamic-auto-read-only',
'if': 'defined(CONFIG_POSIX)' } ] }
'if': 'CONFIG_POSIX' } ] }
##
# @BlockdevOptionsNull:
@ -3774,7 +3774,7 @@
# Since: 2.9
##
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @BlockdevOptionsReplication:
@ -3793,7 +3793,7 @@
'base': 'BlockdevOptionsGenericFormat',
'data': { 'mode': 'ReplicationMode',
'*top-id': 'str' },
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @NFSTransport:
@ -4108,9 +4108,9 @@
'ftps': 'BlockdevOptionsCurlFtps',
'gluster': 'BlockdevOptionsGluster',
'host_cdrom': { 'type': 'BlockdevOptionsFile',
'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' },
'if': 'HAVE_HOST_BLOCK_DEVICE' },
'host_device': { 'type': 'BlockdevOptionsFile',
'if': 'defined(HAVE_HOST_BLOCK_DEVICE)' },
'if': 'HAVE_HOST_BLOCK_DEVICE' },
'http': 'BlockdevOptionsCurlHttp',
'https': 'BlockdevOptionsCurlHttps',
'iscsi': 'BlockdevOptionsIscsi',
@ -4129,7 +4129,7 @@
'raw': 'BlockdevOptionsRaw',
'rbd': 'BlockdevOptionsRbd',
'replication': { 'type': 'BlockdevOptionsReplication',
'if': 'defined(CONFIG_REPLICATION)' },
'if': 'CONFIG_REPLICATION' },
'ssh': 'BlockdevOptionsSsh',
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
@ -4307,8 +4307,8 @@
# @size: Size of the virtual disk in bytes
# @preallocation: Preallocation mode for the new image (default: off;
# allowed values: off,
# falloc (if defined CONFIG_POSIX_FALLOCATE),
# full (if defined CONFIG_POSIX))
# falloc (if CONFIG_POSIX_FALLOCATE),
# full (if CONFIG_POSIX))
# @nocow: Turn off copy-on-write (valid only on btrfs; default: off)
# @extent-size-hint: Extent size hint to add to the image file; 0 for not
# adding an extent size hint (default: 1 MB, since 5.1)
@ -4331,8 +4331,8 @@
# @size: Size of the virtual disk in bytes
# @preallocation: Preallocation mode for the new image (default: off;
# allowed values: off,
# falloc (if defined CONFIG_GLUSTERFS_FALLOCATE),
# full (if defined CONFIG_GLUSTERFS_ZEROFILL))
# falloc (if CONFIG_GLUSTERFS_FALLOCATE),
# full (if CONFIG_GLUSTERFS_ZEROFILL))
#
# Since: 2.12
##
@ -4432,7 +4432,7 @@
# Since: 5.1
##
{ 'enum': 'Qcow2CompressionType',
'data': [ 'zlib', { 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] }
'data': [ 'zlib', { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
##
# @BlockdevCreateOptionsQcow2:

View File

@ -168,7 +168,7 @@
'data': { 'mountpoint': 'str',
'*growable': 'bool',
'*allow-other': 'FuseExportAllowOther' },
'if': 'defined(CONFIG_FUSE)' }
'if': 'CONFIG_FUSE' }
##
# @NbdServerAddOptions:
@ -278,7 +278,7 @@
##
{ 'enum': 'BlockExportType',
'data': [ 'nbd', 'vhost-user-blk',
{ 'name': 'fuse', 'if': 'defined(CONFIG_FUSE)' } ] }
{ 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] }
##
# @BlockExportOptions:
@ -321,7 +321,7 @@
'nbd': 'BlockExportOptionsNbd',
'vhost-user-blk': 'BlockExportOptionsVhostUserBlk',
'fuse': { 'type': 'BlockExportOptionsFuse',
'if': 'defined(CONFIG_FUSE)' }
'if': 'CONFIG_FUSE' }
} }
##

View File

@ -342,7 +342,7 @@
{ 'struct': 'ChardevSpiceChannel',
'data': { 'type': 'str' },
'base': 'ChardevCommon',
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @ChardevSpicePort:
@ -356,7 +356,7 @@
{ 'struct': 'ChardevSpicePort',
'data': { 'fqdn': 'str' },
'base': 'ChardevCommon',
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @ChardevVC:
@ -405,7 +405,7 @@
'data': { '*mouse': 'bool',
'*clipboard': 'bool' },
'base': 'ChardevCommon',
'if': 'defined(CONFIG_SPICE_PROTOCOL)' }
'if': 'CONFIG_SPICE_PROTOCOL' }
##
# @ChardevBackend:
@ -431,11 +431,11 @@
'stdio': 'ChardevStdio',
'console': 'ChardevCommon',
'spicevmc': { 'type': 'ChardevSpiceChannel',
'if': 'defined(CONFIG_SPICE)' },
'if': 'CONFIG_SPICE' },
'spiceport': { 'type': 'ChardevSpicePort',
'if': 'defined(CONFIG_SPICE)' },
'if': 'CONFIG_SPICE' },
'qemu-vdagent': { 'type': 'ChardevQemuVDAgent',
'if': 'defined(CONFIG_SPICE_PROTOCOL)' },
'if': 'CONFIG_SPICE_PROTOCOL' },
'vc': 'ChardevVC',
'ringbuf': 'ChardevRingbuf',
# next one is just for compatibility

View File

@ -89,7 +89,7 @@
##
{ 'struct': 'CpuModelBaselineInfo',
'data': { 'model': 'CpuModelInfo' },
'if': 'defined(TARGET_S390X)' }
'if': 'TARGET_S390X' }
##
# @CpuModelCompareInfo:
@ -112,7 +112,7 @@
{ 'struct': 'CpuModelCompareInfo',
'data': { 'result': 'CpuModelCompareResult',
'responsible-properties': ['str'] },
'if': 'defined(TARGET_S390X)' }
'if': 'TARGET_S390X' }
##
# @query-cpu-model-comparison:
@ -156,7 +156,7 @@
{ 'command': 'query-cpu-model-comparison',
'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' },
'returns': 'CpuModelCompareInfo',
'if': 'defined(TARGET_S390X)' }
'if': 'TARGET_S390X' }
##
# @query-cpu-model-baseline:
@ -200,7 +200,7 @@
'data': { 'modela': 'CpuModelInfo',
'modelb': 'CpuModelInfo' },
'returns': 'CpuModelBaselineInfo',
'if': 'defined(TARGET_S390X)' }
'if': 'TARGET_S390X' }
##
# @CpuModelExpansionInfo:
@ -213,7 +213,9 @@
##
{ 'struct': 'CpuModelExpansionInfo',
'data': { 'model': 'CpuModelInfo' },
'if': 'defined(TARGET_S390X) || defined(TARGET_I386) || defined(TARGET_ARM)' }
'if': { 'any': [ 'TARGET_S390X',
'TARGET_I386',
'TARGET_ARM' ] } }
##
# @query-cpu-model-expansion:
@ -252,7 +254,9 @@
'data': { 'type': 'CpuModelExpansionType',
'model': 'CpuModelInfo' },
'returns': 'CpuModelExpansionInfo',
'if': 'defined(TARGET_S390X) || defined(TARGET_I386) || defined(TARGET_ARM)' }
'if': { 'any': [ 'TARGET_S390X',
'TARGET_I386',
'TARGET_ARM' ] } }
##
# @CpuDefinitionInfo:
@ -316,7 +320,11 @@
'typename': 'str',
'*alias-of' : 'str',
'deprecated' : 'bool' },
'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' }
'if': { 'any': [ 'TARGET_PPC',
'TARGET_ARM',
'TARGET_I386',
'TARGET_S390X',
'TARGET_MIPS' ] } }
##
# @query-cpu-definitions:
@ -328,4 +336,8 @@
# Since: 1.2
##
{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'],
'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' }
'if': { 'any': [ 'TARGET_PPC',
'TARGET_ARM',
'TARGET_I386',
'TARGET_S390X',
'TARGET_MIPS' ] } }

View File

@ -533,7 +533,7 @@
##
{ 'enum': 'MultiFDCompression',
'data': [ 'none', 'zlib',
{ 'name': 'zstd', 'if': 'defined(CONFIG_ZSTD)' } ] }
{ 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] }
##
# @BitmapMigrationBitmapAliasTransform:
@ -1562,7 +1562,7 @@
##
{ 'command': 'xen-set-replication',
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @ReplicationStatus:
@ -1578,7 +1578,7 @@
##
{ 'struct': 'ReplicationStatus',
'data': { 'error': 'bool', '*desc': 'str' },
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @query-xen-replication-status:
@ -1596,7 +1596,7 @@
##
{ 'command': 'query-xen-replication-status',
'returns': 'ReplicationStatus',
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @xen-colo-do-checkpoint:
@ -1613,7 +1613,7 @@
# Since: 2.9
##
{ 'command': 'xen-colo-do-checkpoint',
'if': 'defined(CONFIG_REPLICATION)' }
'if': 'CONFIG_REPLICATION' }
##
# @COLOStatus:

View File

@ -23,7 +23,17 @@
##
{ 'event': 'RTC_CHANGE',
'data': { 'offset': 'int' },
'if': 'defined(TARGET_ALPHA) || defined(TARGET_ARM) || defined(TARGET_HPPA) || defined(TARGET_I386) || defined(TARGET_MIPS) || defined(TARGET_MIPS64) || defined(TARGET_PPC) || defined(TARGET_PPC64) || defined(TARGET_S390X) || defined(TARGET_SH4) || defined(TARGET_SPARC)' }
'if': { 'any': [ 'TARGET_ALPHA',
'TARGET_ARM',
'TARGET_HPPA',
'TARGET_I386',
'TARGET_MIPS',
'TARGET_MIPS64',
'TARGET_PPC',
'TARGET_PPC64',
'TARGET_S390X',
'TARGET_SH4',
'TARGET_SPARC' ] } }
##
# @rtc-reset-reinjection:
@ -42,7 +52,7 @@
#
##
{ 'command': 'rtc-reset-reinjection',
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
@ -69,7 +79,7 @@
{ 'enum': 'SevState',
'data': ['uninit', 'launch-update', 'launch-secret', 'running',
'send-update', 'receive-update' ],
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @SevInfo:
@ -101,7 +111,7 @@
'state' : 'SevState',
'handle' : 'uint32'
},
'if': 'defined(TARGET_I386)'
'if': 'TARGET_I386'
}
##
@ -122,7 +132,7 @@
#
##
{ 'command': 'query-sev', 'returns': 'SevInfo',
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
@ -136,7 +146,7 @@
#
##
{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'},
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @query-sev-launch-measure:
@ -154,7 +164,7 @@
#
##
{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo',
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
@ -179,7 +189,7 @@
'cert-chain': 'str',
'cbitpos': 'int',
'reduced-phys-bits': 'int'},
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @query-sev-capabilities:
@ -199,7 +209,7 @@
#
##
{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability',
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @sev-inject-launch-secret:
@ -217,7 +227,7 @@
##
{ 'command': 'sev-inject-launch-secret',
'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' },
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @dump-skeys:
@ -239,7 +249,7 @@
##
{ 'command': 'dump-skeys',
'data': { 'filename': 'str' },
'if': 'defined(TARGET_S390X)' }
'if': 'TARGET_S390X' }
##
# @GICCapability:
@ -264,7 +274,7 @@
'data': { 'version': 'int',
'emulated': 'bool',
'kernel': 'bool' },
'if': 'defined(TARGET_ARM)' }
'if': 'TARGET_ARM' }
##
# @query-gic-capabilities:
@ -284,7 +294,7 @@
#
##
{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'],
'if': 'defined(TARGET_ARM)' }
'if': 'TARGET_ARM' }
##
@ -300,7 +310,7 @@
##
{ 'struct': 'SevAttestationReport',
'data': { 'data': 'str'},
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }
##
# @query-sev-attestation-report:
@ -322,4 +332,4 @@
##
{ 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' },
'returns': 'SevAttestationReport',
'if': 'defined(TARGET_I386)' }
'if': 'TARGET_I386' }

View File

@ -618,7 +618,7 @@
'data': { '*align': 'size',
'*discard-data': 'bool',
'mem-path': 'str',
'*pmem': { 'type': 'bool', 'if': 'defined(CONFIG_LIBPMEM)' },
'*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' },
'*readonly': 'bool' } }
##
@ -782,7 +782,7 @@
'cryptodev-backend',
'cryptodev-backend-builtin',
{ 'name': 'cryptodev-vhost-user',
'if': 'defined(CONFIG_VHOST_CRYPTO)' },
'if': 'CONFIG_VHOST_CRYPTO' },
'dbus-vmstate',
'filter-buffer',
'filter-dump',
@ -795,7 +795,7 @@
'iothread',
'memory-backend-file',
{ 'name': 'memory-backend-memfd',
'if': 'defined(CONFIG_LINUX)' },
'if': 'CONFIG_LINUX' },
'memory-backend-ram',
'pef-guest',
'pr-manager-helper',
@ -840,7 +840,7 @@
'cryptodev-backend': 'CryptodevBackendProperties',
'cryptodev-backend-builtin': 'CryptodevBackendProperties',
'cryptodev-vhost-user': { 'type': 'CryptodevVhostUserProperties',
'if': 'defined(CONFIG_VHOST_CRYPTO)' },
'if': 'CONFIG_VHOST_CRYPTO' },
'dbus-vmstate': 'DBusVMStateProperties',
'filter-buffer': 'FilterBufferProperties',
'filter-dump': 'FilterDumpProperties',
@ -853,7 +853,7 @@
'iothread': 'IothreadProperties',
'memory-backend-file': 'MemoryBackendFileProperties',
'memory-backend-memfd': { 'type': 'MemoryBackendMemfdProperties',
'if': 'defined(CONFIG_LINUX)' },
'if': 'CONFIG_LINUX' },
'memory-backend-ram': 'MemoryBackendProperties',
'pr-manager-helper': 'PrManagerHelperProperties',
'qtest': 'QtestProperties',

View File

@ -69,7 +69,7 @@
'*ipv4': 'bool',
'*ipv6': 'bool',
'*keep-alive': 'bool',
'*mptcp': { 'type': 'bool', 'if': 'defined(IPPROTO_MPTCP)' } } }
'*mptcp': { 'type': 'bool', 'if': 'IPPROTO_MPTCP' } } }
##
# @UnixSocketAddress:
@ -89,8 +89,8 @@
{ 'struct': 'UnixSocketAddress',
'data': {
'path': 'str',
'*abstract': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' },
'*tight': { 'type': 'bool', 'if': 'defined(CONFIG_LINUX)' } } }
'*abstract': { 'type': 'bool', 'if': 'CONFIG_LINUX' },
'*tight': { 'type': 'bool', 'if': 'CONFIG_LINUX' } } }
##
# @VsockSocketAddress:

View File

@ -18,7 +18,7 @@
# Since: 1.5
##
{ 'enum': 'TpmModel', 'data': [ 'tpm-tis', 'tpm-crb', 'tpm-spapr' ],
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @query-tpm-models:
@ -36,7 +36,7 @@
#
##
{ 'command': 'query-tpm-models', 'returns': ['TpmModel'],
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @TpmType:
@ -50,7 +50,7 @@
# Since: 1.5
##
{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ],
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @query-tpm-types:
@ -68,7 +68,7 @@
#
##
{ 'command': 'query-tpm-types', 'returns': ['TpmType'],
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @TPMPassthroughOptions:
@ -85,7 +85,7 @@
{ 'struct': 'TPMPassthroughOptions',
'data': { '*path': 'str',
'*cancel-path': 'str' },
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @TPMEmulatorOptions:
@ -97,7 +97,7 @@
# Since: 2.11
##
{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' },
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @TpmTypeOptions:
@ -112,7 +112,7 @@
{ 'union': 'TpmTypeOptions',
'data': { 'passthrough' : 'TPMPassthroughOptions',
'emulator': 'TPMEmulatorOptions' },
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @TPMInfo:
@ -131,7 +131,7 @@
'data': {'id': 'str',
'model': 'TpmModel',
'options': 'TpmTypeOptions' },
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }
##
# @query-tpm:
@ -162,4 +162,4 @@
#
##
{ 'command': 'query-tpm', 'returns': ['TPMInfo'],
'if': 'defined(CONFIG_TPM)' }
'if': 'CONFIG_TPM' }

View File

@ -123,7 +123,7 @@
'data': { 'host': 'str',
'port': 'str',
'family': 'NetworkAddressFamily' },
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SpiceServerInfo:
@ -137,7 +137,7 @@
{ 'struct': 'SpiceServerInfo',
'base': 'SpiceBasicInfo',
'data': { '*auth': 'str' },
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SpiceChannel:
@ -163,7 +163,7 @@
'base': 'SpiceBasicInfo',
'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
'tls': 'bool'},
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SpiceQueryMouseMode:
@ -183,7 +183,7 @@
##
{ 'enum': 'SpiceQueryMouseMode',
'data': [ 'client', 'server', 'unknown' ],
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SpiceInfo:
@ -222,7 +222,7 @@
'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
'*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']},
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @query-spice:
@ -268,7 +268,7 @@
#
##
{ 'command': 'query-spice', 'returns': 'SpiceInfo',
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SPICE_CONNECTED:
@ -294,7 +294,7 @@
{ 'event': 'SPICE_CONNECTED',
'data': { 'server': 'SpiceBasicInfo',
'client': 'SpiceBasicInfo' },
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SPICE_INITIALIZED:
@ -323,7 +323,7 @@
{ 'event': 'SPICE_INITIALIZED',
'data': { 'server': 'SpiceServerInfo',
'client': 'SpiceChannel' },
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SPICE_DISCONNECTED:
@ -349,7 +349,7 @@
{ 'event': 'SPICE_DISCONNECTED',
'data': { 'server': 'SpiceBasicInfo',
'client': 'SpiceBasicInfo' },
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# @SPICE_MIGRATE_COMPLETED:
@ -365,7 +365,7 @@
#
##
{ 'event': 'SPICE_MIGRATE_COMPLETED',
'if': 'defined(CONFIG_SPICE)' }
'if': 'CONFIG_SPICE' }
##
# == VNC
@ -393,7 +393,7 @@
'service': 'str',
'family': 'NetworkAddressFamily',
'websocket': 'bool' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncServerInfo:
@ -408,7 +408,7 @@
{ 'struct': 'VncServerInfo',
'base': 'VncBasicInfo',
'data': { '*auth': 'str' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncClientInfo:
@ -426,7 +426,7 @@
{ 'struct': 'VncClientInfo',
'base': 'VncBasicInfo',
'data': { '*x509_dname': 'str', '*sasl_username': 'str' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncInfo:
@ -469,7 +469,7 @@
'data': {'enabled': 'bool', '*host': 'str',
'*family': 'NetworkAddressFamily',
'*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']},
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncPrimaryAuth:
@ -481,7 +481,7 @@
{ 'enum': 'VncPrimaryAuth',
'data': [ 'none', 'vnc', 'ra2', 'ra2ne', 'tight', 'ultra',
'tls', 'vencrypt', 'sasl' ],
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncVencryptSubAuth:
@ -496,7 +496,7 @@
'tls-vnc', 'x509-vnc',
'tls-plain', 'x509-plain',
'tls-sasl', 'x509-sasl' ],
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncServerInfo2:
@ -514,7 +514,7 @@
'base': 'VncBasicInfo',
'data': { 'auth' : 'VncPrimaryAuth',
'*vencrypt' : 'VncVencryptSubAuth' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VncInfo2:
@ -547,7 +547,7 @@
'auth' : 'VncPrimaryAuth',
'*vencrypt' : 'VncVencryptSubAuth',
'*display' : 'str' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @query-vnc:
@ -579,7 +579,7 @@
#
##
{ 'command': 'query-vnc', 'returns': 'VncInfo',
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @query-vnc-servers:
#
@ -590,7 +590,7 @@
# Since: 2.3
##
{ 'command': 'query-vnc-servers', 'returns': ['VncInfo2'],
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @change-vnc-password:
@ -606,7 +606,7 @@
##
{ 'command': 'change-vnc-password',
'data': { 'password': 'str' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VNC_CONNECTED:
@ -636,7 +636,7 @@
{ 'event': 'VNC_CONNECTED',
'data': { 'server': 'VncServerInfo',
'client': 'VncBasicInfo' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VNC_INITIALIZED:
@ -664,7 +664,7 @@
{ 'event': 'VNC_INITIALIZED',
'data': { 'server': 'VncServerInfo',
'client': 'VncClientInfo' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# @VNC_DISCONNECTED:
@ -691,7 +691,7 @@
{ 'event': 'VNC_DISCONNECTED',
'data': { 'server': 'VncServerInfo',
'client': 'VncClientInfo' },
'if': 'defined(CONFIG_VNC)' }
'if': 'CONFIG_VNC' }
##
# = Input
@ -1133,13 +1133,13 @@
'data' : [
{ 'name': 'default' },
{ 'name': 'none' },
{ 'name': 'gtk', 'if': 'defined(CONFIG_GTK)' },
{ 'name': 'sdl', 'if': 'defined(CONFIG_SDL)' },
{ 'name': 'gtk', 'if': 'CONFIG_GTK' },
{ 'name': 'sdl', 'if': 'CONFIG_SDL' },
{ 'name': 'egl-headless',
'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' },
{ 'name': 'curses', 'if': 'defined(CONFIG_CURSES)' },
{ 'name': 'cocoa', 'if': 'defined(CONFIG_COCOA)' },
{ 'name': 'spice-app', 'if': 'defined(CONFIG_SPICE)'} ] }
'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } },
{ 'name': 'curses', 'if': 'CONFIG_CURSES' },
{ 'name': 'cocoa', 'if': 'CONFIG_COCOA' },
{ 'name': 'spice-app', 'if': 'CONFIG_SPICE'} ] }
##
# @DisplayOptions:
@ -1164,10 +1164,10 @@
'*gl' : 'DisplayGLMode' },
'discriminator' : 'type',
'data' : {
'gtk': { 'type': 'DisplayGTK', 'if': 'defined(CONFIG_GTK)' },
'curses': { 'type': 'DisplayCurses', 'if': 'defined(CONFIG_CURSES)' },
'gtk': { 'type': 'DisplayGTK', 'if': 'CONFIG_GTK' },
'curses': { 'type': 'DisplayCurses', 'if': 'CONFIG_CURSES' },
'egl-headless': { 'type': 'DisplayEGLHeadless',
'if': 'defined(CONFIG_OPENGL) && defined(CONFIG_GBM)' }
'if': { 'all': ['CONFIG_OPENGL', 'CONFIG_GBM'] } }
}
}

View File

@ -1380,7 +1380,7 @@
'data': {
'keys': ['str']
},
'if': 'defined(CONFIG_POSIX)' }
'if': 'CONFIG_POSIX' }
##
@ -1398,7 +1398,7 @@
{ 'command': 'guest-ssh-get-authorized-keys',
'data': { 'username': 'str' },
'returns': 'GuestAuthorizedKeys',
'if': 'defined(CONFIG_POSIX)' }
'if': 'CONFIG_POSIX' }
##
# @guest-ssh-add-authorized-keys:
@ -1416,7 +1416,7 @@
##
{ 'command': 'guest-ssh-add-authorized-keys',
'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' },
'if': 'defined(CONFIG_POSIX)' }
'if': 'CONFIG_POSIX' }
##
# @guest-ssh-remove-authorized-keys:
@ -1434,4 +1434,4 @@
##
{ 'command': 'guest-ssh-remove-authorized-keys',
'data': { 'username': 'str', 'keys': ['str'] },
'if': 'defined(CONFIG_POSIX)' }
'if': 'CONFIG_POSIX' }

View File

@ -17,7 +17,6 @@ from typing import (
Dict,
List,
Optional,
Sequence,
Set,
)
@ -31,6 +30,7 @@ from .gen import (
from .schema import (
QAPISchema,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
QAPISchemaType,
)
@ -301,7 +301,7 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
def visit_command(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
arg_type: Optional[QAPISchemaObjectType],
ret_type: Optional[QAPISchemaType],

View File

@ -12,7 +12,13 @@
# See the COPYING file in the top-level directory.
import re
from typing import Match, Optional, Sequence
from typing import (
Any,
Dict,
Match,
Optional,
Union,
)
#: Magic string that gets removed along with all space to its right.
@ -194,22 +200,49 @@ def guardend(name: str) -> str:
name=c_fname(name).upper())
def gen_if(ifcond: Sequence[str]) -> str:
ret = ''
for ifc in ifcond:
ret += mcgen('''
def cgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
if not ifcond:
return ''
if isinstance(ifcond, str):
return 'defined(' + ifcond + ')'
oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + cgen_ifcond(operands)
oper = {'all': '&&', 'any': '||'}[oper]
operands = [cgen_ifcond(o) for o in operands]
return '(' + (') ' + oper + ' (').join(operands) + ')'
def docgen_ifcond(ifcond: Union[str, Dict[str, Any]]) -> str:
# TODO Doc generated for conditions needs polish
if not ifcond:
return ''
if isinstance(ifcond, str):
return ifcond
oper, operands = next(iter(ifcond.items()))
if oper == 'not':
return '!' + docgen_ifcond(operands)
oper = {'all': ' and ', 'any': ' or '}[oper]
operands = [docgen_ifcond(o) for o in operands]
return '(' + oper.join(operands) + ')'
def gen_if(cond: str) -> str:
if not cond:
return ''
return mcgen('''
#if %(cond)s
''', cond=ifc)
return ret
''', cond=cond)
def gen_endif(ifcond: Sequence[str]) -> str:
ret = ''
for ifc in reversed(ifcond):
ret += mcgen('''
def gen_endif(cond: str) -> str:
if not cond:
return ''
return mcgen('''
#endif /* %(cond)s */
''', cond=ifc)
return ret
''', cond=cond)
def must_match(pattern: str, string: str) -> Match[str]:

View File

@ -12,7 +12,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory.
"""
from typing import List, Optional, Sequence
from typing import List, Optional
from .common import c_enum_const, c_name, mcgen
from .gen import QAPISchemaModularCVisitor, build_params, ifcontext
@ -20,6 +20,7 @@ from .schema import (
QAPISchema,
QAPISchemaEnumMember,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
)
from .source import QAPISourceInfo
@ -227,7 +228,7 @@ void %(event_emit)s(%(event_enum)s event, QDict *qdict);
def visit_event(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
arg_type: Optional[QAPISchemaObjectType],
boxed: bool) -> None:

View File

@ -259,14 +259,9 @@ def check_flags(expr: _JSONObject, info: QAPISourceInfo) -> None:
def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
"""
Normalize and validate the ``if`` member of an object.
Validate the ``if`` member of an object.
The ``if`` member may be either a ``str`` or a ``List[str]``.
A ``str`` value will be normalized to ``List[str]``.
:forms:
:sugared: ``Union[str, List[str]]``
:canonical: ``List[str]``
The ``if`` member may be either a ``str`` or a dict.
:param expr: The expression containing the ``if`` member to validate.
:param info: QAPI schema source file information.
@ -275,31 +270,49 @@ def check_if(expr: _JSONObject, info: QAPISourceInfo, source: str) -> None:
:raise QAPISemError:
When the "if" member fails validation, or when there are no
non-empty conditions.
:return: None, ``expr`` is normalized in-place as needed.
:return: None
"""
def _check_if(cond: Union[str, object]) -> None:
if isinstance(cond, str):
if not re.match(r'^[A-Z][A-Z0-9_]*$', cond):
raise QAPISemError(
info,
"'if' condition '%s' of %s is not a valid identifier"
% (cond, source))
return
if not isinstance(cond, dict):
raise QAPISemError(
info,
"'if' condition of %s must be a string or an object" % source)
if len(cond) != 1:
raise QAPISemError(
info,
"'if' condition dict of %s must have one key: "
"'all', 'any' or 'not'" % source)
check_keys(cond, info, "'if' condition", [],
["all", "any", "not"])
oper, operands = next(iter(cond.items()))
if not operands:
raise QAPISemError(
info, "'if' condition [] of %s is useless" % source)
if oper == "not":
_check_if(operands)
return
if oper in ("all", "any") and not isinstance(operands, list):
raise QAPISemError(
info, "'%s' condition of %s must be an array" % (oper, source))
for operand in operands:
_check_if(operand)
ifcond = expr.get('if')
if ifcond is None:
return
if isinstance(ifcond, list):
if not ifcond:
raise QAPISemError(
info, "'if' condition [] of %s is useless" % source)
else:
# Normalize to a list
ifcond = expr['if'] = [ifcond]
for elt in ifcond:
if not isinstance(elt, str):
raise QAPISemError(
info,
"'if' condition of %s must be a string or a list of strings"
% source)
if not elt.strip():
raise QAPISemError(
info,
"'if' condition '%s' of %s makes no sense"
% (elt, source))
_check_if(ifcond)
def normalize_members(members: object) -> None:

View File

@ -18,7 +18,6 @@ from typing import (
Dict,
Iterator,
Optional,
Sequence,
Tuple,
)
@ -32,6 +31,7 @@ from .common import (
mcgen,
)
from .schema import (
QAPISchemaIfCond,
QAPISchemaModule,
QAPISchemaObjectType,
QAPISchemaVisitor,
@ -85,7 +85,7 @@ class QAPIGen:
fp.write(text)
def _wrap_ifcond(ifcond: Sequence[str], before: str, after: str) -> str:
def _wrap_ifcond(ifcond: QAPISchemaIfCond, before: str, after: str) -> str:
if before == after:
return after # suppress empty #if ... #endif
@ -95,9 +95,9 @@ def _wrap_ifcond(ifcond: Sequence[str], before: str, after: str) -> str:
if added[0] == '\n':
out += '\n'
added = added[1:]
out += gen_if(ifcond)
out += gen_if(ifcond.cgen())
out += added
out += gen_endif(ifcond)
out += gen_endif(ifcond.cgen())
return out
@ -127,9 +127,9 @@ def build_params(arg_type: Optional[QAPISchemaObjectType],
class QAPIGenCCode(QAPIGen):
def __init__(self, fname: str):
super().__init__(fname)
self._start_if: Optional[Tuple[Sequence[str], str, str]] = None
self._start_if: Optional[Tuple[QAPISchemaIfCond, str, str]] = None
def start_if(self, ifcond: Sequence[str]) -> None:
def start_if(self, ifcond: QAPISchemaIfCond) -> None:
assert self._start_if is None
self._start_if = (ifcond, self._body, self._preamble)
@ -187,7 +187,7 @@ class QAPIGenH(QAPIGenC):
@contextmanager
def ifcontext(ifcond: Sequence[str], *args: QAPIGenCCode) -> Iterator[None]:
def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]:
"""
A with-statement context manager that wraps with `start_if()` / `end_if()`.

View File

@ -15,11 +15,9 @@ from typing import (
Any,
Dict,
Generic,
Iterable,
List,
Optional,
Sequence,
Tuple,
TypeVar,
Union,
)
@ -38,6 +36,7 @@ from .schema import (
QAPISchemaEntity,
QAPISchemaEnumMember,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
QAPISchemaObjectTypeMember,
QAPISchemaType,
@ -91,11 +90,11 @@ class Annotated(Generic[_ValueT]):
"""
# TODO: Remove after Python 3.7 adds @dataclass:
# pylint: disable=too-few-public-methods
def __init__(self, value: _ValueT, ifcond: Iterable[str],
def __init__(self, value: _ValueT, ifcond: QAPISchemaIfCond,
comment: Optional[str] = None):
self.value = value
self.comment: Optional[str] = comment
self.ifcond: Tuple[str, ...] = tuple(ifcond)
self.ifcond = ifcond
def _tree_to_qlit(obj: JSONValue,
@ -124,11 +123,11 @@ def _tree_to_qlit(obj: JSONValue,
ret = ''
if obj.comment:
ret += indent(level) + f"/* {obj.comment} */\n"
if obj.ifcond:
ret += gen_if(obj.ifcond)
if obj.ifcond.is_present():
ret += gen_if(obj.ifcond.cgen())
ret += _tree_to_qlit(obj.value, level)
if obj.ifcond:
ret += '\n' + gen_endif(obj.ifcond)
if obj.ifcond.is_present():
ret += '\n' + gen_endif(obj.ifcond.cgen())
return ret
ret = ''
@ -254,7 +253,7 @@ const QLitObject %(c_name)s = %(c_string)s;
return [Annotated(f.name, f.ifcond) for f in features]
def _gen_tree(self, name: str, mtype: str, obj: Dict[str, object],
ifcond: Sequence[str] = (),
ifcond: QAPISchemaIfCond = QAPISchemaIfCond(),
features: Sequence[QAPISchemaFeature] = ()) -> None:
"""
Build and append a SchemaInfo object to self._trees.
@ -305,7 +304,7 @@ const QLitObject %(c_name)s = %(c_string)s;
self._gen_tree(name, 'builtin', {'json-type': json_type})
def visit_enum_type(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaEnumMember],
prefix: Optional[str]) -> None:
@ -316,14 +315,14 @@ const QLitObject %(c_name)s = %(c_string)s;
)
def visit_array_type(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
element_type: QAPISchemaType) -> None:
element = self._use_type(element_type)
self._gen_tree('[' + element + ']', 'array', {'element-type': element},
ifcond)
def visit_object_type_flat(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaObjectTypeMember],
variants: Optional[QAPISchemaVariants]) -> None:
@ -336,7 +335,7 @@ const QLitObject %(c_name)s = %(c_string)s;
self._gen_tree(name, 'object', obj, ifcond, features)
def visit_alternate_type(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
variants: QAPISchemaVariants) -> None:
self._gen_tree(
@ -348,7 +347,7 @@ const QLitObject %(c_name)s = %(c_string)s;
)
def visit_command(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
arg_type: Optional[QAPISchemaObjectType],
ret_type: Optional[QAPISchemaType], gen: bool,
@ -367,7 +366,8 @@ const QLitObject %(c_name)s = %(c_string)s;
self._gen_tree(name, 'command', obj, ifcond, features)
def visit_event(self, name: str, info: Optional[QAPISourceInfo],
ifcond: Sequence[str], features: List[QAPISchemaFeature],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
arg_type: Optional[QAPISchemaObjectType],
boxed: bool) -> None:
assert self._schema is not None

View File

@ -19,12 +19,31 @@ import os
import re
from typing import Optional
from .common import POINTER_SUFFIX, c_name
from .common import (
POINTER_SUFFIX,
c_name,
cgen_ifcond,
docgen_ifcond,
)
from .error import QAPIError, QAPISemError, QAPISourceError
from .expr import check_exprs
from .parser import QAPISchemaParser
class QAPISchemaIfCond:
def __init__(self, ifcond=None):
self.ifcond = ifcond or {}
def cgen(self):
return cgen_ifcond(self.ifcond)
def docgen(self):
return docgen_ifcond(self.ifcond)
def is_present(self):
return bool(self.ifcond)
class QAPISchemaEntity:
meta: Optional[str] = None
@ -42,7 +61,7 @@ class QAPISchemaEntity:
# such place).
self.info = info
self.doc = doc
self._ifcond = ifcond or []
self._ifcond = ifcond or QAPISchemaIfCond()
self.features = features or []
self._checked = False
@ -593,7 +612,7 @@ class QAPISchemaVariants:
self.info,
"discriminator member '%s' of %s must not be optional"
% (self._tag_name, base))
if self.tag_member.ifcond:
if self.tag_member.ifcond.is_present():
raise QAPISemError(
self.info,
"discriminator member '%s' of %s must not be conditional"
@ -601,7 +620,7 @@ class QAPISchemaVariants:
else: # simple union
assert isinstance(self.tag_member.type, QAPISchemaEnumType)
assert not self.tag_member.optional
assert self.tag_member.ifcond == []
assert not self.tag_member.ifcond.is_present()
if self._tag_name: # flat union
# branches that are not explicitly covered get an empty type
cases = {v.name for v in self.variants}
@ -646,7 +665,7 @@ class QAPISchemaMember:
assert isinstance(name, str)
self.name = name
self.info = info
self.ifcond = ifcond or []
self.ifcond = ifcond or QAPISchemaIfCond()
self.defined_in = None
def set_defined_in(self, name):
@ -968,11 +987,13 @@ class QAPISchema:
def _make_features(self, features, info):
if features is None:
return []
return [QAPISchemaFeature(f['name'], info, f.get('if'))
return [QAPISchemaFeature(f['name'], info,
QAPISchemaIfCond(f.get('if')))
for f in features]
def _make_enum_members(self, values, info):
return [QAPISchemaEnumMember(v['name'], info, v.get('if'))
return [QAPISchemaEnumMember(v['name'], info,
QAPISchemaIfCond(v.get('if')))
for v in values]
def _make_implicit_enum_type(self, name, info, ifcond, values):
@ -997,18 +1018,18 @@ class QAPISchema:
name = 'q_obj_%s-%s' % (name, role)
typ = self.lookup_entity(name, QAPISchemaObjectType)
if typ:
# The implicit object type has multiple users. This can
# happen only for simple unions' implicit wrapper types.
# Its ifcond should be the disjunction of its user's
# ifconds. Not implemented. Instead, we always pass the
# wrapped type's ifcond, which is trivially the same for all
# users. It's also necessary for the wrapper to compile.
# But it's not tight: the disjunction need not imply it. We
# may end up compiling useless wrapper types.
# The implicit object type has multiple users. This is
# either a duplicate definition (which will be flagged
# later), or an implicit wrapper type used for multiple
# simple unions. In the latter case, ifcond should be the
# disjunction of its user's ifconds. Not implemented.
# Instead, we always pass the wrapped type's ifcond, which
# is trivially the same for all users. It's also
# necessary for the wrapper to compile. But it's not
# tight: the disjunction need not imply it. We may end up
# compiling useless wrapper types.
# TODO kill simple unions or implement the disjunction
# pylint: disable=protected-access
assert (ifcond or []) == typ._ifcond
pass
else:
self._def_entity(QAPISchemaObjectType(
name, info, None, ifcond, None, None, members, None))
@ -1018,7 +1039,7 @@ class QAPISchema:
name = expr['enum']
data = expr['data']
prefix = expr.get('prefix')
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaEnumType(
name, info, doc, ifcond, features,
@ -1036,7 +1057,8 @@ class QAPISchema:
self._make_features(features, info))
def _make_members(self, data, info):
return [self._make_member(key, value['type'], value.get('if'),
return [self._make_member(key, value['type'],
QAPISchemaIfCond(value.get('if')),
value.get('features'), info)
for (key, value) in data.items()]
@ -1044,7 +1066,7 @@ class QAPISchema:
name = expr['struct']
base = expr.get('base')
data = expr['data']
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
self._def_entity(QAPISchemaObjectType(
name, info, doc, ifcond, features, base,
@ -1067,7 +1089,7 @@ class QAPISchema:
name = expr['union']
data = expr['data']
base = expr.get('base')
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
tag_name = expr.get('discriminator')
tag_member = None
@ -1076,15 +1098,19 @@ class QAPISchema:
name, info, ifcond,
'base', self._make_members(base, info))
if tag_name:
variants = [self._make_variant(key, value['type'],
value.get('if'), info)
for (key, value) in data.items()]
variants = [
self._make_variant(key, value['type'],
QAPISchemaIfCond(value.get('if')),
info)
for (key, value) in data.items()]
members = []
else:
variants = [self._make_simple_variant(key, value['type'],
value.get('if'), info)
for (key, value) in data.items()]
enum = [{'name': v.name, 'if': v.ifcond} for v in variants]
variants = [
self._make_simple_variant(key, value['type'],
QAPISchemaIfCond(value.get('if')),
info)
for (key, value) in data.items()]
enum = [{'name': v.name, 'if': v.ifcond.ifcond} for v in variants]
typ = self._make_implicit_enum_type(name, info, ifcond, enum)
tag_member = QAPISchemaObjectTypeMember('type', info, typ, False)
members = [tag_member]
@ -1097,11 +1123,13 @@ class QAPISchema:
def _def_alternate_type(self, expr, info, doc):
name = expr['alternate']
data = expr['data']
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
variants = [self._make_variant(key, value['type'], value.get('if'),
info)
for (key, value) in data.items()]
variants = [
self._make_variant(key, value['type'],
QAPISchemaIfCond(value.get('if')),
info)
for (key, value) in data.items()]
tag_member = QAPISchemaObjectTypeMember('type', info, 'QType', False)
self._def_entity(
QAPISchemaAlternateType(name, info, doc, ifcond, features,
@ -1118,7 +1146,7 @@ class QAPISchema:
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
coroutine = expr.get('coroutine', False)
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
@ -1137,7 +1165,7 @@ class QAPISchema:
name = expr['event']
data = expr.get('data')
boxed = expr.get('boxed', False)
ifcond = expr.get('if')
ifcond = QAPISchemaIfCond(expr.get('if'))
features = self._make_features(expr.get('features'), info)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(

View File

@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
"""
from typing import List, Optional, Sequence
from typing import List, Optional
from .common import (
c_enum_const,
@ -27,6 +27,7 @@ from .schema import (
QAPISchema,
QAPISchemaEnumMember,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
QAPISchemaObjectTypeMember,
QAPISchemaType,
@ -50,13 +51,13 @@ const QEnumLookup %(c_name)s_lookup = {
''',
c_name=c_name(name))
for memb in members:
ret += gen_if(memb.ifcond)
ret += gen_if(memb.ifcond.cgen())
index = c_enum_const(name, memb.name, prefix)
ret += mcgen('''
[%(index)s] = "%(name)s",
''',
index=index, name=memb.name)
ret += gen_endif(memb.ifcond)
ret += gen_endif(memb.ifcond.cgen())
ret += mcgen('''
},
@ -80,12 +81,12 @@ typedef enum %(c_name)s {
c_name=c_name(name))
for memb in enum_members:
ret += gen_if(memb.ifcond)
ret += gen_if(memb.ifcond.cgen())
ret += mcgen('''
%(c_enum)s,
''',
c_enum=c_enum_const(name, memb.name, prefix))
ret += gen_endif(memb.ifcond)
ret += gen_endif(memb.ifcond.cgen())
ret += mcgen('''
} %(c_name)s;
@ -125,7 +126,7 @@ struct %(c_name)s {
def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
ret = ''
for memb in members:
ret += gen_if(memb.ifcond)
ret += gen_if(memb.ifcond.cgen())
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s;
@ -135,11 +136,11 @@ def gen_struct_members(members: List[QAPISchemaObjectTypeMember]) -> str:
%(c_type)s %(c_name)s;
''',
c_type=memb.type.c_type(), c_name=c_name(memb.name))
ret += gen_endif(memb.ifcond)
ret += gen_endif(memb.ifcond.cgen())
return ret
def gen_object(name: str, ifcond: Sequence[str],
def gen_object(name: str, ifcond: QAPISchemaIfCond,
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
variants: Optional[QAPISchemaVariants]) -> str:
@ -158,7 +159,7 @@ def gen_object(name: str, ifcond: Sequence[str],
ret += mcgen('''
''')
ret += gen_if(ifcond)
ret += gen_if(ifcond.cgen())
ret += mcgen('''
struct %(c_name)s {
''',
@ -192,7 +193,7 @@ struct %(c_name)s {
ret += mcgen('''
};
''')
ret += gen_endif(ifcond)
ret += gen_endif(ifcond.cgen())
return ret
@ -219,13 +220,13 @@ def gen_variants(variants: QAPISchemaVariants) -> str:
for var in variants.variants:
if var.type.name == 'q_empty':
continue
ret += gen_if(var.ifcond)
ret += gen_if(var.ifcond.cgen())
ret += mcgen('''
%(c_type)s %(c_name)s;
''',
c_type=var.type.c_unboxed_type(),
c_name=c_name(var.name))
ret += gen_endif(var.ifcond)
ret += gen_endif(var.ifcond.cgen())
ret += mcgen('''
} u;
@ -307,7 +308,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
def visit_enum_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaEnumMember],
prefix: Optional[str]) -> None:
@ -318,7 +319,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
def visit_array_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
element_type: QAPISchemaType) -> None:
with ifcontext(ifcond, self._genh, self._genc):
self._genh.preamble_add(gen_fwd_object_or_array(name))
@ -328,7 +329,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
def visit_object_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
@ -351,7 +352,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
def visit_alternate_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
variants: QAPISchemaVariants) -> None:
with ifcontext(ifcond, self._genh):

View File

@ -13,7 +13,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
See the COPYING file in the top-level directory.
"""
from typing import List, Optional, Sequence
from typing import List, Optional
from .common import (
c_enum_const,
@ -29,6 +29,7 @@ from .schema import (
QAPISchemaEnumMember,
QAPISchemaEnumType,
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaObjectType,
QAPISchemaObjectTypeMember,
QAPISchemaType,
@ -78,7 +79,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
for memb in members:
deprecated = 'deprecated' in [f.name for f in memb.features]
ret += gen_if(memb.ifcond)
ret += gen_if(memb.ifcond.cgen())
if memb.optional:
ret += mcgen('''
if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) {
@ -111,7 +112,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
ret += mcgen('''
}
''')
ret += gen_endif(memb.ifcond)
ret += gen_endif(memb.ifcond.cgen())
if variants:
tag_member = variants.tag_member
@ -125,7 +126,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
for var in variants.variants:
case_str = c_enum_const(tag_member.type.name, var.name,
tag_member.type.prefix)
ret += gen_if(var.ifcond)
ret += gen_if(var.ifcond.cgen())
if var.type.name == 'q_empty':
# valid variant and nothing to do
ret += mcgen('''
@ -141,7 +142,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
case=case_str,
c_type=var.type.c_name(), c_name=c_name(var.name))
ret += gen_endif(var.ifcond)
ret += gen_endif(var.ifcond.cgen())
ret += mcgen('''
default:
abort();
@ -227,7 +228,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
c_name=c_name(name))
for var in variants.variants:
ret += gen_if(var.ifcond)
ret += gen_if(var.ifcond.cgen())
ret += mcgen('''
case %(case)s:
''',
@ -253,7 +254,7 @@ bool visit_type_%(c_name)s(Visitor *v, const char *name,
ret += mcgen('''
break;
''')
ret += gen_endif(var.ifcond)
ret += gen_endif(var.ifcond.cgen())
ret += mcgen('''
case QTYPE_NONE:
@ -352,7 +353,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
def visit_enum_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
members: List[QAPISchemaEnumMember],
prefix: Optional[str]) -> None:
@ -363,7 +364,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
def visit_array_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
element_type: QAPISchemaType) -> None:
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_visit_decl(name))
@ -372,7 +373,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
def visit_object_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
base: Optional[QAPISchemaObjectType],
members: List[QAPISchemaObjectTypeMember],
@ -394,7 +395,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
def visit_alternate_type(self,
name: str,
info: Optional[QAPISourceInfo],
ifcond: Sequence[str],
ifcond: QAPISchemaIfCond,
features: List[QAPISchemaFeature],
variants: QAPISchemaVariants) -> None:
with ifcontext(ifcond, self._genh, self._genc):

View File

@ -1,2 +1,2 @@
alternate-branch-if-invalid.json: In alternate 'Alt':
alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' makes no sense
alternate-branch-if-invalid.json:2: 'if' condition ' ' of 'data' member 'branch' is not a valid identifier

View File

@ -0,0 +1,2 @@
bad-if-all.json: In struct 'TestIfStruct':
bad-if-all.json:2: 'all' condition of struct must be an array

View File

@ -0,0 +1,3 @@
# check 'if all' is not a list
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': { 'all': 'ALL' } }

View File

View File

@ -1,3 +1,3 @@
# check empty 'if' list
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': [] }
'if': { 'all': [] } }

View File

@ -1,2 +1,2 @@
bad-if-empty.json: In struct 'TestIfStruct':
bad-if-empty.json:2: 'if' condition '' of struct makes no sense
bad-if-empty.json:2: 'if' condition '' of struct is not a valid identifier

View File

@ -0,0 +1,3 @@
bad-if-key.json: In struct 'TestIfStruct':
bad-if-key.json:2: 'if' condition has unknown key 'value'
Valid keys are 'all', 'any', 'not'.

View File

@ -0,0 +1,3 @@
# check unknown 'if' dict key
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': { 'value': 'defined(TEST_IF_STRUCT)' } }

View File

View File

@ -0,0 +1,2 @@
bad-if-keys.json: In struct 'TestIfStruct':
bad-if-keys.json:2: 'if' condition dict of struct must have one key: 'all', 'any' or 'not'

View File

@ -0,0 +1,3 @@
# check multiple 'if' keys
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': { 'any': ['ANY'], 'all': ['ALL'] } }

View File

View File

@ -1,2 +1,2 @@
bad-if-list.json: In struct 'TestIfStruct':
bad-if-list.json:2: 'if' condition ' ' of struct makes no sense
bad-if-list.json:2: 'if' condition 'foo' of struct is not a valid identifier

View File

@ -1,3 +1,3 @@
# check invalid 'if' content
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': ['foo', ' '] }
'if': { 'all': ['foo', ' '] } }

View File

@ -1,2 +1,2 @@
bad-if.json: In struct 'TestIfStruct':
bad-if.json:2: 'if' condition of struct must be a string or a list of strings
bad-if.json:2: 'if' condition of struct must be a string or an object

View File

@ -1,3 +1,3 @@
# check invalid 'if' type
{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' },
'if': { 'value': 'defined(TEST_IF_STRUCT)' } }
'if': ['TEST_IF_STRUCT'] }

View File

@ -61,16 +61,17 @@
# @two is undocumented
##
{ 'enum': 'Enum', 'data':
[ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ],
[ { 'name': 'one', 'if': 'IFONE' }, 'two' ],
'features': [ 'enum-feat' ],
'if': 'defined(IFCOND)' }
'if': 'IFCOND' }
##
# @Base:
# @base1:
# the first member
##
{ 'struct': 'Base', 'data': { 'base1': 'Enum' } }
{ 'struct': 'Base', 'data': { 'base1': 'Enum' },
'if': { 'all': ['IFALL1', 'IFALL2'] } }
##
# @Variant1:
@ -86,7 +87,7 @@
'features': [ 'variant1-feat' ],
'data': { 'var1': { 'type': 'str',
'features': [ 'member-feat' ],
'if': 'defined(IFSTR)' } } }
'if': 'IFSTR' } } }
##
# @Variant2:
@ -102,7 +103,9 @@
'features': [ 'union-feat1' ],
'base': 'Base',
'discriminator': 'base1',
'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } }
'data': { 'one': 'Variant1',
'two': { 'type': 'Variant2',
'if': { 'any': ['IFONE', 'IFTWO'] } } } }
##
# @SugaredUnion:
@ -123,7 +126,8 @@
##
{ 'alternate': 'Alternate',
'features': [ 'alt-feat' ],
'data': { 'i': 'int', 'b': 'bool' } }
'data': { 'i': 'int', 'b': 'bool' },
'if': { 'not': 'IFNOT' } }
##
# == Another subsection

View File

@ -12,15 +12,16 @@ enum QType
module doc-good.json
enum Enum
member one
if ['defined(IFONE)']
if IFONE
member two
if ['defined(IFCOND)']
if IFCOND
feature enum-feat
object Base
member base1: Enum optional=False
if OrderedDict([('all', ['IFALL1', 'IFALL2'])])
object Variant1
member var1: str optional=False
if ['defined(IFSTR)']
if IFSTR
feature member-feat
feature variant1-feat
object Variant2
@ -29,7 +30,7 @@ object Object
tag base1
case one: Variant1
case two: Variant2
if ['IFTWO']
if OrderedDict([('any', ['IFONE', 'IFTWO'])])
feature union-feat1
object q_obj_Variant1-wrapper
member data: Variant1 optional=False
@ -38,18 +39,19 @@ object q_obj_Variant2-wrapper
enum SugaredUnionKind
member one
member two
if ['IFTWO']
if IFTWO
object SugaredUnion
member type: SugaredUnionKind optional=False
tag type
case one: q_obj_Variant1-wrapper
case two: q_obj_Variant2-wrapper
if ['IFTWO']
if IFTWO
feature union-feat2
alternate Alternate
tag type
case i: int
case b: bool
if OrderedDict([('not', 'IFNOT')])
feature alt-feat
object q_obj_cmd-arg
member arg1: int optional=False

View File

@ -43,7 +43,7 @@ Example:
Values
~~~~~~
"one" (**If: **"defined(IFONE)")
"one" (**If: **"IFONE")
The _one_ {and only}
"two"
@ -62,7 +62,7 @@ Features
If
~~
"defined(IFCOND)"
"IFCOND"
"Base" (Object)
@ -76,6 +76,12 @@ Members
the first member
If
~~
"(IFALL1 and IFALL2)"
"Variant1" (Object)
-------------------
@ -87,7 +93,7 @@ Another paragraph (but no "var": line)
Members
~~~~~~~
"var1": "string" (**If: **"defined(IFSTR)")
"var1": "string" (**If: **"IFSTR")
Not documented
@ -114,7 +120,8 @@ Members
The members of "Base"
The members of "Variant1" when "base1" is ""one""
The members of "Variant2" when "base1" is ""two"" (**If: **"IFTWO")
The members of "Variant2" when "base1" is ""two"" (**If: **"(IFONE or
IFTWO)")
Features
~~~~~~~~
@ -164,6 +171,12 @@ Features
a feature
If
~~
"!IFNOT"
Another subsection
==================

View File

@ -1,2 +1,3 @@
enum-if-invalid.json: In enum 'TestIfEnum':
enum-if-invalid.json:2: 'if' condition of 'data' member 'bar' must be a string or a list of strings
enum-if-invalid.json:2: 'if' condition has unknown key 'val'
Valid keys are 'all', 'any', 'not'.

View File

@ -1,2 +1,2 @@
features-if-invalid.json: In struct 'Stru':
features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or a list of strings
features-if-invalid.json:2: 'if' condition of 'features' member 'f' must be a string or an object

View File

@ -1,3 +1,3 @@
{ 'struct': 'FeatureStruct0',
'data': { 'foo': 'int' },
'features': [ { 'if': 'defined(NAMELESS_FEATURES)' } ] }
'features': [ { 'if': 'NAMELESS_FEATURES' } ] }

View File

@ -37,8 +37,11 @@ schemas = [
'bad-data.json',
'bad-ident.json',
'bad-if.json',
'bad-if-all.json',
'bad-if-empty.json',
'bad-if-empty-list.json',
'bad-if-key.json',
'bad-if-keys.json',
'bad-if-list.json',
'bad-type-bool.json',
'bad-type-dict.json',

View File

@ -222,44 +222,45 @@
{ 'struct': 'TestIfStruct', 'data':
{ 'foo': 'int',
'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} },
'if': 'defined(TEST_IF_STRUCT)' }
'bar': { 'type': 'int', 'if': 'TEST_IF_STRUCT_BAR'} },
'if': 'TEST_IF_STRUCT' }
{ 'enum': 'TestIfEnum', 'data':
[ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ],
'if': 'defined(TEST_IF_ENUM)' }
[ 'foo', { 'name' : 'bar', 'if': 'TEST_IF_ENUM_BAR' } ],
'if': 'TEST_IF_ENUM' }
{ 'union': 'TestIfUnion', 'data':
{ 'foo': 'TestStruct',
'bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} },
'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' }
'bar': { 'type': 'str', 'if': 'TEST_IF_UNION_BAR'} },
'if': { 'all': ['TEST_IF_UNION', 'TEST_IF_STRUCT'] } }
{ 'command': 'test-if-union-cmd',
'data': { 'union-cmd-arg': 'TestIfUnion' },
'if': 'defined(TEST_IF_UNION)' }
'if': 'TEST_IF_UNION' }
{ 'alternate': 'TestIfAlternate', 'data':
{ 'foo': 'int',
'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} },
'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' }
'bar': { 'type': 'TestStruct', 'if': 'TEST_IF_ALT_BAR'} },
'if': { 'all': ['TEST_IF_ALT', 'TEST_IF_STRUCT'] } }
{ 'command': 'test-if-alternate-cmd',
'data': { 'alt-cmd-arg': 'TestIfAlternate' },
'if': 'defined(TEST_IF_ALT)' }
'if': { 'all': ['TEST_IF_ALT',
{'not': 'TEST_IF_NOT_ALT'}] } }
{ 'command': 'test-if-cmd',
'data': {
'foo': 'TestIfStruct',
'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } },
'bar': { 'type': 'TestIfEnum', 'if': 'TEST_IF_CMD_BAR' } },
'returns': 'UserDefThree',
'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] }
'if': { 'all': ['TEST_IF_CMD', 'TEST_IF_STRUCT'] } }
{ 'command': 'test-cmd-return-def-three', 'returns': 'UserDefThree' }
{ 'event': 'TEST_IF_EVENT', 'data':
{ 'foo': 'TestIfStruct',
'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } },
'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' }
'bar': { 'type': ['TestIfEnum'], 'if': 'TEST_IF_EVT_BAR' } },
'if': { 'all': ['TEST_IF_EVT', 'TEST_IF_STRUCT'] } }
# test 'features'
@ -281,15 +282,21 @@
{ 'struct': 'CondFeatureStruct1',
'data': { 'foo': 'int' },
'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] }
'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] }
{ 'struct': 'CondFeatureStruct2',
'data': { 'foo': 'int' },
'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'},
{ 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] }
'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'},
{ 'name': 'feature2', 'if': 'TEST_IF_FEATURE_2'} ] }
{ 'struct': 'CondFeatureStruct3',
'data': { 'foo': 'int' },
'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)',
'defined(TEST_IF_COND_2)'] } ] }
'features': [ { 'name': 'feature1',
'if': { 'all': [ 'TEST_IF_COND_1',
'TEST_IF_COND_2'] } } ] }
{ 'struct': 'CondFeatureStruct4',
'data': { 'foo': 'int' },
'features': [ { 'name': 'feature1',
'if': {'any': ['TEST_IF_COND_1',
'TEST_IF_COND_2'] } } ] }
{ 'enum': 'FeatureEnum1',
'data': [ 'eins', 'zwei', 'drei' ],
@ -313,7 +320,8 @@
'*fs4': 'FeatureStruct4',
'*cfs1': 'CondFeatureStruct1',
'*cfs2': 'CondFeatureStruct2',
'*cfs3': 'CondFeatureStruct3' },
'*cfs3': 'CondFeatureStruct3',
'*cfs4': 'CondFeatureStruct4' },
'returns': 'FeatureStruct1',
'features': [] }
@ -323,13 +331,14 @@
'features': [ 'feature1', 'feature2' ] }
{ 'command': 'test-command-cond-features1',
'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'} ] }
'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] }
{ 'command': 'test-command-cond-features2',
'features': [ { 'name': 'feature1', 'if': 'defined(TEST_IF_FEATURE_1)'},
{ 'name': 'feature2', 'if': 'defined(TEST_IF_FEATURE_2)'} ] }
'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'},
{ 'name': 'feature2', 'if': 'TEST_IF_FEATURE_2'} ] }
{ 'command': 'test-command-cond-features3',
'features': [ { 'name': 'feature1', 'if': [ 'defined(TEST_IF_COND_1)',
'defined(TEST_IF_COND_2)'] } ] }
'features': [ { 'name': 'feature1',
'if': { 'all': [ 'TEST_IF_COND_1',
'TEST_IF_COND_2'] } } ] }
{ 'event': 'TEST_EVENT_FEATURES0',
'data': 'FeatureStruct1' }

View File

@ -298,65 +298,65 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio
object TestIfStruct
member foo: int optional=False
member bar: int optional=False
if ['defined(TEST_IF_STRUCT_BAR)']
if ['defined(TEST_IF_STRUCT)']
if TEST_IF_STRUCT_BAR
if TEST_IF_STRUCT
enum TestIfEnum
member foo
member bar
if ['defined(TEST_IF_ENUM_BAR)']
if ['defined(TEST_IF_ENUM)']
if TEST_IF_ENUM_BAR
if TEST_IF_ENUM
object q_obj_TestStruct-wrapper
member data: TestStruct optional=False
enum TestIfUnionKind
member foo
member bar
if ['defined(TEST_IF_UNION_BAR)']
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
if TEST_IF_UNION_BAR
if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])])
object TestIfUnion
member type: TestIfUnionKind optional=False
tag type
case foo: q_obj_TestStruct-wrapper
case bar: q_obj_str-wrapper
if ['defined(TEST_IF_UNION_BAR)']
if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)']
if TEST_IF_UNION_BAR
if OrderedDict([('all', ['TEST_IF_UNION', 'TEST_IF_STRUCT'])])
object q_obj_test-if-union-cmd-arg
member union-cmd-arg: TestIfUnion optional=False
if ['defined(TEST_IF_UNION)']
if TEST_IF_UNION
command test-if-union-cmd q_obj_test-if-union-cmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_UNION)']
if TEST_IF_UNION
alternate TestIfAlternate
tag type
case foo: int
case bar: TestStruct
if ['defined(TEST_IF_ALT_BAR)']
if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)']
if TEST_IF_ALT_BAR
if OrderedDict([('all', ['TEST_IF_ALT', 'TEST_IF_STRUCT'])])
object q_obj_test-if-alternate-cmd-arg
member alt-cmd-arg: TestIfAlternate optional=False
if ['defined(TEST_IF_ALT)']
if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])])
command test-if-alternate-cmd q_obj_test-if-alternate-cmd-arg -> None
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_ALT)']
if OrderedDict([('all', ['TEST_IF_ALT', OrderedDict([('not', 'TEST_IF_NOT_ALT')])])])
object q_obj_test-if-cmd-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnum optional=False
if ['defined(TEST_IF_CMD_BAR)']
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
if TEST_IF_CMD_BAR
if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])])
command test-if-cmd q_obj_test-if-cmd-arg -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)']
if OrderedDict([('all', ['TEST_IF_CMD', 'TEST_IF_STRUCT'])])
command test-cmd-return-def-three None -> UserDefThree
gen=True success_response=True boxed=False oob=False preconfig=False
array TestIfEnumList TestIfEnum
if ['defined(TEST_IF_ENUM)']
if TEST_IF_ENUM
object q_obj_TEST_IF_EVENT-arg
member foo: TestIfStruct optional=False
member bar: TestIfEnumList optional=False
if ['defined(TEST_IF_EVT_BAR)']
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
if TEST_IF_EVT_BAR
if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])])
event TEST_IF_EVENT q_obj_TEST_IF_EVENT-arg
boxed=False
if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)']
if OrderedDict([('all', ['TEST_IF_EVT', 'TEST_IF_STRUCT'])])
object FeatureStruct0
member foo: int optional=False
object FeatureStruct1
@ -379,17 +379,21 @@ object FeatureStruct4
object CondFeatureStruct1
member foo: int optional=False
feature feature1
if ['defined(TEST_IF_FEATURE_1)']
if TEST_IF_FEATURE_1
object CondFeatureStruct2
member foo: int optional=False
feature feature1
if ['defined(TEST_IF_FEATURE_1)']
if TEST_IF_FEATURE_1
feature feature2
if ['defined(TEST_IF_FEATURE_2)']
if TEST_IF_FEATURE_2
object CondFeatureStruct3
member foo: int optional=False
feature feature1
if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)']
if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
object CondFeatureStruct4
member foo: int optional=False
feature feature1
if OrderedDict([('any', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
enum FeatureEnum1
member eins
member zwei
@ -417,6 +421,7 @@ object q_obj_test-features0-arg
member cfs1: CondFeatureStruct1 optional=True
member cfs2: CondFeatureStruct2 optional=True
member cfs3: CondFeatureStruct3 optional=True
member cfs4: CondFeatureStruct4 optional=True
command test-features0 q_obj_test-features0-arg -> FeatureStruct1
gen=True success_response=True boxed=False oob=False preconfig=False
command test-command-features1 None -> None
@ -429,17 +434,17 @@ command test-command-features3 None -> None
command test-command-cond-features1 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if ['defined(TEST_IF_FEATURE_1)']
if TEST_IF_FEATURE_1
command test-command-cond-features2 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if ['defined(TEST_IF_FEATURE_1)']
if TEST_IF_FEATURE_1
feature feature2
if ['defined(TEST_IF_FEATURE_2)']
if TEST_IF_FEATURE_2
command test-command-cond-features3 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature feature1
if ['defined(TEST_IF_COND_1)', 'defined(TEST_IF_COND_2)']
if OrderedDict([('all', ['TEST_IF_COND_1', 'TEST_IF_COND_2'])])
event TEST_EVENT_FEATURES0 FeatureStruct1
boxed=False
event TEST_EVENT_FEATURES1 None

View File

@ -1,3 +1,3 @@
# we reject duplicate events
{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
{ 'event': 'EVENT_A', 'data': { 'myint': 'int' } }
{ 'event': 'EVENT_A', 'data': { 'myint': 'int' }, 'if': 'FOO' }

View File

@ -1,2 +1,2 @@
struct-member-if-invalid.json: In struct 'Stru':
struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or a list of strings
struct-member-if-invalid.json:2: 'if' condition of 'data' member 'member' must be a string or an object

View File

@ -94,8 +94,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
@staticmethod
def _print_if(ifcond, indent=4):
if ifcond:
print('%sif %s' % (' ' * indent, ifcond))
if ifcond.is_present():
print('%sif %s' % (' ' * indent, ifcond.ifcond))
@classmethod
def _print_features(cls, features, indent=4):

View File

@ -1,2 +1,2 @@
union-branch-if-invalid.json: In union 'Uni':
union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' makes no sense
union-branch-if-invalid.json:4: 'if' condition '' of 'data' member 'branch1' is not a valid identifier

View File

@ -3,4 +3,4 @@
{ 'struct': 'Stru', 'data': { 'member': 'str' } }
{ 'union': 'Uni',
'base': { 'tag': 'Branches' }, 'discriminator': 'tag',
'data': { 'branch1': { 'type': 'Stru', 'if': [''] } } }
'data': { 'branch1': { 'type': 'Stru', 'if': { 'all': [''] } } } }

View File

@ -51,6 +51,7 @@ FeatureStruct1 *qmp_test_features0(bool has_fs0, FeatureStruct0 *fs0,
bool has_cfs1, CondFeatureStruct1 *cfs1,
bool has_cfs2, CondFeatureStruct2 *cfs2,
bool has_cfs3, CondFeatureStruct3 *cfs3,
bool has_cfs4, CondFeatureStruct4 *cfs4,
Error **errp)
{
return g_new0(FeatureStruct1, 1);