[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v3 40/49] RFC: qapi: learn to split schema by 'top-u
From: |
Marc-André Lureau |
Subject: |
[Qemu-devel] [PATCH v3 40/49] RFC: qapi: learn to split schema by 'top-unit' |
Date: |
Wed, 21 Mar 2018 12:52:02 +0100 |
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 <address@hidden>
---
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(-)
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 3d3e97f737..3ca36fc33b 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -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):
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 174b39ea50..bff8de05d1 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -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"
diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py
index f02b65cf5a..88bfcfcf2a 100644
--- a/scripts/qapi/events.py
+++ b/scripts/qapi/events.py
@@ -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))
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index b0d3ddb596..ac0c4603d4 100644
--- a/scripts/qapi/types.py
+++ b/scripts/qapi/types.py
@@ -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"
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index dc5a3173fc..60625aae72 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -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"
--
2.16.2.521.g9aa15f885a
- [Qemu-devel] [PATCH v3 32/49] qapi2texi: add 'If:' condition to enum values, (continued)
- [Qemu-devel] [PATCH v3 32/49] qapi2texi: add 'If:' condition to enum values, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 30/49] docs: document schema configuration, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 31/49] qapi2texi: add 'If:' section to generated documentation, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 33/49] qapi2texi: add 'If:' condition to struct members, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 35/49] qapi: add conditions to VNC type/commands/events on the schema, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 34/49] qapi2texi: add condition to variants, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 36/49] qapi: add conditions to SPICE type/commands/events on the schema, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 37/49] qapi: add conditions to REPLICATION type/commands on the schema, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 38/49] build-sys: move qmp-introspect per target, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 39/49] qapi-commands: don't initialize command list in qmp_init_marshall(), Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 40/49] RFC: qapi: learn to split schema by 'top-unit',
Marc-André Lureau <=
- [Qemu-devel] [PATCH v3 41/49] qapi: add a top-unit 'target' schema, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 44/49] target.json: add a note about query-cpu* not being s390x-specific, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 42/49] qapi: make rtc-reset-reinjection and SEV depend on TARGET_I386, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 43/49] qapi: make s390 commands depend on TARGET_S390X, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 45/49] qapi: make query-gic-capabilities depend on TARGET_ARM, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 46/49] qapi: make query-cpu-model-expansion depend on s390 or x86, Marc-André Lureau, 2018/03/21
- [Qemu-devel] [PATCH v3 47/49] qapi: make query-cpu-definitions depend on specific targets, Marc-André Lureau, 2018/03/21