diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-03-05 09:47:37 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-03-05 09:47:37 +0000 |
commit | 7fceeb190ac6fbbbec0bf904f743190708301e31 (patch) | |
tree | 65daba4301f340e0678bd86741e947039120f4cb /scripts | |
parent | 4f51e1d386e306a6a94ee997651f580e1c9f7b54 (diff) | |
parent | 418b1d0ae3a2cc992659f626a2a3f11944e0b259 (diff) | |
download | qemu-7fceeb190ac6fbbbec0bf904f743190708301e31.zip qemu-7fceeb190ac6fbbbec0bf904f743190708301e31.tar.gz qemu-7fceeb190ac6fbbbec0bf904f743190708301e31.tar.bz2 |
Merge remote-tracking branch 'remotes/ericb/tags/pull-qapi-2018-03-01-v4' into staging
qapi patches for 2018-03-01
- Markus Armbruster: Modularize generated QAPI code
# gpg: Signature made Fri 02 Mar 2018 19:50:16 GMT
# gpg: using RSA key A7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>"
# gpg: aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>"
# gpg: aka "[jpeg image of size 6874]"
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2 F3AA A7A1 6B4A 2527 436A
* remotes/ericb/tags/pull-qapi-2018-03-01-v4: (30 commits)
qapi: Don't create useless directory qapi-generated
Fix up dangling references to qmp-commands.* in comment and doc
qapi: Move qapi-schema.json to qapi/, rename generated files
docs: Correct outdated information on QAPI
docs/devel/writing-qmp-commands: Update for modular QAPI
qapi: Empty out qapi-schema.json
Include less of the generated modular QAPI headers
qapi: Generate separate .h, .c for each module
watchdog: Consolidate QAPI into single file
qapi/common: Fix guardname() for funny filenames
qapi/types qapi/visit: Generate built-in stuff into separate files
qapi: Make code-generating visitors use QAPIGen more
qapi: Rename generated qmp-marshal.c to qmp-commands.c
qapi: Record 'include' directives in intermediate representation
qapi: Generate in source order
qapi: Record 'include' directives in parse tree
qapi: Concentrate QAPISchemaParser.exprs updates in .__init__()
qapi: Lift error reporting from QAPISchema.__init__() to callers
qapi/common: Eliminate QAPISchema.exprs
qapi: Improve include file name reporting in error messages
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'scripts')
-rwxr-xr-x | scripts/qapi-gen.py | 57 | ||||
-rw-r--r-- | scripts/qapi/__init__.py | 0 | ||||
-rw-r--r-- | scripts/qapi/commands.py (renamed from scripts/qapi-commands.py) | 155 | ||||
-rw-r--r-- | scripts/qapi/common.py (renamed from scripts/qapi.py) | 358 | ||||
-rw-r--r--[-rwxr-xr-x] | scripts/qapi/doc.py (renamed from scripts/qapi2texi.py) | 92 | ||||
-rw-r--r-- | scripts/qapi/events.py (renamed from scripts/qapi-event.py) | 128 | ||||
-rw-r--r-- | scripts/qapi/introspect.py (renamed from scripts/qapi-introspect.py) | 121 | ||||
-rw-r--r-- | scripts/qapi/types.py (renamed from scripts/qapi-types.py) | 185 | ||||
-rw-r--r-- | scripts/qapi/visit.py (renamed from scripts/qapi-visit.py) | 189 |
9 files changed, 591 insertions, 694 deletions
diff --git a/scripts/qapi-gen.py b/scripts/qapi-gen.py new file mode 100755 index 0000000..3d98ca2 --- /dev/null +++ b/scripts/qapi-gen.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# QAPI generator +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +from __future__ import print_function +import argparse +import re +import sys +from qapi.common import QAPIError, QAPISchema +from qapi.types import gen_types +from qapi.visit import gen_visit +from qapi.commands import gen_commands +from qapi.events import gen_events +from qapi.introspect import gen_introspect +from qapi.doc import gen_doc + + +def main(argv): + 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('schema', action='store') + args = parser.parse_args() + + match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', args.prefix) + if match.end() != len(args.prefix): + print("%s: 'funny character '%s' in argument of --prefix" + % (sys.argv[0], args.prefix[match.end()]), + file=sys.stderr) + sys.exit(1) + + try: + schema = QAPISchema(args.schema) + except QAPIError as err: + print(err, file=sys.stderr) + exit(1) + + gen_types(schema, args.output_dir, args.prefix, args.builtins) + gen_visit(schema, args.output_dir, args.prefix, args.builtins) + gen_commands(schema, args.output_dir, args.prefix) + gen_events(schema, args.output_dir, args.prefix) + gen_introspect(schema, args.output_dir, args.prefix, args.unmask) + gen_doc(schema, args.output_dir, args.prefix) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/scripts/qapi/__init__.py b/scripts/qapi/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/scripts/qapi/__init__.py diff --git a/scripts/qapi-commands.py b/scripts/qapi/commands.py index f89d748..21a7e0d 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi/commands.py @@ -1,18 +1,19 @@ -# -# QAPI command marshaller generator -# -# Copyright IBM, Corp. 2011 -# Copyright (C) 2014-2016 Red Hat, Inc. -# -# Authors: -# Anthony Liguori <aliguori@us.ibm.com> -# Michael Roth <mdroth@linux.vnet.ibm.com> -# Markus Armbruster <armbru@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. -# See the COPYING file in the top-level directory. - -from qapi import * +""" +QAPI command marshaller generator + +Copyright IBM, Corp. 2011 +Copyright (C) 2014-2018 Red Hat, Inc. + +Authors: + Anthony Liguori <aliguori@us.ibm.com> + Michael Roth <mdroth@linux.vnet.ibm.com> + Markus Armbruster <armbru@redhat.com> + +This work is licensed under the terms of the GNU GPL, version 2. +See the COPYING file in the top-level directory. +""" + +from qapi.common import * def gen_command_decl(name, arg_type, boxed, ret_type): @@ -206,7 +207,7 @@ def gen_register_command(name, success_response): return ret -def gen_registry(registry): +def gen_registry(registry, prefix): ret = mcgen(''' void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) @@ -222,74 +223,21 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds) return ret -class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): - def __init__(self): - self.decl = None - self.defn = None - self._regy = None - self._visited_ret_types = None +class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): - def visit_begin(self, schema): - self.decl = '' - self.defn = '' + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-commands', + ' * Schema-defined QAPI/QMP commands', __doc__) self._regy = '' - self._visited_ret_types = set() - - def visit_end(self): - self.defn += gen_registry(self._regy) - self._regy = None - self._visited_ret_types = None - - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): - if not gen: - return - self.decl += gen_command_decl(name, arg_type, boxed, ret_type) - if ret_type and ret_type not in self._visited_ret_types: - self._visited_ret_types.add(ret_type) - self.defn += gen_marshal_output(ret_type) - self.decl += gen_marshal_decl(name) - self.defn += gen_marshal(name, arg_type, boxed, ret_type) - self._regy += gen_register_command(name, success_response) - - -(input_file, output_dir, do_c, do_h, prefix, opts) = parse_command_line() - -c_comment = ''' -/* - * schema-defined QMP->QAPI command dispatch - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' -h_comment = ''' -/* - * schema-defined QAPI function prototypes - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' - -(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, - 'qmp-marshal.c', 'qmp-commands.h', - c_comment, h_comment) - -fdef.write(mcgen(''' - + self._visited_ret_types = {} + + def _begin_module(self, name): + self._visited_ret_types[self._genc] = set() + commands = self._module_basename('qapi-commands', name) + types = self._module_basename('qapi-types', name) + visit = self._module_basename('qapi-visit', name) + self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/module.h" @@ -299,25 +247,40 @@ fdef.write(mcgen(''' #include "qapi/qobject-input-visitor.h" #include "qapi/dealloc-visitor.h" #include "qapi/error.h" -#include "%(prefix)sqapi-types.h" -#include "%(prefix)sqapi-visit.h" -#include "%(prefix)sqmp-commands.h" +#include "%(visit)s.h" +#include "%(commands)s.h" ''', - prefix=prefix)) - -fdecl.write(mcgen(''' -#include "%(prefix)sqapi-types.h" + commands=commands, visit=visit)) + self._genh.add(mcgen(''' +#include "%(types)s.h" #include "qapi/qmp/dispatch.h" +''', + types=types)) + + def visit_end(self): + (genc, genh) = self._module[self._main_module] + genh.add(mcgen(''' void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', - prefix=prefix, c_prefix=c_name(prefix, protect=False))) + c_prefix=c_name(self._prefix, protect=False))) + genc.add(gen_registry(self._regy, self._prefix)) + + def visit_command(self, name, info, arg_type, ret_type, + gen, success_response, boxed): + if not gen: + return + self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + if ret_type and ret_type not in self._visited_ret_types[self._genc]: + self._visited_ret_types[self._genc].add(ret_type) + self._genc.add(gen_marshal_output(ret_type)) + self._genh.add(gen_marshal_decl(name)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._regy += gen_register_command(name, success_response) -schema = QAPISchema(input_file) -gen = QAPISchemaGenCommandVisitor() -schema.visit(gen) -fdef.write(gen.defn) -fdecl.write(gen.decl) -close_output(fdef, fdecl) +def gen_commands(schema, output_dir, prefix): + vis = QAPISchemaGenCommandVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi.py b/scripts/qapi/common.py index 58f995b..97e9060 100644 --- a/scripts/qapi.py +++ b/scripts/qapi/common.py @@ -2,7 +2,7 @@ # QAPI helper library # # Copyright IBM, Corp. 2011 -# Copyright (c) 2013-2016 Red Hat Inc. +# Copyright (c) 2013-2018 Red Hat Inc. # # Authors: # Anthony Liguori <aliguori@us.ibm.com> @@ -13,19 +13,13 @@ from __future__ import print_function import errno -import getopt import os import re import string -import sys try: from collections import OrderedDict except: from ordereddict import OrderedDict -try: - from StringIO import StringIO -except ImportError: - from io import StringIO builtin_types = { 'null': 'QTYPE_QNULL', @@ -264,9 +258,8 @@ class QAPIDoc(object): class QAPISchemaParser(object): def __init__(self, fp, previously_included=[], incl_info=None): - abs_fname = os.path.abspath(fp.name) self.fname = fp.name - previously_included.append(abs_fname) + previously_included.append(os.path.abspath(fp.name)) self.incl_info = incl_info self.src = fp.read() if self.src == '' or self.src[-1] != '\n': @@ -297,8 +290,15 @@ class QAPISchemaParser(object): if not isinstance(include, str): raise QAPISemError(info, "Value of 'include' must be a string") - self._include(include, info, os.path.dirname(abs_fname), - previously_included) + incl_fname = os.path.join(os.path.dirname(self.fname), + include) + self.exprs.append({'expr': {'include': incl_fname}, + 'info': info}) + exprs_include = self._include(include, info, incl_fname, + previously_included) + if exprs_include: + self.exprs.extend(exprs_include.exprs) + self.docs.extend(exprs_include.docs) elif "pragma" in expr: self.reject_expr_doc(cur_doc) if len(expr) != 1: @@ -329,8 +329,8 @@ class QAPISchemaParser(object): "Documentation for '%s' is not followed by the definition" % doc.symbol) - def _include(self, include, info, base_dir, previously_included): - incl_abs_fname = os.path.join(base_dir, include) + def _include(self, include, info, incl_fname, previously_included): + incl_abs_fname = os.path.abspath(incl_fname) # catch inclusion cycle inf = info while inf: @@ -340,14 +340,13 @@ class QAPISchemaParser(object): # skip multiple include of the same file if incl_abs_fname in previously_included: - return + return None + try: - fobj = open(incl_abs_fname, 'r') + fobj = open(incl_fname, 'r') except IOError as e: - raise QAPISemError(info, '%s: %s' % (e.strerror, include)) - exprs_include = QAPISchemaParser(fobj, previously_included, info) - self.exprs.extend(exprs_include.exprs) - self.docs.extend(exprs_include.docs) + raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname)) + return QAPISchemaParser(fobj, previously_included, info) def _pragma(self, name, value, info): global doc_required, returns_whitelist, name_case_whitelist @@ -896,6 +895,9 @@ def check_exprs(exprs): info = expr_elem['info'] doc = expr_elem.get('doc') + if 'include' in expr: + continue + if not doc and doc_required: raise QAPISemError(info, "Expression missing documentation comment") @@ -935,6 +937,9 @@ def check_exprs(exprs): # Try again for hidden UnionKind enum for expr_elem in exprs: expr = expr_elem['expr'] + + if 'include' in expr: + continue if 'union' in expr and not discriminator_find_enum_define(expr): name = '%sKind' % expr['union'] elif 'alternate' in expr: @@ -950,6 +955,8 @@ def check_exprs(exprs): info = expr_elem['info'] doc = expr_elem.get('doc') + if 'include' in expr: + continue if 'enum' in expr: check_enum(expr, info) elif 'union' in expr: @@ -977,8 +984,9 @@ def check_exprs(exprs): class QAPISchemaEntity(object): def __init__(self, name, info, doc): - assert isinstance(name, str) + assert name is None or isinstance(name, str) self.name = name + self.module = None # For explicitly defined entities, info points to the (explicit) # definition. For builtins (and their arrays), info is None. # For implicitly defined entities, info points to a place that @@ -1007,10 +1015,16 @@ class QAPISchemaVisitor(object): def visit_end(self): pass + def visit_module(self, fname): + pass + def visit_needed(self, entity): # Default to visiting everything return True + def visit_include(self, fname, info): + pass + def visit_builtin_type(self, name, info, json_type): pass @@ -1037,6 +1051,16 @@ class QAPISchemaVisitor(object): pass +class QAPISchemaInclude(QAPISchemaEntity): + + def __init__(self, fname, info): + QAPISchemaEntity.__init__(self, None, info, None) + self.fname = fname + + def visit(self, visitor): + visitor.visit_include(self.fname, self.info) + + class QAPISchemaType(QAPISchemaEntity): # Return the C type for common use. # For the types we commonly box, this is a pointer type. @@ -1464,25 +1488,28 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): - try: - parser = QAPISchemaParser(open(fname, 'r')) - self.exprs = check_exprs(parser.exprs) - self.docs = parser.docs - self._entity_dict = {} - self._predefining = True - self._def_predefineds() - self._predefining = False - self._def_exprs() - self.check() - except QAPIError as err: - print(err, file=sys.stderr) - exit(1) + self._fname = fname + parser = QAPISchemaParser(open(fname, 'r')) + exprs = check_exprs(parser.exprs) + self.docs = parser.docs + self._entity_list = [] + self._entity_dict = {} + self._predefining = True + self._def_predefineds() + self._predefining = False + self._def_exprs(exprs) + self.check() def _def_entity(self, ent): # Only the predefined types are allowed to not have info assert ent.info or self._predefining - assert ent.name not in self._entity_dict - self._entity_dict[ent.name] = ent + assert ent.name is None or ent.name not in self._entity_dict + self._entity_list.append(ent) + if ent.name is not None: + self._entity_dict[ent.name] = ent + if ent.info: + ent.module = os.path.relpath(ent.info['file'], + os.path.dirname(self._fname)) def lookup_entity(self, name, typ=None): ent = self._entity_dict.get(name) @@ -1493,13 +1520,21 @@ class QAPISchema(object): def lookup_type(self, name): return self.lookup_entity(name, QAPISchemaType) + def _def_include(self, expr, info, doc): + include = expr['include'] + assert doc is None + main_info = info + while main_info['parent']: + main_info = main_info['parent'] + fname = os.path.relpath(include, os.path.dirname(main_info['file'])) + self._def_entity(QAPISchemaInclude(fname, info)) + def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) - # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple - # qapi-types.h from a single .c, all arrays of builtins must be - # declared in the first file whether or not they are used. Nicer - # would be to use lazy instantiation, while figuring out how to - # avoid compilation issues with multiple qapi-types.h. + # Instantiating only the arrays that are actually used would + # be nice, but we can't as long as their generated code + # (qapi-builtin-types.[ch]) may be shared by some other + # schema. self._make_array_type(name, None) def _def_predefineds(self): @@ -1657,8 +1692,8 @@ class QAPISchema(object): name, info, doc, 'arg', self._make_members(data, info)) self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) - def _def_exprs(self): - for expr_elem in self.exprs: + def _def_exprs(self, exprs): + for expr_elem in exprs: expr = expr_elem['expr'] info = expr_elem['info'] doc = expr_elem.get('doc') @@ -1674,17 +1709,23 @@ class QAPISchema(object): self._def_command(expr, info, doc) elif 'event' in expr: self._def_event(expr, info, doc) + elif 'include' in expr: + self._def_include(expr, info, doc) else: assert False def check(self): - for (name, ent) in sorted(self._entity_dict.items()): + for ent in self._entity_list: ent.check(self) def visit(self, visitor): visitor.visit_begin(self) - for (name, entity) in sorted(self._entity_dict.items()): + module = None + for entity in self._entity_list: if visitor.visit_needed(entity): + if entity.module != module: + module = entity.module + visitor.visit_module(module) entity.visit(visitor) visitor.visit_end() @@ -1826,12 +1867,11 @@ def mcgen(code, **kwds): def guardname(filename): - return c_name(filename, protect=False).upper() + return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() def guardstart(name): return mcgen(''' - #ifndef %(name)s #define %(name)s @@ -1843,7 +1883,6 @@ def guardend(name): return mcgen(''' #endif /* %(name)s */ - ''', name=guardname(name)) @@ -1930,104 +1969,161 @@ def build_params(arg_type, boxed, extra): # -# Common command line parsing +# Accumulate and write output # +class QAPIGen(object): + + def __init__(self): + self._preamble = '' + self._body = '' + + def preamble_add(self, text): + self._preamble += text + + def add(self, text): + self._body += text + + def _top(self, fname): + return '' + + def _bottom(self, fname): + return '' + + def write(self, output_dir, fname): + pathname = os.path.join(output_dir, fname) + dir = os.path.dirname(pathname) + if dir: + try: + os.makedirs(dir) + except os.error as e: + if e.errno != errno.EEXIST: + raise + fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666) + f = os.fdopen(fd, 'r+') + text = (self._top(fname) + self._preamble + self._body + + self._bottom(fname)) + oldtext = f.read(len(text) + 1) + if text != oldtext: + f.seek(0) + f.truncate(0) + f.write(text) + f.close() + + +class QAPIGenC(QAPIGen): + + def __init__(self, blurb, pydoc): + QAPIGen.__init__(self) + self._blurb = blurb + self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, + re.MULTILINE)) + + def _top(self, fname): + return mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ -def parse_command_line(extra_options='', extra_long_options=[]): - - try: - opts, args = getopt.gnu_getopt(sys.argv[1:], - 'chp:o:' + extra_options, - ['source', 'header', 'prefix=', - 'output-dir='] + extra_long_options) - except getopt.GetoptError as err: - print("%s: %s" % (sys.argv[0], str(err)), file=sys.stderr) - sys.exit(1) - - output_dir = '' - prefix = '' - do_c = False - do_h = False - extra_opts = [] - - for oa in opts: - o, a = oa - if o in ('-p', '--prefix'): - match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', a) - if match.end() != len(a): - print("%s: 'funny character '%s' in argument of --prefix" \ - % (sys.argv[0], a[match.end()]), file=sys.stderr) - sys.exit(1) - prefix = a - elif o in ('-o', '--output-dir'): - output_dir = a + '/' - elif o in ('-c', '--source'): - do_c = True - elif o in ('-h', '--header'): - do_h = True - else: - extra_opts.append(oa) +/* +%(blurb)s + * + * %(copyright)s + * + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. + * See the COPYING.LIB file in the top-level directory. + */ - if not do_c and not do_h: - do_c = True - do_h = True +''', + blurb=self._blurb, copyright=self._copyright) - if len(args) != 1: - print("%s: need exactly one argument" % sys.argv[0], file=sys.stderr) - sys.exit(1) - fname = args[0] + def _bottom(self, fname): + return mcgen(''' +/* Dummy declaration to prevent empty .o file */ +char dummy_%(name)s; +''', + name=c_name(fname)) - return (fname, output_dir, do_c, do_h, prefix, extra_opts) -# -# Generate output files with boilerplate -# +class QAPIGenH(QAPIGenC): + def _top(self, fname): + return QAPIGenC._top(self, fname) + guardstart(fname) -def open_output(output_dir, do_c, do_h, prefix, c_file, h_file, - c_comment, h_comment): - guard = guardname(prefix + h_file) - c_file = output_dir + prefix + c_file - h_file = output_dir + prefix + h_file + def _bottom(self, fname): + return guardend(fname) - if output_dir: - try: - os.makedirs(output_dir) - except os.error as e: - if e.errno != errno.EEXIST: - raise - - def maybe_open(really, name, opt): - if really: - return open(name, opt) - else: - return StringIO() - fdef = maybe_open(do_c, c_file, 'w') - fdecl = maybe_open(do_h, h_file, 'w') +class QAPIGenDoc(QAPIGen): - fdef.write(mcgen(''' -/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ -%(comment)s -''', - comment=c_comment)) + def _top(self, fname): + return (QAPIGen._top(self, fname) + + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') - fdecl.write(mcgen(''' -/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ -%(comment)s -#ifndef %(guard)s -#define %(guard)s -''', - comment=h_comment, guard=guard)) +class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): + + def __init__(self, prefix, what, blurb, pydoc): + self._prefix = prefix + self._what = what + self._genc = QAPIGenC(blurb, pydoc) + self._genh = QAPIGenH(blurb, pydoc) - return (fdef, fdecl) + def write(self, output_dir): + self._genc.write(output_dir, self._prefix + self._what + '.c') + self._genh.write(output_dir, self._prefix + self._what + '.h') -def close_output(fdef, fdecl): - fdecl.write(''' -#endif -''') - fdecl.close() - fdef.close() +class QAPISchemaModularCVisitor(QAPISchemaVisitor): + + def __init__(self, prefix, what, blurb, pydoc): + self._prefix = prefix + self._what = what + self._blurb = blurb + self._pydoc = pydoc + self._module = {} + self._main_module = None + + def _module_basename(self, what, name): + if name is None: + return re.sub(r'-', '-builtin-', what) + basename = os.path.join(os.path.dirname(name), + self._prefix + what) + if name == self._main_module: + return basename + return basename + '-' + os.path.splitext(os.path.basename(name))[0] + + def _add_module(self, name, blurb): + if self._main_module is None and name is not None: + self._main_module = name + genc = QAPIGenC(blurb, self._pydoc) + genh = QAPIGenH(blurb, self._pydoc) + self._module[name] = (genc, genh) + self._set_module(name) + + def _set_module(self, name): + self._genc, self._genh = self._module[name] + + def write(self, output_dir, opt_builtins=False): + for name in self._module: + if name is None and not opt_builtins: + continue + basename = self._module_basename(self._what, name) + (genc, genh) = self._module[name] + genc.write(output_dir, basename + '.c') + genh.write(output_dir, basename + '.h') + + def _begin_module(self, name): + pass + + def visit_module(self, name): + if name in self._module: + self._set_module(name) + return + self._add_module(name, self._blurb) + self._begin_module(name) + + def visit_include(self, name, info): + basename = self._module_basename(self._what, name) + self._genh.preamble_add(mcgen(''' +#include "%(basename)s.h" +''', + basename=basename)) diff --git a/scripts/qapi2texi.py b/scripts/qapi/doc.py index bf1c57b..0ea68bf 100755..100644 --- a/scripts/qapi2texi.py +++ b/scripts/qapi/doc.py @@ -4,11 +4,10 @@ # This work is licensed under the terms of the GNU LGPL, version 2+. # See the COPYING file in the top-level directory. """This script produces the documentation of a qapi schema in texinfo format""" + from __future__ import print_function import re -import sys - -import qapi +import qapi.common MSG_FMT = """ @deftypefn {type} {{}} {name} @@ -197,34 +196,36 @@ def texi_entity(doc, what, base=None, variants=None, + texi_sections(doc)) -class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): - def __init__(self): - self.out = None +class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): + def __init__(self, prefix): + self._prefix = prefix + self._gen = qapi.common.QAPIGenDoc() self.cur_doc = None - def visit_begin(self, schema): - self.out = '' + def write(self, output_dir): + self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') def visit_enum_type(self, name, info, values, prefix): doc = self.cur_doc - self.out += TYPE_FMT(type='Enum', - name=doc.symbol, - body=texi_entity(doc, 'Values', - member_func=texi_enum_value)) + self._gen.add(TYPE_FMT(type='Enum', + name=doc.symbol, + body=texi_entity(doc, 'Values', + member_func=texi_enum_value))) def visit_object_type(self, name, info, base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None - self.out += TYPE_FMT(type='Object', - name=doc.symbol, - body=texi_entity(doc, 'Members', base, variants)) + self._gen.add(TYPE_FMT(type='Object', + name=doc.symbol, + body=texi_entity(doc, 'Members', + base, variants))) def visit_alternate_type(self, name, info, variants): doc = self.cur_doc - self.out += TYPE_FMT(type='Alternate', - name=doc.symbol, - body=texi_entity(doc, 'Members')) + self._gen.add(TYPE_FMT(type='Alternate', + name=doc.symbol, + body=texi_entity(doc, 'Members'))) def visit_command(self, name, info, arg_type, ret_type, gen, success_response, boxed): @@ -236,55 +237,38 @@ class QAPISchemaGenDocVisitor(qapi.QAPISchemaVisitor): body += texi_sections(doc) else: body = texi_entity(doc, 'Arguments') - self.out += MSG_FMT(type='Command', - name=doc.symbol, - body=body) + self._gen.add(MSG_FMT(type='Command', + name=doc.symbol, + body=body)) def visit_event(self, name, info, arg_type, boxed): doc = self.cur_doc - self.out += MSG_FMT(type='Event', - name=doc.symbol, - body=texi_entity(doc, 'Arguments')) + self._gen.add(MSG_FMT(type='Event', + name=doc.symbol, + body=texi_entity(doc, 'Arguments'))) def symbol(self, doc, entity): - if self.out: - self.out += '\n' + if self._gen._body: + self._gen.add('\n') self.cur_doc = doc entity.visit(self) self.cur_doc = None def freeform(self, doc): assert not doc.args - if self.out: - self.out += '\n' - self.out += texi_body(doc) + texi_sections(doc) + if self._gen._body: + self._gen.add('\n') + self._gen.add(texi_body(doc) + texi_sections(doc)) -def texi_schema(schema): - """Convert QAPI schema documentation to Texinfo""" - gen = QAPISchemaGenDocVisitor() - gen.visit_begin(schema) +def gen_doc(schema, output_dir, prefix): + if not qapi.common.doc_required: + return + vis = QAPISchemaGenDocVisitor(prefix) + vis.visit_begin(schema) for doc in schema.docs: if doc.symbol: - gen.symbol(doc, schema.lookup_entity(doc.symbol)) + vis.symbol(doc, schema.lookup_entity(doc.symbol)) else: - gen.freeform(doc) - return gen.out - - -def main(argv): - """Takes schema argument, prints result to stdout""" - if len(argv) != 2: - print("%s: need exactly 1 argument: SCHEMA" % argv[0], file=sys.stderr) - sys.exit(1) - - schema = qapi.QAPISchema(argv[1]) - if not qapi.doc_required: - print("%s: need pragma 'doc-required' " - "to generate documentation" % argv[0], file=sys.stderr) - sys.exit(1) - print(texi_schema(schema)) - - -if __name__ == '__main__': - main(sys.argv) + vis.freeform(doc) + vis.write(output_dir) diff --git a/scripts/qapi-event.py b/scripts/qapi/events.py index c710968..3dc523c 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi/events.py @@ -1,17 +1,18 @@ -# -# QAPI event generator -# -# Copyright (c) 2014 Wenchao Xia -# Copyright (c) 2015-2016 Red Hat Inc. -# -# Authors: -# Wenchao Xia <wenchaoqemu@gmail.com> -# Markus Armbruster <armbru@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. -# See the COPYING file in the top-level directory. - -from qapi import * +""" +QAPI event generator + +Copyright (c) 2014 Wenchao Xia +Copyright (c) 2015-2018 Red Hat Inc. + +Authors: + Wenchao Xia <wenchaoqemu@gmail.com> + Markus Armbruster <armbru@redhat.com> + +This work is licensed under the terms of the GNU GPL, version 2. +See the COPYING file in the top-level directory. +""" + +from qapi.common import * def build_event_send_proto(name, arg_type, boxed): @@ -57,7 +58,7 @@ def gen_param_var(typ): return ret -def gen_event_send(name, arg_type, boxed): +def gen_event_send(name, arg_type, boxed, event_enum_name): # FIXME: Our declaration of local variables (and of 'errp' in the # parameter list) can collide with exploded members of the event's # data type passed in as parameters. If this collision ever hits in @@ -147,89 +148,48 @@ out: return ret -class QAPISchemaGenEventVisitor(QAPISchemaVisitor): - def __init__(self): - self.decl = None - self.defn = None - self._event_names = None +class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): - def visit_begin(self, schema): - self.decl = '' - self.defn = '' + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-events', + ' * Schema-defined QAPI/QMP events', __doc__) + self._enum_name = c_name(prefix + 'QAPIEvent', protect=False) self._event_names = [] - def visit_end(self): - self.decl += gen_enum(event_enum_name, self._event_names) - self.defn += gen_enum_lookup(event_enum_name, self._event_names) - self._event_names = None - - def visit_event(self, name, info, arg_type, boxed): - self.decl += gen_event_send_decl(name, arg_type, boxed) - self.defn += gen_event_send(name, arg_type, boxed) - self._event_names.append(name) - - -(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() - -c_comment = ''' -/* - * schema-defined QAPI event functions - * - * Copyright (c) 2014 Wenchao Xia - * - * Authors: - * Wenchao Xia <wenchaoqemu@gmail.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' -h_comment = ''' -/* - * schema-defined QAPI event functions - * - * Copyright (c) 2014 Wenchao Xia - * - * Authors: - * Wenchao Xia <wenchaoqemu@gmail.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' - -(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, - 'qapi-event.c', 'qapi-event.h', - c_comment, h_comment) - -fdef.write(mcgen(''' + def _begin_module(self, name): + types = self._module_basename('qapi-types', name) + visit = self._module_basename('qapi-visit', name) + self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" -#include "%(prefix)sqapi-event.h" -#include "%(prefix)sqapi-visit.h" +#include "%(prefix)sqapi-events.h" +#include "%(visit)s.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" #include "qapi/qobject-output-visitor.h" #include "qapi/qmp-event.h" ''', - prefix=prefix)) - -fdecl.write(mcgen(''' + visit=visit, prefix=self._prefix)) + self._genh.add(mcgen(''' #include "qapi/util.h" -#include "%(prefix)sqapi-types.h" +#include "%(types)s.h" ''', - prefix=prefix)) + types=types)) -event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) + def visit_end(self): + self._genh.add(gen_enum(self._enum_name, self._event_names)) + self._genc.add(gen_enum_lookup(self._enum_name, self._event_names)) + + def visit_event(self, name, info, arg_type, boxed): + self._genh.add(gen_event_send_decl(name, arg_type, boxed)) + self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) + self._event_names.append(name) -schema = QAPISchema(input_file) -gen = QAPISchemaGenEventVisitor() -schema.visit(gen) -fdef.write(gen.defn) -fdecl.write(gen.decl) -close_output(fdef, fdecl) +def gen_events(schema, output_dir, prefix): + vis = QAPISchemaGenEventVisitor(prefix) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi-introspect.py b/scripts/qapi/introspect.py index 032bcea..f66c397 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi/introspect.py @@ -1,15 +1,16 @@ -# -# QAPI introspection generator -# -# Copyright (C) 2015-2016 Red Hat, Inc. -# -# Authors: -# Markus Armbruster <armbru@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. -# See the COPYING file in the top-level directory. +""" +QAPI introspection generator -from qapi import * +Copyright (C) 2015-2018 Red Hat, Inc. + +Authors: + Markus Armbruster <armbru@redhat.com> + +This work is licensed under the terms of the GNU GPL, version 2. +See the COPYING file in the top-level directory. +""" + +from qapi.common import * # Caveman's json.dumps() replacement (we're stuck at Python 2.4) @@ -39,21 +40,26 @@ def to_c_string(string): return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' -class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): - def __init__(self, unmask): +class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): + + def __init__(self, prefix, unmask): + QAPISchemaMonolithicCVisitor.__init__( + self, prefix, 'qapi-introspect', + ' * QAPI/QMP schema introspection', __doc__) self._unmask = unmask - self.defn = None - self.decl = None self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None - - def visit_begin(self, schema): - self._schema = schema self._jsons = [] self._used_types = [] self._name_map = {} + self._genc.add(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqapi-introspect.h" + +''', + prefix=prefix)) + + def visit_begin(self, schema): + self._schema = schema def visit_end(self): # visit the types that are actually used @@ -64,22 +70,22 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): # generate C # TODO can generate awfully long lines jsons.extend(self._jsons) - name = c_name(prefix, protect=False) + 'qmp_schema_json' - self.decl = mcgen(''' + name = c_name(self._prefix, protect=False) + 'qmp_schema_json' + self._genh.add(mcgen(''' extern const char %(c_name)s[]; ''', - c_name=c_name(name)) + c_name=c_name(name))) lines = to_json(jsons).split('\n') c_string = '\n '.join([to_c_string(line) for line in lines]) - self.defn = mcgen(''' + self._genc.add(mcgen(''' const char %(c_name)s[] = %(c_string)s; ''', - c_name=c_name(name), - c_string=c_string) + c_name=c_name(name), + c_string=c_string)) self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None + self._jsons = [] + self._used_types = [] + self._name_map = {} def visit_needed(self, entity): # Ignore types on first pass; visit_end() will pick up used types @@ -165,55 +171,8 @@ const char %(c_name)s[] = %(c_string)s; arg_type = arg_type or self._schema.the_empty_object_type self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) -# Debugging aid: unmask QAPI schema's type names -# We normally mask them, because they're not QMP wire ABI -opt_unmask = False - -(input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line('u', ['unmask-non-abi-names']) - -for o, a in opts: - if o in ('-u', '--unmask-non-abi-names'): - opt_unmask = True - -c_comment = ''' -/* - * QAPI/QMP schema introspection - * - * Copyright (C) 2015 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' -h_comment = ''' -/* - * QAPI/QMP schema introspection - * - * Copyright (C) 2015 Red Hat, Inc. - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' - -(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, - 'qmp-introspect.c', 'qmp-introspect.h', - c_comment, h_comment) - -fdef.write(mcgen(''' -#include "qemu/osdep.h" -#include "%(prefix)sqmp-introspect.h" - -''', - prefix=prefix)) - -schema = QAPISchema(input_file) -gen = QAPISchemaGenIntrospectVisitor(opt_unmask) -schema.visit(gen) -fdef.write(gen.defn) -fdecl.write(gen.decl) -close_output(fdef, fdecl) +def gen_introspect(schema, output_dir, prefix, opt_unmask): + vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask) + schema.visit(vis) + vis.write(output_dir) diff --git a/scripts/qapi-types.py b/scripts/qapi/types.py index 7e3051d..64d9c0f 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi/types.py @@ -1,17 +1,19 @@ -# -# QAPI types generator -# -# Copyright IBM, Corp. 2011 -# Copyright (c) 2013-2016 Red Hat Inc. -# -# Authors: -# Anthony Liguori <aliguori@us.ibm.com> -# Markus Armbruster <armbru@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. +""" +QAPI types generator + +Copyright IBM, Corp. 2011 +Copyright (c) 2013-2018 Red Hat Inc. + +Authors: + Anthony Liguori <aliguori@us.ibm.com> + Michael Roth <mdroth@linux.vnet.ibm.com> + Markus Armbruster <armbru@redhat.com> + +This work is licensed under the terms of the GNU GPL, version 2. # See the COPYING file in the top-level directory. +""" -from qapi import * +from qapi.common import * # variants must be emitted before their container; track what has already @@ -165,67 +167,62 @@ void qapi_free_%(c_name)s(%(c_name)s *obj) return ret -class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): - def __init__(self): - self.decl = None - self.defn = None - self._fwdecl = None - self._btin = None +class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): + + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-types', ' * Schema-defined QAPI types', + __doc__) + self._add_module(None, ' * Built-in QAPI types') + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qapi/dealloc-visitor.h" +#include "qapi/qapi-builtin-types.h" +#include "qapi/qapi-builtin-visit.h" +''')) + self._genh.preamble_add(mcgen(''' +#include "qapi/util.h" +''')) + + def _begin_module(self, name): + types = self._module_basename('qapi-types', name) + visit = self._module_basename('qapi-visit', name) + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qapi/dealloc-visitor.h" +#include "%(types)s.h" +#include "%(visit)s.h" +''', + types=types, visit=visit)) + self._genh.preamble_add(mcgen(''' +#include "qapi/qapi-builtin-types.h" +''')) def visit_begin(self, schema): # gen_object() is recursive, ensure it doesn't visit the empty type objects_seen.add(schema.the_empty_object_type.name) - self.decl = '' - self.defn = '' - self._fwdecl = '' - self._btin = guardstart('QAPI_TYPES_BUILTIN') - - def visit_end(self): - self.decl = self._fwdecl + self.decl - self._fwdecl = None - # To avoid header dependency hell, we always generate - # declarations for built-in types in our header files and - # simply guard them. See also do_builtins (command line - # option -b). - self._btin += guardend('QAPI_TYPES_BUILTIN') - self.decl = self._btin + self.decl - self._btin = None def _gen_type_cleanup(self, name): - self.decl += gen_type_cleanup_decl(name) - self.defn += gen_type_cleanup(name) + self._genh.add(gen_type_cleanup_decl(name)) + self._genc.add(gen_type_cleanup(name)) def visit_enum_type(self, name, info, values, prefix): - # Special case for our lone builtin enum type - # TODO use something cleaner than existence of info - if not info: - self._btin += gen_enum(name, values, prefix) - if do_builtins: - self.defn += gen_enum_lookup(name, values, prefix) - else: - self._fwdecl += gen_enum(name, values, prefix) - self.defn += gen_enum_lookup(name, values, prefix) + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) def visit_array_type(self, name, info, element_type): - if isinstance(element_type, QAPISchemaBuiltinType): - self._btin += gen_fwd_object_or_array(name) - self._btin += gen_array(name, element_type) - self._btin += gen_type_cleanup_decl(name) - if do_builtins: - self.defn += gen_type_cleanup(name) - else: - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_array(name, element_type) - self._gen_type_cleanup(name) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) + self._gen_type_cleanup(name) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_object(name, base, members, variants) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, base, members, variants)) if base and not base.is_implicit(): - self.decl += gen_upcast(name, base) + self._genh.add(gen_upcast(name, base)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): @@ -233,73 +230,13 @@ class QAPISchemaGenTypeVisitor(QAPISchemaVisitor): self._gen_type_cleanup(name) def visit_alternate_type(self, name, info, variants): - self._fwdecl += gen_fwd_object_or_array(name) - self.decl += gen_object(name, None, [variants.tag_member], variants) + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, None, + [variants.tag_member], variants)) self._gen_type_cleanup(name) -# If you link code generated from multiple schemata, you want only one -# instance of the code for built-in types. Generate it only when -# do_builtins, enabled by command line option -b. See also -# QAPISchemaGenTypeVisitor.visit_end(). -do_builtins = False - -(input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line('b', ['builtins']) - -for o, a in opts: - if o in ('-b', '--builtins'): - do_builtins = True - -c_comment = ''' -/* - * deallocation functions for schema-defined QAPI types - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * Michael Roth <mdroth@linux.vnet.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' -h_comment = ''' -/* - * schema-defined QAPI types - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' - -(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, - 'qapi-types.c', 'qapi-types.h', - c_comment, h_comment) - -fdef.write(mcgen(''' -#include "qemu/osdep.h" -#include "qapi/dealloc-visitor.h" -#include "%(prefix)sqapi-types.h" -#include "%(prefix)sqapi-visit.h" -''', - prefix=prefix)) - -fdecl.write(mcgen(''' -#include "qapi/util.h" -''')) - -schema = QAPISchema(input_file) -gen = QAPISchemaGenTypeVisitor() -schema.visit(gen) -fdef.write(gen.defn) -fdecl.write(gen.decl) -close_output(fdef, fdecl) +def gen_types(schema, output_dir, prefix, opt_builtins): + vis = QAPISchemaGenTypeVisitor(prefix) + schema.visit(vis) + vis.write(output_dir, opt_builtins) diff --git a/scripts/qapi-visit.py b/scripts/qapi/visit.py index 7e1cfc1..5d72d89 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi/visit.py @@ -1,18 +1,19 @@ -# -# QAPI visitor generator -# -# Copyright IBM, Corp. 2011 -# Copyright (C) 2014-2016 Red Hat, Inc. -# -# Authors: -# Anthony Liguori <aliguori@us.ibm.com> -# Michael Roth <mdroth@linux.vnet.ibm.com> -# Markus Armbruster <armbru@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2. -# See the COPYING file in the top-level directory. - -from qapi import * +""" +QAPI visitor generator + +Copyright IBM, Corp. 2011 +Copyright (C) 2014-2018 Red Hat, Inc. + +Authors: + Anthony Liguori <aliguori@us.ibm.com> + Michael Roth <mdroth@linux.vnet.ibm.com> + Markus Armbruster <armbru@redhat.com> + +This work is licensed under the terms of the GNU GPL, version 2. +See the COPYING file in the top-level directory. +""" + +from qapi.common import * def gen_visit_decl(name, scalar=False): @@ -262,131 +263,71 @@ out: c_name=c_name(name)) -class QAPISchemaGenVisitVisitor(QAPISchemaVisitor): - def __init__(self): - self.decl = None - self.defn = None - self._btin = None +class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): + + def __init__(self, prefix): + QAPISchemaModularCVisitor.__init__( + self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', + __doc__) + self._add_module(None, ' * Built-in QAPI visitors') + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qapi-builtin-visit.h" +''')) + self._genh.preamble_add(mcgen(''' +#include "qapi/visitor.h" +#include "qapi/qapi-builtin-types.h" + +''', + prefix=prefix)) - def visit_begin(self, schema): - self.decl = '' - self.defn = '' - self._btin = guardstart('QAPI_VISIT_BUILTIN') + def _begin_module(self, name): + types = self._module_basename('qapi-types', name) + visit = self._module_basename('qapi-visit', name) + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "%(visit)s.h" +''', + visit=visit, prefix=self._prefix)) + self._genh.preamble_add(mcgen(''' +#include "qapi/qapi-builtin-visit.h" +#include "%(types)s.h" - def visit_end(self): - # To avoid header dependency hell, we always generate - # declarations for built-in types in our header files and - # simply guard them. See also do_builtins (command line - # option -b). - self._btin += guardend('QAPI_VISIT_BUILTIN') - self.decl = self._btin + self.decl - self._btin = None +''', + types=types)) def visit_enum_type(self, name, info, values, prefix): - # Special case for our lone builtin enum type - # TODO use something cleaner than existence of info - if not info: - self._btin += gen_visit_decl(name, scalar=True) - if do_builtins: - self.defn += gen_visit_enum(name) - else: - self.decl += gen_visit_decl(name, scalar=True) - self.defn += gen_visit_enum(name) + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) def visit_array_type(self, name, info, element_type): - decl = gen_visit_decl(name) - defn = gen_visit_list(name, element_type) - if isinstance(element_type, QAPISchemaBuiltinType): - self._btin += decl - if do_builtins: - self.defn += defn - else: - self.decl += decl - self.defn += defn + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) def visit_object_type(self, name, info, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self.decl += gen_visit_members_decl(name) - self.defn += gen_visit_object_members(name, base, members, variants) + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, members, variants)) # TODO Worth changing the visitor signature, so we could # directly use rather than repeat type.is_implicit()? if not name.startswith('q_'): # only explicit types need an allocating visit - self.decl += gen_visit_decl(name) - self.defn += gen_visit_object(name, base, members, variants) + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(name, base, members, variants)) def visit_alternate_type(self, name, info, variants): - self.decl += gen_visit_decl(name) - self.defn += gen_visit_alternate(name, variants) - -# If you link code generated from multiple schemata, you want only one -# instance of the code for built-in types. Generate it only when -# do_builtins, enabled by command line option -b. See also -# QAPISchemaGenVisitVisitor.visit_end(). -do_builtins = False - -(input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line('b', ['builtins']) - -for o, a in opts: - if o in ('-b', '--builtins'): - do_builtins = True - -c_comment = ''' -/* - * schema-defined QAPI visitor functions - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' -h_comment = ''' -/* - * schema-defined QAPI visitor functions - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori <aliguori@us.ibm.com> - * - * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. - * See the COPYING.LIB file in the top-level directory. - * - */ -''' - -(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, - 'qapi-visit.c', 'qapi-visit.h', - c_comment, h_comment) - -fdef.write(mcgen(''' -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/error.h" -#include "%(prefix)sqapi-visit.h" -''', - prefix=prefix)) - -fdecl.write(mcgen(''' -#include "qapi/visitor.h" -#include "qapi/qmp/qerror.h" -#include "%(prefix)sqapi-types.h" - -''', - prefix=prefix)) + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_alternate(name, variants)) -schema = QAPISchema(input_file) -gen = QAPISchemaGenVisitVisitor() -schema.visit(gen) -fdef.write(gen.defn) -fdecl.write(gen.decl) -close_output(fdef, fdecl) +def gen_visit(schema, output_dir, prefix, opt_builtins): + vis = QAPISchemaGenVisitVisitor(prefix) + schema.visit(vis) + vis.write(output_dir, opt_builtins) |