Re: [PATCH v2 09/16] scripts/qapi: add QAPISchemaIfCond.rsgen()

Markus Armbruster
2026-02-25T06:49:10Z

Paolo Bonzini <pbonzini@redhat.com> writes:

> From: Marc-André Lureau <marcandre.lureau@redhat.com>
>
> Generate Rust #[cfg(...)] guards from QAPI 'if' conditions; it
> turns out that they are very similar, with both of them using
> not/any/all, so just walk the tree.
>
> The next commit will put it to use.
>
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> Link: https://lore.kernel.org/r/20210907121943.3498701-15-marcandre.lureau@redhat.com
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
> scripts/qapi/common.py | 19 +++++++++++++++++++
> scripts/qapi/schema.py | 4 ++++
> 2 files changed, 23 insertions(+)
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index d7c8aa3365c..14d5dd259c4 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -199,6 +199,25 @@ def guardend(name: str) -> str:
> name=c_fname(name).upper())
>
>
> +def rsgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
> +
> + def cfg(ifcond: Union[str, Dict[str, Any]]) -> str:
> + if isinstance(ifcond, str):
> + return ifcond
> + assert isinstance(ifcond, dict) and len(ifcond) == 1
> + if 'not' in ifcond:
> + oper = 'not'
> + arg = cfg(ifcond['not'])
> + else:
> + oper, operands = next(iter(ifcond.items()))
> + arg = ', '.join([cfg(c) for c in operands])
> + return f'{oper}({arg})'
> +
> + if not ifcond:
> + return ''
> + return '#[cfg(%s)]' % cfg(ifcond)
> +
> +

Why not reuse the existing tree walker? See appended diff.

> def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
> cond_fmt: str, not_fmt: str,
> all_operator: str, any_operator: str) -> str:
> diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
> index 8d88b40de2e..848a7401251 100644
> --- a/scripts/qapi/schema.py
> +++ b/scripts/qapi/schema.py
> @@ -37,6 +37,7 @@
> docgen_ifcond,
> gen_endif,
> gen_if,
> + rsgen_ifcond,
> )
> from .error import QAPIError, QAPISemError, QAPISourceError
> from .expr import check_exprs
> @@ -63,6 +64,9 @@ def gen_endif(self) -> str:
> def docgen(self) -> str:
> return docgen_ifcond(self.ifcond)
>
> + def rsgen(self) -> str:
> + return rsgen_ifcond(self.ifcond)
> +
> def is_present(self) -> bool:
> return bool(self.ifcond)

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index d7c8aa3365..d8accae835 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -201,7 +201,8 @@ def guardend(name: str) -> str:

def gen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]],
cond_fmt: str, not_fmt: str,
- all_operator: str, any_operator: str) -> str:
+ all_fmt: str, all_sep: str,
+ any_fmt: str, any_sep: str) -> str:

def do_gen(ifcond: Union[str, Dict[str, Any]],
need_parens: bool) -> str:
@@ -211,15 +212,15 @@ def do_gen(ifcond: Union[str, Dict[str, Any]],
if 'not' in ifcond:
return not_fmt % do_gen(ifcond['not'], True)
if 'all' in ifcond:
- gen = gen_infix(all_operator, ifcond['all'])
+ gen = gen_infix(all_fmt, all_sep, ifcond['all'])
else:
- gen = gen_infix(any_operator, ifcond['any'])
+ gen = gen_infix(any_fmt, any_sep, ifcond['any'])
if need_parens:
gen = '(' + gen + ')'
return gen

- def gen_infix(operator: str, operands: Sequence[Any]) -> str:
- return operator.join([do_gen(o, True) for o in operands])
+ def gen_infix(fmt: str, sep: str, operands: Sequence[Any]) -> str:
+ return fmt % sep.join([do_gen(o, True) for o in operands])

if not ifcond:
return ''
@@ -227,12 +228,18 @@ def gen_infix(operator: str, operands: Sequence[Any]) -> str:

def cgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
- return gen_ifcond(ifcond, 'defined(%s)', '!%s', ' && ', ' || ')
+ return gen_ifcond(ifcond, 'defined(%s)', '!%s',
+ '%s', ' && ', '%s', ' || ')
+
+
+def rsgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
+ return '#[cfg(%s)]' % gen_ifcond(ifcond, '%s', 'not(%s)',
+ 'all(%s)', ', ', 'any(%s)', ', ')

def docgen_ifcond(ifcond: Optional[Union[str, Dict[str, Any]]]) -> str:
# TODO Doc generated for conditions needs polish
- return gen_ifcond(ifcond, '%s', 'not %s', ' and ', ' or ')
+ return gen_ifcond(ifcond, '%s', 'not %s', '%s', ' and ', '%s', ' or ')

def gen_if(cond: str) -> str: