@@ -235,14 +235,20 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
QAPISchemaModularCVisitor.__init__(
self, prefix, 'qapi-commands',
' * Schema-defined QAPI/QMP commands', __doc__)
+
+ def visit_unit_begin(self, unit):
+ super(self.__class__, self).visit_unit_begin(unit)
self._regy = QAPIGenCSnippet()
self._visited_ret_types = {}
- def _begin_module(self, name):
+ def _begin_module(self, name, main_module):
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)
+ commands = self._module_basename('qapi-commands', name,
+ self._unit, main_module)
+ types = self._module_basename('qapi-types', name,
+ self._unit, main_module)
+ visit = self._module_basename('qapi-visit', name,
+ self._unit, main_module)
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qemu-common.h"
@@ -265,13 +271,13 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
''',
types=types))
- def visit_end(self):
- (genc, genh) = self._module[self._main_module]
+ def visit_unit_end(self):
+ (genc, genh) = self.get_module_gen(self._main_module)
genh.add(mcgen('''
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
''',
- c_prefix=c_name(self._prefix, protect=False)))
- genc.add(gen_registry(self._regy.get_content(), self._prefix))
+ c_prefix=c_name(self._prefix_unit(), protect=False)))
+ genc.add(gen_registry(self._regy.get_content(), self._prefix_unit()))
def visit_command(self, name, info, ifcond, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
@@ -271,11 +271,12 @@ class QAPISchemaParser(object):
self.exprs = []
self.docs = []
self.accept()
+ self._top_unit = None
cur_doc = None
while self.tok is not None:
info = {'file': self.fname, 'line': self.line,
- 'parent': self.incl_info}
+ 'parent': self.incl_info, 'top-unit': self._top_unit}
if self.tok == '#':
self.reject_expr_doc(cur_doc)
cur_doc = self.get_doc(info)
@@ -298,6 +299,9 @@ class QAPISchemaParser(object):
exprs_include = self._include(include, info, incl_fname,
previously_included)
if exprs_include:
+ incl_info = self.exprs[-1]['info']
+ if exprs_include._top_unit:
+ incl_info['has-pragma-top-unit'] = exprs_include._top_unit
self.exprs.extend(exprs_include.exprs)
self.docs.extend(exprs_include.docs)
elif "pragma" in expr:
@@ -356,6 +360,11 @@ class QAPISchemaParser(object):
raise QAPISemError(info,
"Pragma 'doc-required' must be boolean")
doc_required = value
+ elif name == 'top-unit':
+ if not isinstance(value, str):
+ raise QAPISemError(info,
+ "Pragma 'top-unit' must be a string")
+ self._top_unit = value
elif name == 'returns-whitelist':
if (not isinstance(value, list)
or any([not isinstance(elt, str) for elt in value])):
@@ -1098,6 +1107,11 @@ class QAPISchemaEntity(object):
def c_name(self):
return c_name(self.name)
+ def get_top_unit(self):
+ if self.info:
+ return self.info['top-unit']
+ return None
+
def check(self, schema):
if isinstance(self._ifcond, QAPISchemaType):
# inherit the condition from a type
@@ -1118,6 +1132,12 @@ class QAPISchemaVisitor(object):
def visit_begin(self, schema):
pass
+ def visit_unit_begin(self, unit):
+ pass
+
+ def visit_unit_end(self):
+ pass
+
def visit_end(self):
pass
@@ -1613,7 +1633,7 @@ class QAPISchema(object):
parser = QAPISchemaParser(open(fname, 'r'))
exprs = check_exprs(parser.exprs)
self.docs = parser.docs
- self._entity_list = []
+ self._entity_list = {} # dict of unit name -> list of entity
self._entity_dict = {}
self._predefining = True
self._def_predefineds()
@@ -1625,7 +1645,8 @@ class QAPISchema(object):
# Only the predefined types are allowed to not have info
assert ent.info or self._predefining
assert ent.name is None or ent.name not in self._entity_dict
- self._entity_list.append(ent)
+ entity_list = self._entity_list.setdefault(ent.get_top_unit(), [])
+ entity_list.append(ent)
if ent.name is not None:
self._entity_dict[ent.name] = ent
if ent.info:
@@ -1870,18 +1891,23 @@ class QAPISchema(object):
assert False
def check(self):
- for ent in self._entity_list:
- ent.check(self)
+ for unit in self._entity_list:
+ for ent in self._entity_list[unit]:
+ ent.check(self)
def visit(self, visitor):
visitor.visit_begin(self)
- module = None
- for entity in self._entity_list:
- if visitor.visit_needed(entity):
+ for unit in self._entity_list:
+ module = None
+ visitor.visit_unit_begin(unit)
+ for entity in self._entity_list[unit]:
+ if not visitor.visit_needed(entity):
+ continue
if entity.module != module:
module = entity.module
visitor.visit_module(module)
entity.visit(visitor)
+ visitor.visit_unit_end()
visitor.visit_end()
@@ -2301,6 +2327,19 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
self._genh.write(output_dir, self._prefix + self._what + '.h')
+class QAPIGenCModule(object):
+
+ def __init__(self, blurb, pydoc, unit, main_module=False):
+ self.genc = QAPIGenC(blurb, pydoc)
+ self.genh = QAPIGenH(blurb, pydoc)
+ self.unit = unit
+ self.main_module = main_module
+
+ def write(self, output_dir, basename):
+ self.genc.write(output_dir, basename + '.c')
+ self.genh.write(output_dir, basename + '.h')
+
+
class QAPISchemaModularCVisitor(QAPISchemaVisitor):
def __init__(self, prefix, what, blurb, pydoc):
@@ -2309,48 +2348,70 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor):
self._blurb = blurb
self._pydoc = pydoc
self._module = {}
+ self._unit = None
self._main_module = None
- def _module_basename(self, what, name):
+ def _module_basename(self, what, name, unit=None, main_module=False):
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:
+ if unit:
+ basename = unit + '-' + basename
+ if main_module:
return basename
return basename + '-' + os.path.splitext(os.path.basename(name))[0]
+ def _prefix_unit(self):
+ if self._unit:
+ return self._prefix + self._unit + '-'
+ return self._prefix
+
+ def visit_unit_begin(self, unit):
+ self._unit = unit
+ self._main_module = None
+
def _add_module(self, name, blurb):
- if self._main_module is None and name is not None:
+ main_module = False
+ if (name is not None and
+ ((self._unit is None and self._main_module is None) or
+ (self._unit == os.path.splitext(os.path.basename(name))[0]))):
self._main_module = name
- genc = QAPIGenC(blurb, self._pydoc)
- genh = QAPIGenH(blurb, self._pydoc)
- self._module[name] = (genc, genh)
+ main_module = True
+ self._module[name] = QAPIGenCModule(blurb, self._pydoc,
+ self._unit, main_module)
self._set_module(name)
+ return main_module
+
+ def get_module_gen(self, name):
+ mod = self._module[name]
+ return mod.genc, mod.genh
def _set_module(self, name):
- self._genc, self._genh = self._module[name]
+ self._genc, self._genh = self.get_module_gen(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')
+ module = self._module[name]
+ basename = self._module_basename(self._what, name,
+ module.unit, module.main_module)
+ module.write(output_dir, basename)
- def _begin_module(self, name):
+ def _begin_module(self, name, main_module):
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)
+ main_module = self._add_module(name, self._blurb)
+ self._begin_module(name, main_module)
def visit_include(self, name, info):
+ if 'has-pragma-top-unit' in info:
+ return
basename = self._module_basename(self._what, name)
self._genh.preamble_add(mcgen('''
#include "%(basename)s.h"
@@ -58,7 +58,12 @@ def gen_param_var(typ):
return ret
-def gen_event_send(name, arg_type, boxed, event_enum_name):
+def gen_event_send(unit, name, arg_type, boxed, event_enum_name):
+ if not unit:
+ unit = ''
+ else:
+ unit += '_'
+
# 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
@@ -87,7 +92,7 @@ def gen_event_send(name, arg_type, boxed, event_enum_name):
ret += mcgen('''
- emit = qmp_event_get_func_emit();
+ emit = %(unit)sqmp_event_get_func_emit();
if (!emit) {
return;
}
@@ -95,7 +100,7 @@ def gen_event_send(name, arg_type, boxed, event_enum_name):
qmp = qmp_event_build_dict("%(name)s");
''',
- name=name)
+ name=name, unit=unit)
if arg_type and not arg_type.is_empty():
ret += mcgen('''
@@ -154,12 +159,17 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
QAPISchemaModularCVisitor.__init__(
self, prefix, 'qapi-events',
' * Schema-defined QAPI/QMP events', __doc__)
- self._enum_name = c_name(prefix + 'QAPIEvent', protect=False)
+
+ def visit_unit_begin(self, unit):
+ super(self.__class__, self).visit_unit_begin(unit)
+ self._enum_name = c_name(self._prefix_unit() + 'QAPIEvent', protect=False)
self._event_names = []
- def _begin_module(self, name):
- types = self._module_basename('qapi-types', name)
- visit = self._module_basename('qapi-visit', name)
+ def _begin_module(self, name, main_module):
+ types = self._module_basename('qapi-types', name,
+ self._unit, main_module)
+ visit = self._module_basename('qapi-visit', name,
+ self._unit, main_module)
self._genc.add(mcgen('''
#include "qemu/osdep.h"
#include "qemu-common.h"
@@ -171,7 +181,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
#include "qapi/qmp-event.h"
''',
- visit=visit, prefix=self._prefix))
+ visit=visit, prefix=self._prefix_unit()))
self._genh.add(mcgen('''
#include "qapi/util.h"
#include "%(types)s.h"
@@ -179,15 +189,16 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
''',
types=types))
- def visit_end(self):
- (genc, genh) = self._module[self._main_module]
+ def visit_unit_end(self):
+ (genc, genh) = self.get_module_gen(self._main_module)
genh.add(gen_enum(self._enum_name, self._event_names))
genc.add(gen_enum_lookup(self._enum_name, self._event_names))
def visit_event(self, name, info, ifcond, arg_type, boxed):
with ifcontext(ifcond, self._genh, self._genc):
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._genc.add(gen_event_send(self._unit, name,
+ arg_type, boxed, self._enum_name))
self._event_names.append(QAPISchemaMember(name, ifcond))
@@ -194,9 +194,11 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor):
#include "qapi/util.h"
'''))
- def _begin_module(self, name):
- types = self._module_basename('qapi-types', name)
- visit = self._module_basename('qapi-visit', name)
+ def _begin_module(self, name, main_module):
+ types = self._module_basename('qapi-types', name,
+ self._unit, main_module)
+ visit = self._module_basename('qapi-visit', name,
+ self._unit, main_module)
self._genc.preamble_add(mcgen('''
#include "qemu/osdep.h"
#include "qapi/dealloc-visitor.h"
@@ -289,9 +289,11 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor):
''',
prefix=prefix))
- def _begin_module(self, name):
- types = self._module_basename('qapi-types', name)
- visit = self._module_basename('qapi-visit', name)
+ def _begin_module(self, name, main_module):
+ types = self._module_basename('qapi-types', name,
+ self._unit, main_module)
+ visit = self._module_basename('qapi-visit', name,
+ self._unit, main_module)
self._genc.preamble_add(mcgen('''
#include "qemu/osdep.h"
#include "qemu-common.h"
Another take at making the schema modular, this time by introducing the concept of a 'top-unit'. With this approach, each module is part of a top-unit, which is visited first before visiting the module themselfs. The default 'top-unit' is None. Else, a module belong to a 'top-unit' whose name is given by the 'top-unit' pragma value. This gives a chance to the generators to break generated output into different files. The point of this excercice is to have modules that can be compiled per qemu targets, instead of everything belonging to the top schema without the right to use poisoin defines for conditional compilation. Generated types, visitors, events and commands are split by 'top-unit'. The generated introspection and documentation remain monolithic. TODO: if this approach is acceptable, write tests & doc. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> --- scripts/qapi/commands.py | 22 +++++--- scripts/qapi/common.py | 105 +++++++++++++++++++++++++++++++-------- scripts/qapi/events.py | 33 ++++++++---- scripts/qapi/types.py | 8 +-- scripts/qapi/visit.py | 8 +-- 5 files changed, 129 insertions(+), 47 deletions(-)