A module can declare belonging to a 'top-unit', with the corresponding
pragma value (the default 'top-unit' is None).

The generators have a chance to break the generated output by
units (top-units are visited first).  Generated types, visitors,
events and commands are split by 'top-unit'. Generated introspection
and documentation remain monolithic.

In the following commits, a 'target' unit is introduced. The generated
'target' files will be allowed to used poisoned defines for
conditional compilation, since they will be built with the target
objects.

TODO: if this approach is acceptable, split the patch, write tests & doc.
Note: I don't really have better ideas, but open for suggestions...

Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com>
---
 scripts/qapi/commands.py |  22 +++++---
 scripts/qapi/common.py   | 105 +++++++++++++++++++++++++++++++--------
 scripts/qapi/events.py   |  37 ++++++++------
 scripts/qapi/types.py    |   8 +--
 scripts/qapi/visit.py    |   8 +--
 5 files changed, 130 insertions(+), 50 deletions(-)

diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 94313b8aef..e4dc92e70b 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -237,14 +237,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 = QAPIGenCCode()
         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"
@@ -267,13 +273,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_register_commands(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, allow_preconfig):
diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index 8c2d97369e..1ef3a4f70b 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -269,11 +269,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)
@@ -296,6 +297,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:
@@ -357,6 +361,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])):
@@ -1081,6 +1090,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
@@ -1101,6 +1115,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
 
@@ -1610,7 +1630,7 @@ class QAPISchema(object):
         parser = QAPISchemaParser(f)
         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()
@@ -1622,7 +1642,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:
@@ -1861,18 +1882,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()
 
 
@@ -2313,6 +2339,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):
@@ -2321,48 +2360,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 37ee5de682..b73e10e16d 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
@@ -86,7 +91,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;
     }
@@ -94,7 +99,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('''
@@ -143,12 +148,17 @@ class 
QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor):
         QAPISchemaModularCVisitor.__init__(
             self, prefix, 'qapi-events',
             ' * Schema-defined QAPI/QMP events', __doc__)
-        self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False)
+
+    def visit_unit_begin(self, unit):
+        super(self.__class__, self).visit_unit_begin(unit)
+        self._event_enum_name = c_name(self._prefix_unit() + 'QAPIEvent', 
protect=False)
         self._event_enum_members = []
 
-    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"
@@ -160,7 +170,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"
@@ -168,17 +178,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._event_enum_name, self._event_enum_members))
-        genc.add(gen_enum_lookup(self._event_enum_name,
-                                 self._event_enum_members))
+        genc.add(gen_enum_lookup(self._event_enum_name, 
self._event_enum_members))
 
     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._event_enum_name))
+            self._genc.add(gen_event_send(self._unit, name,
+                                          arg_type, boxed, 
self._event_enum_name))
         self._event_enum_members.append(QAPISchemaMember(name, ifcond))
 
 
diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py
index 62d4cf9f95..db85cf9b9b 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 82eab72b21..258ebdb690 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -298,9 +298,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.20.0


Reply via email to