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

Markus Armbruster <[email protected]> Wed, 25 Feb 2026 07:48:48 +0100
Newsgroups org.nongnu.qemu-rust,org.nongnu.qemu-devel
Message-ID <[email protected]>
Paolo Bonzini <[email protected]> writes:

> From: Marc-AndrĂ© Lureau <[email protected]>
>
> 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 <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Signed-off-by: Paolo Bonzini <[email protected]>
> ---
>  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: