mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-27 04:13:53 -06:00

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>
115 lines
3.7 KiB
Python
115 lines
3.7 KiB
Python
# This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
# See the COPYING file in the top-level directory.
|
|
|
|
"""
|
|
QAPI Generator
|
|
|
|
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 .backend import QAPIBackend, QAPICBackend
|
|
from .common import must_match
|
|
from .error import QAPIError
|
|
from .schema import QAPISchema
|
|
|
|
|
|
def invalid_prefix_char(prefix: str) -> Optional[str]:
|
|
match = must_match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', prefix)
|
|
if match.end() != len(prefix):
|
|
return prefix[match.end()]
|
|
return None
|
|
|
|
|
|
def create_backend(path: str) -> QAPIBackend:
|
|
if path is None:
|
|
return QAPICBackend()
|
|
|
|
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)
|
|
|
|
try:
|
|
mod = import_module(module_path)
|
|
except Exception as ex:
|
|
print(f"unable to import '{module_path}': {ex}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
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:
|
|
"""
|
|
gapi-gen executable entry point.
|
|
Expects arguments via sys.argv, see --help for details.
|
|
|
|
:return: int, 0 on success, 1 on failure.
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description='Generate code from a QAPI schema')
|
|
parser.add_argument('-b', '--builtins', action='store_true',
|
|
help="generate code for built-in types")
|
|
parser.add_argument('-o', '--output-dir', action='store',
|
|
default='',
|
|
help="write output to directory OUTPUT_DIR")
|
|
parser.add_argument('-p', '--prefix', action='store',
|
|
default='',
|
|
help="prefix for symbols")
|
|
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.
|
|
parser.add_argument('--suppress-tracing', action='store_true',
|
|
help="suppress adding trace events to qmp marshals")
|
|
|
|
parser.add_argument('schema', action='store')
|
|
args = parser.parse_args()
|
|
|
|
funny_char = invalid_prefix_char(args.prefix)
|
|
if funny_char:
|
|
msg = f"funny character '{funny_char}' in argument of --prefix"
|
|
print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
|
|
return 1
|
|
|
|
try:
|
|
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
|
|
return 0
|