From 7e9c1707e14e50d5447cc6c3d3b20a641331ad18 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Nov 2019 19:25:46 +0100 Subject: qapi: Tweak "command returns a nice type" check for clarity Signed-off-by: Markus Armbruster Message-Id: <20191120182551.23795-2-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi/schema.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cf0045f..cfb574c 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -711,10 +711,11 @@ class QAPISchemaCommand(QAPISchemaEntity): self.ret_type = schema.resolve_type( self._ret_type_name, self.info, "command's 'returns'") if self.name not in self.info.pragma.returns_whitelist: - if not (isinstance(self.ret_type, QAPISchemaObjectType) - or (isinstance(self.ret_type, QAPISchemaArrayType) - and isinstance(self.ret_type.element_type, - QAPISchemaObjectType))): + typ = self.ret_type + if isinstance(typ, QAPISchemaArrayType): + typ = self.ret_type.element_type + assert typ + if not isinstance(typ, QAPISchemaObjectType): raise QAPISemError( self.info, "command's 'returns' cannot take %s" -- cgit v1.1 From 00ca24ff9e5fc3c6ae608226908e6864ad8193a8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Nov 2019 19:25:48 +0100 Subject: qapi: Generate command registration stuff into separate files Having to include qapi-commands.h just for qmp_init_marshal() is suboptimal. Generate it into separate files. This lets monitor/misc.c, qga/main.c, and the generated qapi-commands-FOO.h include less. Signed-off-by: Markus Armbruster Message-Id: <20191120182551.23795-4-armbru@redhat.com> [Typos in docs/devel/qapi-code-gen.txt fixed] Reviewed-by: Eric Blake --- scripts/qapi/commands.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index ab98e50..47f4a18 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -263,18 +263,25 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): commands=commands, visit=visit)) self._genh.add(mcgen(''' #include "%(types)s.h" -#include "qapi/qmp/dispatch.h" ''', types=types)) def visit_end(self): - (genc, genh) = self._module[self._main_module] - genh.add(mcgen(''' + self._add_system_module('init', ' * QAPI Commands initialization') + self._genh.add(mcgen(''' +#include "qapi/qmp/dispatch.h" + 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)) + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqapi-commands.h" +#include "%(prefix)sqapi-init-commands.h" +''', + prefix=self._prefix)) + self._genc.add(gen_registry(self._regy.get_content(), self._prefix)) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig, -- cgit v1.1 From a9f1dd7ee001b645b81ad67217b582e51a44d545 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Nov 2019 19:25:49 +0100 Subject: qapi: Proper intermediate representation for modules Modules are represented only by their names so far. Introduce class QAPISchemaModule. So far, it merely wraps the name. The next patch will put it to more interesting use. Once again, arrays spice up the patch a bit. For any other type, @info points to the definition, which lets us map from @info to module. For arrays, there is no definition, and @info points to the first use instead. We have to use the element type's module instead, which is only available after .check(). Signed-off-by: Markus Armbruster Message-Id: <20191120182551.23795-5-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi/schema.py | 63 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 20 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cfb574c..0f2e0dc 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -50,9 +50,6 @@ class QAPISchemaEntity(object): def check(self, schema): assert not self._checked - if self.info: - self._module = os.path.relpath(self.info.fname, - os.path.dirname(schema.fname)) seen = {} for f in self.features: f.check_clash(self.info, seen) @@ -68,6 +65,13 @@ class QAPISchemaEntity(object): if self.doc: self.doc.check() + def _set_module(self, schema, info): + assert self._checked + self._module = schema.module_by_fname(info and info.fname) + + def set_module(self, schema): + self._set_module(schema, self.info) + @property def ifcond(self): assert self._checked @@ -75,7 +79,7 @@ class QAPISchemaEntity(object): @property def module(self): - assert self._checked + assert self._module or not self.info return self._module def is_implicit(self): @@ -135,15 +139,19 @@ class QAPISchemaVisitor(object): pass -class QAPISchemaInclude(QAPISchemaEntity): +class QAPISchemaModule(object): + def __init__(self, name): + self.name = name + - def __init__(self, fname, info): +class QAPISchemaInclude(QAPISchemaEntity): + def __init__(self, sub_module, info): QAPISchemaEntity.__init__(self, None, info, None) - self.fname = fname + self._sub_module = sub_module def visit(self, visitor): QAPISchemaEntity.visit(self, visitor) - visitor.visit_include(self.fname, self.info) + visitor.visit_include(self._sub_module.name, self.info) class QAPISchemaType(QAPISchemaEntity): @@ -276,16 +284,14 @@ class QAPISchemaArrayType(QAPISchemaType): self.info and self.info.defn_meta) assert not isinstance(self.element_type, QAPISchemaArrayType) + def set_module(self, schema): + self._set_module(schema, self.element_type.info) + @property def ifcond(self): assert self._checked return self.element_type.ifcond - @property - def module(self): - assert self._checked - return self.element_type.module - def is_implicit(self): return True @@ -783,6 +789,10 @@ class QAPISchema(object): self.docs = parser.docs self._entity_list = [] self._entity_dict = {} + self._module_dict = {} + self._schema_dir = os.path.dirname(fname) + self._make_module(None) # built-ins + self._make_module(fname) self._predefining = True self._def_predefineds() self._predefining = False @@ -826,14 +836,26 @@ class QAPISchema(object): info, "%s uses unknown type '%s'" % (what, name)) return typ + def _module_name(self, fname): + if fname is None: + return None + return os.path.relpath(fname, self._schema_dir) + + def _make_module(self, fname): + name = self._module_name(fname) + if not name in self._module_dict: + self._module_dict[name] = QAPISchemaModule(name) + return self._module_dict[name] + + def module_by_fname(self, fname): + name = self._module_name(fname) + assert name in self._module_dict + return self._module_dict[name] + 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.fname)) - self._def_entity(QAPISchemaInclude(fname, info)) + self._def_entity(QAPISchemaInclude(self._make_module(include), info)) def _def_builtin_type(self, name, json_type, c_type): self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) @@ -1065,15 +1087,16 @@ class QAPISchema(object): ent.check(self) ent.connect_doc() ent.check_doc() + for ent in self._entity_list: + ent.set_module(self) def visit(self, visitor): visitor.visit_begin(self) module = None - visitor.visit_module(module) for entity in self._entity_list: if visitor.visit_needed(entity): if entity.module != module: module = entity.module - visitor.visit_module(module) + visitor.visit_module(module.name) entity.visit(visitor) visitor.visit_end() -- cgit v1.1 From 3e7fb5811baab213dcc7149c3aa69442d683c26c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Nov 2019 19:25:50 +0100 Subject: qapi: Fix code generation for empty modules When a sub-module doesn't contain any definitions, we don't generate code for it, but we do generate the #include. We generate code only for modules that get visited. QAPISchema.visit() visits only modules that have definitions. It can visit modules multiple times. Clean this up as follows. Collect entities in their QAPISchemaModule. Have QAPISchema.visit() call QAPISchemaModule.visit() for each module. Have QAPISchemaModule.visit() call .visit_module() for itself, and QAPISchemaEntity.visit() for each of its entities. This way, we visit each module exactly once. Signed-off-by: Markus Armbruster Message-Id: <20191120182551.23795-6-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi/schema.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index 0f2e0dc..0bfc525 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -68,6 +68,7 @@ class QAPISchemaEntity(object): def _set_module(self, schema, info): assert self._checked self._module = schema.module_by_fname(info and info.fname) + self._module.add_entity(self) def set_module(self, schema): self._set_module(schema, self.info) @@ -77,11 +78,6 @@ class QAPISchemaEntity(object): assert self._checked return self._ifcond - @property - def module(self): - assert self._module or not self.info - return self._module - def is_implicit(self): return not self.info @@ -142,6 +138,16 @@ class QAPISchemaVisitor(object): class QAPISchemaModule(object): def __init__(self, name): self.name = name + self._entity_list = [] + + def add_entity(self, ent): + self._entity_list.append(ent) + + def visit(self, visitor): + visitor.visit_module(self.name) + for entity in self._entity_list: + if visitor.visit_needed(entity): + entity.visit(visitor) class QAPISchemaInclude(QAPISchemaEntity): @@ -1093,10 +1099,6 @@ 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.name) - entity.visit(visitor) + for mod in self._module_dict.values(): + mod.visit(visitor) visitor.visit_end() -- cgit v1.1 From 3bef3aaec91815b75a78a4c12ca92ac3cec53faf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 20 Nov 2019 19:25:51 +0100 Subject: qapi: Simplify QAPISchemaModularCVisitor Since the previous commit, QAPISchemaVisitor.visit_module() is called just once. Simplify QAPISchemaModularCVisitor accordingly. Signed-off-by: Markus Armbruster Message-Id: <20191120182551.23795-7-armbru@redhat.com> Reviewed-by: Eric Blake --- scripts/qapi/commands.py | 2 +- scripts/qapi/events.py | 2 +- scripts/qapi/gen.py | 28 ++++++++++++++-------------- scripts/qapi/types.py | 5 +++-- scripts/qapi/visit.py | 8 ++++---- 5 files changed, 23 insertions(+), 22 deletions(-) (limited to 'scripts/qapi') diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 47f4a18..afa55b0 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -239,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-commands', - ' * Schema-defined QAPI/QMP commands', __doc__) + ' * Schema-defined QAPI/QMP commands', None, __doc__) self._regy = QAPIGenCCode(None) self._visited_ret_types = {} diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 10fc509..2bde3e6 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -140,7 +140,7 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-events', - ' * Schema-defined QAPI/QMP events', __doc__) + ' * Schema-defined QAPI/QMP events', None, __doc__) self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) self._event_enum_members = [] self._event_emit_name = c_name(prefix + 'qapi_event_emit') diff --git a/scripts/qapi/gen.py b/scripts/qapi/gen.py index 112b6d9..95afae0 100644 --- a/scripts/qapi/gen.py +++ b/scripts/qapi/gen.py @@ -201,10 +201,11 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): class QAPISchemaModularCVisitor(QAPISchemaVisitor): - def __init__(self, prefix, what, blurb, pydoc): + def __init__(self, prefix, what, user_blurb, builtin_blurb, pydoc): self._prefix = prefix self._what = what - self._blurb = blurb + self._user_blurb = user_blurb + self._builtin_blurb = builtin_blurb self._pydoc = pydoc self._genc = None self._genh = None @@ -245,7 +246,7 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): genc = QAPIGenC(basename + '.c', blurb, self._pydoc) genh = QAPIGenH(basename + '.h', blurb, self._pydoc) self._module[name] = (genc, genh) - self._set_module(name) + self._genc, self._genh = self._module[name] def _add_user_module(self, name, blurb): assert self._is_user_module(name) @@ -256,9 +257,6 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): def _add_system_module(self, name, blurb): self._add_module(name and './' + name, blurb) - def _set_module(self, name): - self._genc, self._genh = self._module[name] - def write(self, output_dir, opt_builtins=False): for name in self._module: if self._is_builtin_module(name) and not opt_builtins: @@ -271,15 +269,17 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): pass def visit_module(self, name): - if name in self._module: - self._set_module(name) - elif self._is_builtin_module(name): - # The built-in module has not been created. No code may - # be generated. - self._genc = None - self._genh = None + if name is None: + if self._builtin_blurb: + self._add_system_module(None, self._builtin_blurb) + self._begin_system_module(name) + else: + # The built-in module has not been created. No code may + # be generated. + self._genc = None + self._genh = None else: - self._add_user_module(name, self._blurb) + self._add_user_module(name, self._user_blurb) self._begin_user_module(name) def visit_include(self, name, info): diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index d8751da..99dcaf7 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -243,8 +243,9 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-types', ' * Schema-defined QAPI types', - __doc__) - self._add_system_module(None, ' * Built-in QAPI types') + ' * Built-in QAPI types', __doc__) + + def _begin_system_module(self, name): 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 c72f2bc..4efce62 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -285,8 +285,9 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): def __init__(self, prefix): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', - __doc__) - self._add_system_module(None, ' * Built-in QAPI visitors') + ' * Built-in QAPI visitors', __doc__) + + def _begin_system_module(self, name): self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/error.h" @@ -296,8 +297,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): #include "qapi/visitor.h" #include "qapi/qapi-builtin-types.h" -''', - prefix=prefix)) +''')) def _begin_user_module(self, name): types = self._module_basename('qapi-types', name) -- cgit v1.1