Skip to content

Commit

Permalink
qapi: Make code-generating visitors use QAPIGen more
Browse files Browse the repository at this point in the history
The use of QAPIGen is rather shallow so far: most of the output
accumulation is not converted.  Take the next step: convert output
accumulation in the code-generating visitor classes.  Helper functions
outside these classes are not converted.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20180211093607.27351-20-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
[eblake: rebase to earlier guardstart cleanup]
Signed-off-by: Eric Blake <eblake@redhat.com>
  • Loading branch information
Markus Armbruster authored and ebblake committed Mar 2, 2018
1 parent 834a3f3 commit 71b3f04
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 243 deletions.
71 changes: 28 additions & 43 deletions scripts/qapi/commands.py
Expand Up @@ -223,44 +223,15 @@ def gen_registry(registry, prefix):
return ret


class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
class QAPISchemaGenCommandVisitor(QAPISchemaMonolithicCVisitor):

def __init__(self, prefix):
self._prefix = prefix
self.decl = None
self.defn = None
self._regy = None
self._visited_ret_types = None

def visit_begin(self, schema):
self.decl = ''
self.defn = ''
QAPISchemaMonolithicCVisitor.__init__(
self, prefix, 'qmp-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._prefix)
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)


def gen_commands(schema, output_dir, prefix):
blurb = ' * Schema-defined QAPI/QMP commands'
genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__)

genc.add(mcgen('''
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/module.h"
Expand All @@ -275,19 +246,33 @@ def gen_commands(schema, output_dir, prefix):
#include "%(prefix)sqmp-commands.h"
''',
prefix=prefix))

genh.add(mcgen('''
prefix=prefix))
self._genh.add(mcgen('''
#include "%(prefix)sqapi-types.h"
#include "qapi/qmp/dispatch.h"
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''',
prefix=prefix, c_prefix=c_name(prefix, protect=False)))
prefix=prefix,
c_prefix=c_name(prefix, protect=False)))

def visit_end(self):
self._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._visited_ret_types.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)


def gen_commands(schema, output_dir, prefix):
vis = QAPISchemaGenCommandVisitor(prefix)
schema.visit(vis)
genc.add(vis.defn)
genh.add(vis.decl)
genc.write(output_dir, prefix + 'qmp-commands.c')
genh.write(output_dir, prefix + 'qmp-commands.h')
vis.write(output_dir)
13 changes: 13 additions & 0 deletions scripts/qapi/common.py
Expand Up @@ -2050,3 +2050,16 @@ class QAPIGenDoc(QAPIGen):
def _top(self, fname):
return (QAPIGen._top(self, fname)
+ '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')


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)

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')
74 changes: 35 additions & 39 deletions scripts/qapi/doc.py
Expand Up @@ -197,33 +197,35 @@ def texi_entity(doc, what, base=None, variants=None,


class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor):
def __init__(self):
self.out = None
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):
Expand All @@ -235,44 +237,38 @@ def visit_command(self, name, info, arg_type, ret_type,
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 gen_doc(schema, output_dir, prefix):
if qapi.common.doc_required:
gen = qapi.common.QAPIGenDoc()
gen.add(texi_schema(schema))
gen.write(output_dir, prefix + 'qapi-doc.texi')
vis.freeform(doc)
vis.write(output_dir)
55 changes: 21 additions & 34 deletions scripts/qapi/events.py
Expand Up @@ -148,35 +148,15 @@ def gen_event_send(name, arg_type, boxed, event_enum_name):
return ret


class QAPISchemaGenEventVisitor(QAPISchemaVisitor):
class QAPISchemaGenEventVisitor(QAPISchemaMonolithicCVisitor):

def __init__(self, prefix):
QAPISchemaMonolithicCVisitor.__init__(
self, prefix, 'qapi-event',
' * Schema-defined QAPI/QMP events', __doc__)
self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
self.decl = None
self.defn = None
self._event_names = None

def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._event_names = []

def visit_end(self):
self.decl += gen_enum(self._enum_name, self._event_names)
self.defn += gen_enum_lookup(self._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._enum_name)
self._event_names.append(name)


def gen_events(schema, output_dir, prefix):
blurb = ' * Schema-defined QAPI/QMP events'
genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__)

genc.add(mcgen('''
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "%(prefix)sqapi-event.h"
Expand All @@ -187,18 +167,25 @@ def gen_events(schema, output_dir, prefix):
#include "qapi/qmp-event.h"
''',
prefix=prefix))

genh.add(mcgen('''
prefix=prefix))
self._genh.add(mcgen('''
#include "qapi/util.h"
#include "%(prefix)sqapi-types.h"
''',
prefix=prefix))
prefix=prefix))

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)


def gen_events(schema, output_dir, prefix):
vis = QAPISchemaGenEventVisitor(prefix)
schema.visit(vis)
genc.add(vis.defn)
genh.add(vis.decl)
genc.write(output_dir, prefix + 'qapi-event.c')
genh.write(output_dir, prefix + 'qapi-event.h')
vis.write(output_dir)
56 changes: 23 additions & 33 deletions scripts/qapi/introspect.py
Expand Up @@ -40,22 +40,26 @@ def to_c_string(string):
return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'


class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):

def __init__(self, prefix, unmask):
self._prefix = prefix
QAPISchemaMonolithicCVisitor.__init__(
self, prefix, 'qmp-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)sqmp-introspect.h"
''',
prefix=prefix))

def visit_begin(self, schema):
self._schema = schema

def visit_end(self):
# visit the types that are actually used
Expand All @@ -67,21 +71,21 @@ def visit_end(self):
# TODO can generate awfully long lines
jsons.extend(self._jsons)
name = c_name(self._prefix, protect=False) + 'qmp_schema_json'
self.decl = mcgen('''
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
Expand Down Expand Up @@ -169,20 +173,6 @@ def visit_event(self, name, info, arg_type, boxed):


def gen_introspect(schema, output_dir, prefix, opt_unmask):
blurb = ' * QAPI/QMP schema introspection'
genc = QAPIGenC(blurb, __doc__)
genh = QAPIGenH(blurb, __doc__)

genc.add(mcgen('''
#include "qemu/osdep.h"
#include "%(prefix)sqmp-introspect.h"
''',
prefix=prefix))

vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
schema.visit(vis)
genc.add(vis.defn)
genh.add(vis.decl)
genc.write(output_dir, prefix + 'qmp-introspect.c')
genh.write(output_dir, prefix + 'qmp-introspect.h')
vis.write(output_dir)

0 comments on commit 71b3f04

Please sign in to comment.