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