mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-01 14:53:54 -06:00
qapi: pluggable backend code generators
The 'qapi.backend.QAPIBackend' class defines an API contract for code generators. The current generator is put into a new class 'qapi.backend.QAPICBackend' and made to be the default impl. A custom generator can be requested using the '-k' arg which takes a fully qualified python class name qapi-gen.py -B the.python.module.QAPIMyBackend This allows out of tree code to use the QAPI generator infrastructure to create new language bindings for QAPI schemas. This has the caveat that the QAPI generator APIs are not guaranteed stable, so consumers of this feature may have to update their code to be compatible with future QEMU releases. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Message-ID: <20250224182030.2089959-1-berrange@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> [Error checking and messages tweaked] Signed-off-by: Markus Armbruster <armbru@redhat.com>
This commit is contained in:
parent
5e4c466e6a
commit
dde279925c
2 changed files with 108 additions and 35 deletions
63
scripts/qapi/backend.py
Normal file
63
scripts/qapi/backend.py
Normal 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)
|
|
@ -8,18 +8,14 @@ This is the main entry point for generating C code from the QAPI schema.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
from importlib import import_module
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from .commands import gen_commands
|
from .backend import QAPIBackend, QAPICBackend
|
||||||
from .common import must_match
|
from .common import must_match
|
||||||
from .error import QAPIError
|
from .error import QAPIError
|
||||||
from .events import gen_events
|
|
||||||
from .features import gen_features
|
|
||||||
from .introspect import gen_introspect
|
|
||||||
from .schema import QAPISchema
|
from .schema import QAPISchema
|
||||||
from .types import gen_types
|
|
||||||
from .visit import gen_visit
|
|
||||||
|
|
||||||
|
|
||||||
def invalid_prefix_char(prefix: str) -> Optional[str]:
|
def invalid_prefix_char(prefix: str) -> Optional[str]:
|
||||||
|
@ -29,32 +25,42 @@ def invalid_prefix_char(prefix: str) -> Optional[str]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate(schema_file: str,
|
def create_backend(path: str) -> QAPIBackend:
|
||||||
output_dir: str,
|
if path is None:
|
||||||
prefix: str,
|
return QAPICBackend()
|
||||||
unmask: bool = False,
|
|
||||||
builtins: bool = False,
|
|
||||||
gen_tracing: bool = False) -> None:
|
|
||||||
"""
|
|
||||||
Generate C code for the given schema into the target directory.
|
|
||||||
|
|
||||||
:param schema_file: The primary QAPI schema file.
|
module_path, dot, class_name = path.rpartition('.')
|
||||||
:param output_dir: The output directory to store generated code.
|
if not dot:
|
||||||
:param prefix: Optional C-code prefix for symbol names.
|
print("argument of -B must be of the form MODULE.CLASS",
|
||||||
:param unmask: Expose non-ABI names through introspection?
|
file=sys.stderr)
|
||||||
:param builtins: Generate code for built-in types?
|
sys.exit(1)
|
||||||
|
|
||||||
:raise QAPIError: On failures.
|
try:
|
||||||
"""
|
mod = import_module(module_path)
|
||||||
assert invalid_prefix_char(prefix) is None
|
except Exception as ex:
|
||||||
|
print(f"unable to import '{module_path}': {ex}", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
schema = QAPISchema(schema_file)
|
try:
|
||||||
gen_types(schema, output_dir, prefix, builtins)
|
klass = getattr(mod, class_name)
|
||||||
gen_features(schema, output_dir, prefix)
|
except AttributeError:
|
||||||
gen_visit(schema, output_dir, prefix, builtins)
|
print(f"module '{module_path}' has no class '{class_name}'",
|
||||||
gen_commands(schema, output_dir, prefix, gen_tracing)
|
file=sys.stderr)
|
||||||
gen_events(schema, output_dir, prefix)
|
sys.exit(1)
|
||||||
gen_introspect(schema, output_dir, prefix, unmask)
|
|
||||||
|
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:
|
def main() -> int:
|
||||||
|
@ -77,6 +83,8 @@ def main() -> int:
|
||||||
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
|
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
|
||||||
dest='unmask',
|
dest='unmask',
|
||||||
help="expose non-ABI names in introspection")
|
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
|
# Option --suppress-tracing exists so we can avoid solving build system
|
||||||
# problems. TODO Drop it when we no longer need it.
|
# problems. TODO Drop it when we no longer need it.
|
||||||
|
@ -93,12 +101,14 @@ def main() -> int:
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
try:
|
try:
|
||||||
generate(args.schema,
|
schema = QAPISchema(args.schema)
|
||||||
output_dir=args.output_dir,
|
backend = create_backend(args.backend)
|
||||||
prefix=args.prefix,
|
backend.generate(schema,
|
||||||
unmask=args.unmask,
|
output_dir=args.output_dir,
|
||||||
builtins=args.builtins,
|
prefix=args.prefix,
|
||||||
gen_tracing=not args.suppress_tracing)
|
unmask=args.unmask,
|
||||||
|
builtins=args.builtins,
|
||||||
|
gen_tracing=not args.suppress_tracing)
|
||||||
except QAPIError as err:
|
except QAPIError as err:
|
||||||
print(err, file=sys.stderr)
|
print(err, file=sys.stderr)
|
||||||
return 1
|
return 1
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue