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 11*dde27992SDaniel P. Berrangéfrom importlib import import_module 12a76ab215SJohn Snowimport sys 13a76ab215SJohn Snowfrom typing import Optional 14a76ab215SJohn Snow 15*dde27992SDaniel 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 28*dde27992SDaniel P. Berrangédef create_backend(path: str) -> QAPIBackend: 29*dde27992SDaniel P. Berrangé if path is None: 30*dde27992SDaniel P. Berrangé return QAPICBackend() 31a76ab215SJohn Snow 32*dde27992SDaniel P. Berrangé module_path, dot, class_name = path.rpartition('.') 33*dde27992SDaniel P. Berrangé if not dot: 34*dde27992SDaniel P. Berrangé print("argument of -B must be of the form MODULE.CLASS", 35*dde27992SDaniel P. Berrangé file=sys.stderr) 36*dde27992SDaniel P. Berrangé sys.exit(1) 37a76ab215SJohn Snow 38*dde27992SDaniel P. Berrangé try: 39*dde27992SDaniel P. Berrangé mod = import_module(module_path) 40*dde27992SDaniel P. Berrangé except Exception as ex: 41*dde27992SDaniel P. Berrangé print(f"unable to import '{module_path}': {ex}", file=sys.stderr) 42*dde27992SDaniel P. Berrangé sys.exit(1) 43a76ab215SJohn Snow 44*dde27992SDaniel P. Berrangé try: 45*dde27992SDaniel P. Berrangé klass = getattr(mod, class_name) 46*dde27992SDaniel P. Berrangé except AttributeError: 47*dde27992SDaniel P. Berrangé print(f"module '{module_path}' has no class '{class_name}'", 48*dde27992SDaniel P. Berrangé file=sys.stderr) 49*dde27992SDaniel P. Berrangé sys.exit(1) 50*dde27992SDaniel P. Berrangé 51*dde27992SDaniel P. Berrangé try: 52*dde27992SDaniel P. Berrangé backend = klass() 53*dde27992SDaniel P. Berrangé except Exception as ex: 54*dde27992SDaniel P. Berrangé print(f"backend '{path}' cannot be instantiated: {ex}", 55*dde27992SDaniel P. Berrangé file=sys.stderr) 56*dde27992SDaniel P. Berrangé sys.exit(1) 57*dde27992SDaniel P. Berrangé 58*dde27992SDaniel P. Berrangé if not isinstance(backend, QAPIBackend): 59*dde27992SDaniel P. Berrangé print(f"backend '{path}' must be an instance of QAPIBackend", 60*dde27992SDaniel P. Berrangé file=sys.stderr) 61*dde27992SDaniel P. Berrangé sys.exit(1) 62*dde27992SDaniel P. Berrangé 63*dde27992SDaniel P. Berrangé return backend 64a76ab215SJohn Snow 65a76ab215SJohn Snow 66a76ab215SJohn Snowdef main() -> int: 67a76ab215SJohn Snow """ 68a76ab215SJohn Snow gapi-gen executable entry point. 69a76ab215SJohn Snow Expects arguments via sys.argv, see --help for details. 70a76ab215SJohn Snow 71a76ab215SJohn Snow :return: int, 0 on success, 1 on failure. 72a76ab215SJohn Snow """ 73a76ab215SJohn Snow parser = argparse.ArgumentParser( 74a76ab215SJohn Snow description='Generate code from a QAPI schema') 75a76ab215SJohn Snow parser.add_argument('-b', '--builtins', action='store_true', 76a76ab215SJohn Snow help="generate code for built-in types") 77a76ab215SJohn Snow parser.add_argument('-o', '--output-dir', action='store', 78a76ab215SJohn Snow default='', 79a76ab215SJohn Snow help="write output to directory OUTPUT_DIR") 80a76ab215SJohn Snow parser.add_argument('-p', '--prefix', action='store', 81a76ab215SJohn Snow default='', 82a76ab215SJohn Snow help="prefix for symbols") 83a76ab215SJohn Snow parser.add_argument('-u', '--unmask-non-abi-names', action='store_true', 84a76ab215SJohn Snow dest='unmask', 85a76ab215SJohn Snow help="expose non-ABI names in introspection") 86*dde27992SDaniel P. Berrangé parser.add_argument('-B', '--backend', default=None, 87*dde27992SDaniel P. Berrangé help="Python module name for code generator") 88bd2017bcSVladimir Sementsov-Ogievskiy 89761a1a48SVladimir Sementsov-Ogievskiy # Option --suppress-tracing exists so we can avoid solving build system 90bd2017bcSVladimir Sementsov-Ogievskiy # problems. TODO Drop it when we no longer need it. 91761a1a48SVladimir Sementsov-Ogievskiy parser.add_argument('--suppress-tracing', action='store_true', 92761a1a48SVladimir Sementsov-Ogievskiy help="suppress adding trace events to qmp marshals") 93bd2017bcSVladimir Sementsov-Ogievskiy 94a76ab215SJohn Snow parser.add_argument('schema', action='store') 95a76ab215SJohn Snow args = parser.parse_args() 96a76ab215SJohn Snow 97a76ab215SJohn Snow funny_char = invalid_prefix_char(args.prefix) 98a76ab215SJohn Snow if funny_char: 99a76ab215SJohn Snow msg = f"funny character '{funny_char}' in argument of --prefix" 100a76ab215SJohn Snow print(f"{sys.argv[0]}: {msg}", file=sys.stderr) 101a76ab215SJohn Snow return 1 102a76ab215SJohn Snow 103a76ab215SJohn Snow try: 104*dde27992SDaniel P. Berrangé schema = QAPISchema(args.schema) 105*dde27992SDaniel P. Berrangé backend = create_backend(args.backend) 106*dde27992SDaniel P. Berrangé backend.generate(schema, 107a76ab215SJohn Snow output_dir=args.output_dir, 108a76ab215SJohn Snow prefix=args.prefix, 109a76ab215SJohn Snow unmask=args.unmask, 110bd2017bcSVladimir Sementsov-Ogievskiy builtins=args.builtins, 111761a1a48SVladimir Sementsov-Ogievskiy gen_tracing=not args.suppress_tracing) 112a76ab215SJohn Snow except QAPIError as err: 113bc5d3031SMarkus Armbruster print(err, file=sys.stderr) 114a76ab215SJohn Snow return 1 115a76ab215SJohn Snow return 0 116