aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-03-05 09:47:37 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-03-05 09:47:37 +0000
commit7fceeb190ac6fbbbec0bf904f743190708301e31 (patch)
tree65daba4301f340e0678bd86741e947039120f4cb /scripts
parent4f51e1d386e306a6a94ee997651f580e1c9f7b54 (diff)
parent418b1d0ae3a2cc992659f626a2a3f11944e0b259 (diff)
downloadqemu-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-xscripts/qapi-gen.py57
-rw-r--r--scripts/qapi/__init__.py0
-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)