qapi: Record 'include' directives in intermediate representation

The include directive permits modular QAPI schemata, but the generated
code is monolithic all the same. To permit generating modular code,
the front end needs to pass more information on inclusions to the back
ends. The commit before last added the necessary information to the
parse tree. This commit adds it to the intermediate representation
and its QAPISchemaVisitor. A later commit will use this to to
generate modular code.

New entity QAPISchemaInclude represents inclusions. Call new visitor
method visit_include() for it, so visitors can see the sub-modules a
module includes.

Note that unlike other entities, QAPISchemaInclude has no name, and is
therefore not added to entity_dict.

New QAPISchemaEntity attribute @module names the entity's source file.
Call new visitor method visit_module() when it changes during a visit,
so visitors can keep track of the module being visited.

Backports commit cf40a0a5c2e1091846974cc8cc95a60e0b1db4af from qemu
This commit is contained in:
Markus Armbruster 2018-03-09 09:03:14 -05:00 committed by Lioncash
parent 6fe71ebc68
commit 58246ea9d9
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -993,8 +993,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
@ -1023,10 +1024,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
@ -1053,6 +1060,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.
@ -1480,6 +1497,7 @@ class QAPISchemaEvent(QAPISchemaEntity):
class QAPISchema(object):
def __init__(self, fname):
self._fname = fname
parser = QAPISchemaParser(open(fname, 'r'))
exprs = check_exprs(parser.exprs)
self.docs = parser.docs
@ -1494,9 +1512,13 @@ class QAPISchema(object):
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
assert ent.name is None or ent.name not in self._entity_dict
self._entity_list.append(ent)
self._entity_dict[ent.name] = 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)
@ -1507,6 +1529,15 @@ 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
@ -1689,7 +1720,7 @@ class QAPISchema(object):
elif 'event' in expr:
self._def_event(expr, info, doc)
elif 'include' in expr:
pass
self._def_include(expr, info, doc)
else:
assert False
@ -1699,8 +1730,12 @@ class QAPISchema(object):
def visit(self, visitor):
visitor.visit_begin(self)
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()