QAPI patches patches for 2025-02-26

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmfGpZsSHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTGtQQAKmLIHy1c1T1yU6G9MFrAaFvH0n10UnB
 mQGOvcV+8f01KxUUIvVRBD9DyRl+kJGMelZoYuMpt+vNiO5XDMqZarJUYFOYukMW
 6e/qrD3AMeyI0HGZYtELUENmuKLeAhhdE9d1PO56owR2jiFDxdzW+9TXkEXa1NHS
 c54v1y5HPgIdTXd11aXWI+06S6OVSpBteUOQTiSLowBq5BC28DoFAgViA1jZ1qM+
 1PxL4IV4QkYXfEW2ORt/auamxFWuLNBYNWnyqhR+OYeQjWWAe2A0cmcoLIHVKFwE
 zcuVCXt9+8nZi3Fyx24yOineAhAi29qWxRnbXLrfa4y8OF3cihY0ZNYxvJF61iel
 Wis9WM78OcuQrHCF3BNPqBm9rrac5mQf5mMk4V75Yc+I4oeJeoDG8cLZF1HMpoBT
 iZAl4aW2aa7JQ778c4RxzuCWQSVzOzm5T72Ez9VDJRAWHxoiumoaIOPW9wDi7jWL
 2P/XxyVCIMsuNLsxkFQEcHwtyK5BYqSf6gCpCbbzTW1YdfDzXQIvrRx4AEuClQvP
 yoLhpv3HjaomWEZbY26gJq1ImSF4uIaLbdOAvAub9wuZWCW0qd/j8PkU/xy0Q0Jx
 gNoNymbRdN5v3eekcZr7L96ILzwMDp2d+zS3q+YgpYUf30INfC7LyTCg6BZ7b06r
 zT05GmiP5DqS
 =LNhH
 -----END PGP SIGNATURE-----

Merge tag 'pull-qapi-2025-02-26-v2' of https://repo.or.cz/qemu/armbru into staging

QAPI patches patches for 2025-02-26

# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmfGpZsSHGFybWJydUBy
# ZWRoYXQuY29tAAoJEDhwtADrkYZTGtQQAKmLIHy1c1T1yU6G9MFrAaFvH0n10UnB
# mQGOvcV+8f01KxUUIvVRBD9DyRl+kJGMelZoYuMpt+vNiO5XDMqZarJUYFOYukMW
# 6e/qrD3AMeyI0HGZYtELUENmuKLeAhhdE9d1PO56owR2jiFDxdzW+9TXkEXa1NHS
# c54v1y5HPgIdTXd11aXWI+06S6OVSpBteUOQTiSLowBq5BC28DoFAgViA1jZ1qM+
# 1PxL4IV4QkYXfEW2ORt/auamxFWuLNBYNWnyqhR+OYeQjWWAe2A0cmcoLIHVKFwE
# zcuVCXt9+8nZi3Fyx24yOineAhAi29qWxRnbXLrfa4y8OF3cihY0ZNYxvJF61iel
# Wis9WM78OcuQrHCF3BNPqBm9rrac5mQf5mMk4V75Yc+I4oeJeoDG8cLZF1HMpoBT
# iZAl4aW2aa7JQ778c4RxzuCWQSVzOzm5T72Ez9VDJRAWHxoiumoaIOPW9wDi7jWL
# 2P/XxyVCIMsuNLsxkFQEcHwtyK5BYqSf6gCpCbbzTW1YdfDzXQIvrRx4AEuClQvP
# yoLhpv3HjaomWEZbY26gJq1ImSF4uIaLbdOAvAub9wuZWCW0qd/j8PkU/xy0Q0Jx
# gNoNymbRdN5v3eekcZr7L96ILzwMDp2d+zS3q+YgpYUf30INfC7LyTCg6BZ7b06r
# zT05GmiP5DqS
# =LNhH
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 04 Mar 2025 15:02:51 HKT
# 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

* tag 'pull-qapi-2025-02-26-v2' of https://repo.or.cz/qemu/armbru:
  qapi: pluggable backend code generators
  docs/qapidoc: remove example section support
  docs/qapidoc: support header-less freeform sections
  qapi: update pylintrc config
  qapi/char.json: minor doc rewording for `hub` device

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2025-03-05 21:53:11 +08:00
commit b93c9dfd70
7 changed files with 120 additions and 44 deletions

View file

@ -254,10 +254,6 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
section += dlnode
return [section]
def _nodes_for_example(self, exampletext):
"""Return list of doctree nodes for a code example snippet"""
return [nodes.literal_block(exampletext, exampletext)]
def _nodes_for_sections(self, doc):
"""Return list of doctree nodes for additional sections"""
nodelist = []
@ -275,10 +271,7 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
continue
snode = self._make_section(section.tag)
if section.tag.startswith('Example'):
snode += self._nodes_for_example(dedent(section.text))
else:
self._parse_text_into_node(dedent(section.text), snode)
self._parse_text_into_node(dedent(section.text), snode)
nodelist.append(snode)
return nodelist
@ -421,6 +414,8 @@ class QAPISchemaGenRSTVisitor(QAPISchemaVisitor):
node = self._start_new_heading(heading, len(leader))
if text == '':
return
else:
node = nodes.container()
self._parse_text_into_node(text, node)
self._cur_doc = None

View file

@ -337,7 +337,7 @@
#
# Configuration info for hub chardevs.
#
# @chardevs: List of chardev IDs, which should be added to this hub
# @chardevs: IDs to be added to this hub (maximum 4 devices).
#
# Since: 10.0
##

63
scripts/qapi/backend.py Normal file
View file

@ -0,0 +1,63 @@
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
from abc import ABC, abstractmethod
from .commands import gen_commands
from .events import gen_events
from .features import gen_features
from .introspect import gen_introspect
from .schema import QAPISchema
from .types import gen_types
from .visit import gen_visit
class QAPIBackend(ABC):
@abstractmethod
def generate(self,
schema: QAPISchema,
output_dir: str,
prefix: str,
unmask: bool,
builtins: bool,
gen_tracing: bool) -> None:
"""
Generate code for the given schema into the target directory.
:param schema: The primary QAPI schema object.
:param output_dir: The output directory to store generated code.
:param prefix: Optional C-code prefix for symbol names.
:param unmask: Expose non-ABI names through introspection?
:param builtins: Generate code for built-in types?
:raise QAPIError: On failures.
"""
class QAPICBackend(QAPIBackend):
def generate(self,
schema: QAPISchema,
output_dir: str,
prefix: str,
unmask: bool,
builtins: bool,
gen_tracing: bool) -> None:
"""
Generate C code for the given schema into the target directory.
:param schema_file: The primary QAPI schema file.
:param output_dir: The output directory to store generated code.
:param prefix: Optional C-code prefix for symbol names.
:param unmask: Expose non-ABI names through introspection?
:param builtins: Generate code for built-in types?
:raise QAPIError: On failures.
"""
gen_types(schema, output_dir, prefix, builtins)
gen_features(schema, output_dir, prefix)
gen_visit(schema, output_dir, prefix, builtins)
gen_commands(schema, output_dir, prefix, gen_tracing)
gen_events(schema, output_dir, prefix)
gen_introspect(schema, output_dir, prefix, unmask)

View file

@ -8,18 +8,14 @@ This is the main entry point for generating C code from the QAPI schema.
"""
import argparse
from importlib import import_module
import sys
from typing import Optional
from .commands import gen_commands
from .backend import QAPIBackend, QAPICBackend
from .common import must_match
from .error import QAPIError
from .events import gen_events
from .features import gen_features
from .introspect import gen_introspect
from .schema import QAPISchema
from .types import gen_types
from .visit import gen_visit
def invalid_prefix_char(prefix: str) -> Optional[str]:
@ -29,32 +25,42 @@ def invalid_prefix_char(prefix: str) -> Optional[str]:
return None
def generate(schema_file: str,
output_dir: str,
prefix: str,
unmask: bool = False,
builtins: bool = False,
gen_tracing: bool = False) -> None:
"""
Generate C code for the given schema into the target directory.
def create_backend(path: str) -> QAPIBackend:
if path is None:
return QAPICBackend()
:param schema_file: The primary QAPI schema file.
:param output_dir: The output directory to store generated code.
:param prefix: Optional C-code prefix for symbol names.
:param unmask: Expose non-ABI names through introspection?
:param builtins: Generate code for built-in types?
module_path, dot, class_name = path.rpartition('.')
if not dot:
print("argument of -B must be of the form MODULE.CLASS",
file=sys.stderr)
sys.exit(1)
:raise QAPIError: On failures.
"""
assert invalid_prefix_char(prefix) is None
try:
mod = import_module(module_path)
except Exception as ex:
print(f"unable to import '{module_path}': {ex}", file=sys.stderr)
sys.exit(1)
schema = QAPISchema(schema_file)
gen_types(schema, output_dir, prefix, builtins)
gen_features(schema, output_dir, prefix)
gen_visit(schema, output_dir, prefix, builtins)
gen_commands(schema, output_dir, prefix, gen_tracing)
gen_events(schema, output_dir, prefix)
gen_introspect(schema, output_dir, prefix, unmask)
try:
klass = getattr(mod, class_name)
except AttributeError:
print(f"module '{module_path}' has no class '{class_name}'",
file=sys.stderr)
sys.exit(1)
try:
backend = klass()
except Exception as ex:
print(f"backend '{path}' cannot be instantiated: {ex}",
file=sys.stderr)
sys.exit(1)
if not isinstance(backend, QAPIBackend):
print(f"backend '{path}' must be an instance of QAPIBackend",
file=sys.stderr)
sys.exit(1)
return backend
def main() -> int:
@ -77,6 +83,8 @@ def main() -> int:
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
dest='unmask',
help="expose non-ABI names in introspection")
parser.add_argument('-B', '--backend', default=None,
help="Python module name for code generator")
# Option --suppress-tracing exists so we can avoid solving build system
# problems. TODO Drop it when we no longer need it.
@ -93,12 +101,14 @@ def main() -> int:
return 1
try:
generate(args.schema,
output_dir=args.output_dir,
prefix=args.prefix,
unmask=args.unmask,
builtins=args.builtins,
gen_tracing=not args.suppress_tracing)
schema = QAPISchema(args.schema)
backend = create_backend(args.backend)
backend.generate(schema,
output_dir=args.output_dir,
prefix=args.prefix,
unmask=args.unmask,
builtins=args.builtins,
gen_tracing=not args.suppress_tracing)
except QAPIError as err:
print(err, file=sys.stderr)
return 1

View file

@ -17,6 +17,7 @@ disable=consider-using-f-string,
too-many-arguments,
too-many-branches,
too-many-instance-attributes,
too-many-positional-arguments,
too-many-statements,
useless-option-value,

View file

@ -11,6 +11,10 @@
# = Section
##
##
# Just text, no heading.
##
##
# == Subsection
#

View file

@ -56,6 +56,9 @@ event EVT_BOXED Object
doc freeform
body=
= Section
doc freeform
body=
Just text, no heading.
doc freeform
body=
== Subsection