xref: /qemu/scripts/qapi/main.py (revision f07a5674cf97b8473e5d06d7b1df9b51e97d553f)
1# This work is licensed under the terms of the GNU GPL, version 2 or later.
2# See the COPYING file in the top-level directory.
3
4"""
5QAPI Generator
6
7This is the main entry point for generating C code from the QAPI schema.
8"""
9
10import argparse
11from importlib import import_module
12import sys
13from typing import Optional
14
15from .backend import QAPIBackend, QAPICBackend
16from .common import must_match
17from .error import QAPIError
18from .schema import QAPISchema
19
20
21def invalid_prefix_char(prefix: str) -> Optional[str]:
22    match = must_match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', prefix)
23    if match.end() != len(prefix):
24        return prefix[match.end()]
25    return None
26
27
28def create_backend(path: str) -> QAPIBackend:
29    if path is None:
30        return QAPICBackend()
31
32    module_path, dot, class_name = path.rpartition('.')
33    if not dot:
34        print("argument of -B must be of the form MODULE.CLASS",
35              file=sys.stderr)
36        sys.exit(1)
37
38    try:
39        mod = import_module(module_path)
40    except Exception as ex:
41        print(f"unable to import '{module_path}': {ex}", file=sys.stderr)
42        sys.exit(1)
43
44    try:
45        klass = getattr(mod, class_name)
46    except AttributeError:
47        print(f"module '{module_path}' has no class '{class_name}'",
48              file=sys.stderr)
49        sys.exit(1)
50
51    try:
52        backend = klass()
53    except Exception as ex:
54        print(f"backend '{path}' cannot be instantiated: {ex}",
55              file=sys.stderr)
56        sys.exit(1)
57
58    if not isinstance(backend, QAPIBackend):
59        print(f"backend '{path}' must be an instance of QAPIBackend",
60              file=sys.stderr)
61        sys.exit(1)
62
63    return backend
64
65
66def main() -> int:
67    """
68    gapi-gen executable entry point.
69    Expects arguments via sys.argv, see --help for details.
70
71    :return: int, 0 on success, 1 on failure.
72    """
73    parser = argparse.ArgumentParser(
74        description='Generate code from a QAPI schema')
75    parser.add_argument('-b', '--builtins', action='store_true',
76                        help="generate code for built-in types")
77    parser.add_argument('-o', '--output-dir', action='store',
78                        default='',
79                        help="write output to directory OUTPUT_DIR")
80    parser.add_argument('-p', '--prefix', action='store',
81                        default='',
82                        help="prefix for symbols")
83    parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
84                        dest='unmask',
85                        help="expose non-ABI names in introspection")
86    parser.add_argument('-B', '--backend', default=None,
87                        help="Python module name for code generator")
88
89    # Option --suppress-tracing exists so we can avoid solving build system
90    # problems.  TODO Drop it when we no longer need it.
91    parser.add_argument('--suppress-tracing', action='store_true',
92                        help="suppress adding trace events to qmp marshals")
93
94    parser.add_argument('schema', action='store')
95    args = parser.parse_args()
96
97    funny_char = invalid_prefix_char(args.prefix)
98    if funny_char:
99        msg = f"funny character '{funny_char}' in argument of --prefix"
100        print(f"{sys.argv[0]}: {msg}", file=sys.stderr)
101        return 1
102
103    try:
104        schema = QAPISchema(args.schema)
105        backend = create_backend(args.backend)
106        backend.generate(schema,
107                         output_dir=args.output_dir,
108                         prefix=args.prefix,
109                         unmask=args.unmask,
110                         builtins=args.builtins,
111                         gen_tracing=not args.suppress_tracing)
112    except QAPIError as err:
113        print(err, file=sys.stderr)
114        return 1
115    return 0
116