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