diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2018-07-05 10:31:36 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2018-07-05 10:31:36 +0100 |
commit | 5dafaf4fbceeb4c5d204039045b50b2f37443ff4 (patch) | |
tree | db8692abda31876ebf09ba63d2e64b78c36a1aaa /scripts | |
parent | 8beb8cc64da2868acec270e4becb9fea8f9093dc (diff) | |
parent | 514337c142f9522f6ab89c3d2f964f446ebeb1cd (diff) |
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2018-07-03' into staging
QAPI patches for 2018-07-03
# gpg: Signature made Tue 03 Jul 2018 21:52:55 BST
# gpg: using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qapi-2018-07-03:
qapi: add conditions to SPICE type/commands/events on the schema
qapi: add conditions to VNC type/commands/events on the schema
qapi: add 'If:' section to generated documentation
qapi-types: add #if conditions to types & visitors
qapi/events: add #if conditions to events
qapi/commands: add #if conditions to commands
qapi-introspect: add preprocessor conditions to generated QLit
qapi-introspect: modify to_qlit() to append ',' on level > 0
qapi: add #if/#endif helpers
qapi: mcgen() shouldn't indent # lines
qapi: add 'ifcond' to visitor methods
qapi: leave the ifcond attribute undefined until check()
qapi: pass 'if' condition into QAPISchemaEntity objects
qapi: add 'if' to top-level expressions
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/qapi/commands.py | 26 | ||||
-rw-r--r-- | scripts/qapi/common.py | 280 | ||||
-rwxr-xr-x[-rw-r--r--] | scripts/qapi/doc.py | 32 | ||||
-rw-r--r-- | scripts/qapi/events.py | 8 | ||||
-rw-r--r-- | scripts/qapi/introspect.py | 47 | ||||
-rw-r--r-- | scripts/qapi/types.py | 58 | ||||
-rw-r--r-- | scripts/qapi/visit.py | 41 |
7 files changed, 347 insertions, 145 deletions
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 3b0867c14f..0f3c991918 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -239,7 +239,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-commands', ' * Schema-defined QAPI/QMP commands', __doc__) - self._regy = '' + self._regy = QAPIGenCCode() self._visited_ret_types = {} def _begin_module(self, name): @@ -275,20 +275,28 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); ''', c_prefix=c_name(self._prefix, protect=False))) - genc.add(gen_registry(self._regy, self._prefix)) + genc.add(gen_registry(self._regy.get_content(), self._prefix)) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): if not gen: return - self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + # FIXME: If T is a user-defined type, the user is responsible + # for making this work, i.e. to make T's condition the + # conjunction of the T-returning commands' conditions. If T + # is a built-in type, this isn't possible: the + # qmp_marshal_output_T() will be generated unconditionally. if ret_type and ret_type not in self._visited_ret_types[self._genc]: self._visited_ret_types[self._genc].add(ret_type) - self._genc.add(gen_marshal_output(ret_type)) - self._genh.add(gen_marshal_decl(name)) - self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy += gen_register_command(name, success_response, allow_oob, - allow_preconfig) + with ifcontext(ret_type.ifcond, + self._genh, self._genc, self._regy): + self._genc.add(gen_marshal_output(ret_type)) + with ifcontext(ifcond, self._genh, self._genc, self._regy): + self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) + self._genh.add(gen_marshal_decl(name)) + self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) + self._regy.add(gen_register_command(name, success_response, + allow_oob, allow_preconfig)) def gen_commands(schema, output_dir, prefix): diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 8b6708dbf1..9230a2a3e8 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -12,6 +12,7 @@ # See the COPYING file in the top-level directory. from __future__ import print_function +from contextlib import contextmanager import errno import os import re @@ -638,6 +639,27 @@ def add_name(name, info, meta, implicit=False): all_names[name] = meta +def check_if(expr, info): + + def check_if_str(ifcond, info): + if not isinstance(ifcond, str): + raise QAPISemError( + info, "'if' condition must be a string or a list of strings") + if ifcond == '': + raise QAPISemError(info, "'if' condition '' makes no sense") + + ifcond = expr.get('if') + if ifcond is None: + return + if isinstance(ifcond, list): + if ifcond == []: + raise QAPISemError(info, "'if' condition [] is useless") + for elt in ifcond: + check_if_str(elt, info) + else: + check_if_str(ifcond, info) + + def check_type(info, source, value, allow_array=False, allow_dict=False, allow_optional=False, allow_metas=[]): @@ -871,6 +893,8 @@ def check_keys(expr_elem, meta, required, optional=[]): raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) + if key == 'if': + check_if(expr, info) for key in required: if key not in expr: raise QAPISemError(info, "Key '%s' is missing from %s '%s'" @@ -899,28 +923,28 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' - check_keys(expr_elem, 'enum', ['data'], ['prefix']) + check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], - ['base', 'discriminator']) + ['base', 'discriminator', 'if']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' - check_keys(expr_elem, 'alternate', ['data']) + check_keys(expr_elem, 'alternate', ['data'], ['if']) elif 'struct' in expr: meta = 'struct' - check_keys(expr_elem, 'struct', ['data'], ['base']) + check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', - 'boxed', 'allow-oob', 'allow-preconfig']) + 'boxed', 'allow-oob', 'allow-preconfig', 'if']) elif 'event' in expr: meta = 'event' - check_keys(expr_elem, 'event', [], ['data', 'boxed']) + check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") @@ -978,8 +1002,16 @@ def check_exprs(exprs): # Schema compiler frontend # +def listify_cond(ifcond): + if not ifcond: + return [] + if not isinstance(ifcond, list): + return [ifcond] + return ifcond + + class QAPISchemaEntity(object): - def __init__(self, name, info, doc): + def __init__(self, name, info, doc, ifcond=None): assert name is None or isinstance(name, str) self.name = name self.module = None @@ -990,12 +1022,19 @@ class QAPISchemaEntity(object): # such place). self.info = info self.doc = doc + self._ifcond = ifcond # self.ifcond is set only after .check() def c_name(self): return c_name(self.name) def check(self, schema): - pass + if isinstance(self._ifcond, QAPISchemaType): + # inherit the condition from a type + typ = self._ifcond + typ.check(schema) + self.ifcond = typ.ifcond + else: + self.ifcond = listify_cond(self._ifcond) def is_implicit(self): return not self.info @@ -1024,26 +1063,26 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): pass - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): pass - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): pass - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): pass - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): pass - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): pass - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): pass @@ -1122,8 +1161,8 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, doc, values, prefix): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, values, prefix): + QAPISchemaType.__init__(self, name, info, doc, ifcond) for v in values: assert isinstance(v, QAPISchemaMember) v.set_owner(name) @@ -1132,6 +1171,7 @@ class QAPISchemaEnumType(QAPISchemaType): self.prefix = prefix def check(self, schema): + QAPISchemaType.check(self, schema) seen = {} for v in self.values: v.check_clash(self.info, seen) @@ -1152,20 +1192,23 @@ class QAPISchemaEnumType(QAPISchemaType): return 'string' def visit(self, visitor): - visitor.visit_enum_type(self.name, self.info, + visitor.visit_enum_type(self.name, self.info, self.ifcond, self.member_names(), self.prefix) class QAPISchemaArrayType(QAPISchemaType): def __init__(self, name, info, element_type): - QAPISchemaType.__init__(self, name, info, None) + QAPISchemaType.__init__(self, name, info, None, None) assert isinstance(element_type, str) self._element_type_name = element_type self.element_type = None def check(self, schema): + QAPISchemaType.check(self, schema) self.element_type = schema.lookup_type(self._element_type_name) assert self.element_type + self.element_type.check(schema) + self.ifcond = self.element_type.ifcond def is_implicit(self): return True @@ -1183,15 +1226,17 @@ class QAPISchemaArrayType(QAPISchemaType): return 'array of ' + elt_doc_type def visit(self, visitor): - visitor.visit_array_type(self.name, self.info, self.element_type) + visitor.visit_array_type(self.name, self.info, self.ifcond, + self.element_type) class QAPISchemaObjectType(QAPISchemaType): - def __init__(self, name, info, doc, base, local_members, variants): + def __init__(self, name, info, doc, ifcond, + base, local_members, variants): # struct has local_members, optional base, and no variants # flat union has base, variants, and no local_members # simple union has local_members, variants, and no base - QAPISchemaType.__init__(self, name, info, doc) + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert base is None or isinstance(base, str) for m in local_members: assert isinstance(m, QAPISchemaObjectTypeMember) @@ -1206,6 +1251,7 @@ class QAPISchemaObjectType(QAPISchemaType): self.members = None def check(self, schema): + QAPISchemaType.check(self, schema) if self.members is False: # check for cycles raise QAPISemError(self.info, "Object %s contains itself" % self.name) @@ -1263,9 +1309,9 @@ class QAPISchemaObjectType(QAPISchemaType): return 'object' def visit(self, visitor): - visitor.visit_object_type(self.name, self.info, + visitor.visit_object_type(self.name, self.info, self.ifcond, self.base, self.local_members, self.variants) - visitor.visit_object_type_flat(self.name, self.info, + visitor.visit_object_type_flat(self.name, self.info, self.ifcond, self.members, self.variants) @@ -1387,8 +1433,8 @@ class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): class QAPISchemaAlternateType(QAPISchemaType): - def __init__(self, name, info, doc, variants): - QAPISchemaType.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, variants): + QAPISchemaType.__init__(self, name, info, doc, ifcond) assert isinstance(variants, QAPISchemaObjectTypeVariants) assert variants.tag_member variants.set_owner(name) @@ -1396,6 +1442,7 @@ class QAPISchemaAlternateType(QAPISchemaType): self.variants = variants def check(self, schema): + QAPISchemaType.check(self, schema) self.variants.tag_member.check(schema) # Not calling self.variants.check_clash(), because there's nothing # to clash with @@ -1417,16 +1464,17 @@ class QAPISchemaAlternateType(QAPISchemaType): return 'value' def visit(self, visitor): - visitor.visit_alternate_type(self.name, self.info, self.variants) + visitor.visit_alternate_type(self.name, self.info, self.ifcond, + self.variants) def is_empty(self): return False class QAPISchemaCommand(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, ret_type, + def __init__(self, name, info, doc, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): - QAPISchemaEntity.__init__(self, name, info, doc) + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) assert not ret_type or isinstance(ret_type, str) self._arg_type_name = arg_type @@ -1440,6 +1488,7 @@ class QAPISchemaCommand(QAPISchemaEntity): self.allow_preconfig = allow_preconfig def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1459,7 +1508,7 @@ class QAPISchemaCommand(QAPISchemaEntity): assert isinstance(self.ret_type, QAPISchemaType) def visit(self, visitor): - visitor.visit_command(self.name, self.info, + visitor.visit_command(self.name, self.info, self.ifcond, self.arg_type, self.ret_type, self.gen, self.success_response, self.boxed, self.allow_oob, @@ -1467,14 +1516,15 @@ class QAPISchemaCommand(QAPISchemaEntity): class QAPISchemaEvent(QAPISchemaEntity): - def __init__(self, name, info, doc, arg_type, boxed): - QAPISchemaEntity.__init__(self, name, info, doc) + def __init__(self, name, info, doc, ifcond, arg_type, boxed): + QAPISchemaEntity.__init__(self, name, info, doc, ifcond) assert not arg_type or isinstance(arg_type, str) self._arg_type_name = arg_type self.arg_type = None self.boxed = boxed def check(self, schema): + QAPISchemaEntity.check(self, schema) if self._arg_type_name: self.arg_type = schema.lookup_type(self._arg_type_name) assert (isinstance(self.arg_type, QAPISchemaObjectType) or @@ -1491,7 +1541,8 @@ class QAPISchemaEvent(QAPISchemaEntity): raise QAPISemError(self.info, "Use of 'boxed' requires 'data'") def visit(self, visitor): - visitor.visit_event(self.name, self.info, self.arg_type, self.boxed) + visitor.visit_event(self.name, self.info, self.ifcond, + self.arg_type, self.boxed) class QAPISchema(object): @@ -1567,22 +1618,22 @@ class QAPISchema(object): ('null', 'null', 'QNull' + pointer_suffix)]: self._def_builtin_type(*t) self.the_empty_object_type = QAPISchemaObjectType( - 'q_empty', None, None, None, [], None) + 'q_empty', None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']) - self._def_entity(QAPISchemaEnumType('QType', None, None, + self._def_entity(QAPISchemaEnumType('QType', None, None, None, qtype_values, 'QTYPE')) def _make_enum_members(self, values): return [QAPISchemaMember(v) for v in values] - def _make_implicit_enum_type(self, name, info, values): + def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() name = name + 'Kind' # Use namespace reserved by add_name() self._def_entity(QAPISchemaEnumType( - name, info, None, self._make_enum_members(values), None)) + name, info, None, ifcond, self._make_enum_members(values), None)) return name def _make_array_type(self, element_type, info): @@ -1591,22 +1642,37 @@ class QAPISchema(object): self._def_entity(QAPISchemaArrayType(name, info, element_type)) return name - def _make_implicit_object_type(self, name, info, doc, role, members): + def _make_implicit_object_type(self, name, info, doc, ifcond, + role, members): if not members: return None # See also QAPISchemaObjectTypeMember._pretty_owner() name = 'q_obj_%s-%s' % (name, role) - if not self.lookup_entity(name, QAPISchemaObjectType): - self._def_entity(QAPISchemaObjectType(name, info, doc, None, - members, None)) + typ = self.lookup_entity(name, QAPISchemaObjectType) + if typ: + # The implicit object type has multiple users. This can + # happen only for simple unions' implicit wrapper types. + # Its ifcond should be the disjunction of its user's + # ifconds. Not implemented. Instead, we always pass the + # wrapped type's ifcond, which is trivially the same for all + # users. It's also necessary for the wrapper to compile. + # But it's not tight: the disjunction need not imply it. We + # may end up compiling useless wrapper types. + # TODO kill simple unions or implement the disjunction + assert ifcond == typ._ifcond # pylint: disable=protected-access + else: + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, + None, members, None)) return name def _def_enum_type(self, expr, info, doc): name = expr['enum'] data = expr['data'] prefix = expr.get('prefix') + ifcond = expr.get('if') self._def_entity(QAPISchemaEnumType( - name, info, doc, self._make_enum_members(data), prefix)) + name, info, doc, ifcond, + self._make_enum_members(data), prefix)) def _make_member(self, name, typ, info): optional = False @@ -1626,7 +1692,8 @@ class QAPISchema(object): name = expr['struct'] base = expr.get('base') data = expr['data'] - self._def_entity(QAPISchemaObjectType(name, info, doc, base, + ifcond = expr.get('if') + self._def_entity(QAPISchemaObjectType(name, info, doc, ifcond, base, self._make_members(data, info), None)) @@ -1638,18 +1705,21 @@ class QAPISchema(object): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) typ = self._make_implicit_object_type( - typ, info, None, 'wrapper', [self._make_member('data', typ, info)]) + typ, info, None, self.lookup_type(typ), + 'wrapper', [self._make_member('data', typ, info)]) return QAPISchemaObjectTypeVariant(case, typ) def _def_union_type(self, expr, info, doc): name = expr['union'] data = expr['data'] base = expr.get('base') + ifcond = expr.get('if') tag_name = expr.get('discriminator') tag_member = None if isinstance(base, dict): - base = (self._make_implicit_object_type( - name, info, doc, 'base', self._make_members(base, info))) + base = self._make_implicit_object_type( + name, info, doc, ifcond, + 'base', self._make_members(base, info)) if tag_name: variants = [self._make_variant(key, value) for (key, value) in data.items()] @@ -1657,12 +1727,12 @@ class QAPISchema(object): else: variants = [self._make_simple_variant(key, value, info) for (key, value) in data.items()] - typ = self._make_implicit_enum_type(name, info, + typ = self._make_implicit_enum_type(name, info, ifcond, [v.name for v in variants]) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( - QAPISchemaObjectType(name, info, doc, base, members, + QAPISchemaObjectType(name, info, doc, ifcond, base, members, QAPISchemaObjectTypeVariants(tag_name, tag_member, variants))) @@ -1670,11 +1740,12 @@ class QAPISchema(object): def _def_alternate_type(self, expr, info, doc): name = expr['alternate'] data = expr['data'] + ifcond = expr.get('if') variants = [self._make_variant(key, value) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( - QAPISchemaAlternateType(name, info, doc, + QAPISchemaAlternateType(name, info, doc, ifcond, QAPISchemaObjectTypeVariants(None, tag_member, variants))) @@ -1688,13 +1759,14 @@ class QAPISchema(object): boxed = expr.get('boxed', False) allow_oob = expr.get('allow-oob', False) allow_preconfig = expr.get('allow-preconfig', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) if isinstance(rets, list): assert len(rets) == 1 rets = self._make_array_type(rets[0], info) - self._def_entity(QAPISchemaCommand(name, info, doc, data, rets, + self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets, gen, success_response, boxed, allow_oob, allow_preconfig)) @@ -1702,10 +1774,11 @@ class QAPISchema(object): name = expr['event'] data = expr.get('data') boxed = expr.get('boxed', False) + ifcond = expr.get('if') if isinstance(data, OrderedDict): data = self._make_implicit_object_type( - name, info, doc, 'arg', self._make_members(data, info)) - self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed)) + name, info, doc, ifcond, 'arg', self._make_members(data, info)) + self._def_entity(QAPISchemaEvent(name, info, doc, ifcond, data, boxed)) def _def_exprs(self, exprs): for expr_elem in exprs: @@ -1869,8 +1942,8 @@ def cgen(code, **kwds): if indent_level: indent = genindent(indent_level) # re.subn() lacks flags support before Python 2.7, use re.compile() - raw = re.subn(re.compile(r'^.', re.MULTILINE), - indent + r'\g<0>', raw) + raw = re.subn(re.compile(r'^(?!(#|$))', re.MULTILINE), + indent, raw) raw = raw[0] return re.sub(re.escape(eatspace) + r' *', '', raw) @@ -1902,6 +1975,40 @@ def guardend(name): name=guardname(name)) +def gen_if(ifcond): + ret = '' + for ifc in ifcond: + ret += mcgen(''' +#if %(cond)s +''', cond=ifc) + return ret + + +def gen_endif(ifcond): + ret = '' + for ifc in reversed(ifcond): + ret += mcgen(''' +#endif /* %(cond)s */ +''', cond=ifc) + return ret + + +def _wrap_ifcond(ifcond, before, after): + if before == after: + return after # suppress empty #if ... #endif + + assert after.startswith(before) + out = before + added = after[len(before):] + if added[0] == '\n': + out += '\n' + added = added[1:] + out += gen_if(ifcond) + out += added + out += gen_endif(ifcond) + return out + + def gen_enum_lookup(name, values, prefix=None): ret = mcgen(''' @@ -1999,6 +2106,10 @@ class QAPIGen(object): def add(self, text): self._body += text + def get_content(self, fname=None): + return (self._top(fname) + self._preamble + self._body + + self._bottom(fname)) + def _top(self, fname): return '' @@ -2019,8 +2130,7 @@ class QAPIGen(object): f = open(fd, 'r+', encoding='utf-8') else: f = os.fdopen(fd, 'r+') - text = (self._top(fname) + self._preamble + self._body - + self._bottom(fname)) + text = self.get_content(fname) oldtext = f.read(len(text) + 1) if text != oldtext: f.seek(0) @@ -2029,10 +2139,62 @@ class QAPIGen(object): f.close() -class QAPIGenC(QAPIGen): +@contextmanager +def ifcontext(ifcond, *args): + """A 'with' statement context manager to wrap with start_if()/end_if() - def __init__(self, blurb, pydoc): + *args: any number of QAPIGenCCode + + Example:: + + with ifcontext(ifcond, self._genh, self._genc): + modify self._genh and self._genc ... + + Is equivalent to calling:: + + self._genh.start_if(ifcond) + self._genc.start_if(ifcond) + modify self._genh and self._genc ... + self._genh.end_if() + self._genc.end_if() + """ + for arg in args: + arg.start_if(ifcond) + yield + for arg in args: + arg.end_if() + + +class QAPIGenCCode(QAPIGen): + + def __init__(self): QAPIGen.__init__(self) + self._start_if = None + + def start_if(self, ifcond): + assert self._start_if is None + self._start_if = (ifcond, self._body, self._preamble) + + def end_if(self): + assert self._start_if + self._wrap_ifcond() + self._start_if = None + + def _wrap_ifcond(self): + self._body = _wrap_ifcond(self._start_if[0], + self._start_if[1], self._body) + self._preamble = _wrap_ifcond(self._start_if[0], + self._start_if[2], self._preamble) + + def get_content(self, fname=None): + assert self._start_if is None + return QAPIGen.get_content(self, fname) + + +class QAPIGenC(QAPIGenCCode): + + def __init__(self, blurb, pydoc): + QAPIGenCCode.__init__(self) self._blurb = blurb self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, re.MULTILINE)) diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index b5630844f9..987fd3c943 100644..100755 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -174,7 +174,7 @@ def texi_members(doc, what, base, variants, member_func): return '\n@b{%s:}\n@table @asis\n%s@end table\n' % (what, items) -def texi_sections(doc): +def texi_sections(doc, ifcond): """Format additional sections following arguments""" body = '' for section in doc.sections: @@ -185,14 +185,16 @@ def texi_sections(doc): body += texi_example(section.text) else: body += texi_format(section.text) + if ifcond: + body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond) return body -def texi_entity(doc, what, base=None, variants=None, +def texi_entity(doc, what, ifcond, base=None, variants=None, member_func=texi_member): return (texi_body(doc) + texi_members(doc, what, base, variants, member_func) - + texi_sections(doc)) + + texi_sections(doc, ifcond)) class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): @@ -204,47 +206,47 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): def write(self, output_dir): self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') - def visit_enum_type(self, name, info, values, prefix): + def visit_enum_type(self, name, info, ifcond, values, prefix): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Enum', name=doc.symbol, - body=texi_entity(doc, 'Values', + body=texi_entity(doc, 'Values', ifcond, member_func=texi_enum_value))) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): doc = self.cur_doc if base and base.is_implicit(): base = None self._gen.add(TYPE_FMT(type='Object', name=doc.symbol, - body=texi_entity(doc, 'Members', + body=texi_entity(doc, 'Members', ifcond, base, variants))) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Alternate', name=doc.symbol, - body=texi_entity(doc, 'Members'))) + body=texi_entity(doc, 'Members', ifcond))) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): doc = self.cur_doc if boxed: body = texi_body(doc) body += ('\n@b{Arguments:} the members of @code{%s}\n' % arg_type.name) - body += texi_sections(doc) + body += texi_sections(doc, ifcond) else: - body = texi_entity(doc, 'Arguments') + body = texi_entity(doc, 'Arguments', ifcond) self._gen.add(MSG_FMT(type='Command', name=doc.symbol, body=body)) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): doc = self.cur_doc self._gen.add(MSG_FMT(type='Event', name=doc.symbol, - body=texi_entity(doc, 'Arguments'))) + body=texi_entity(doc, 'Arguments', ifcond))) def symbol(self, doc, entity): if self._gen._body: @@ -257,7 +259,7 @@ class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): assert not doc.args if self._gen._body: self._gen.add('\n') - self._gen.add(texi_body(doc) + texi_sections(doc)) + self._gen.add(texi_body(doc) + texi_sections(doc, None)) def gen_doc(schema, output_dir, prefix): diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 5657524688..764ef177ab 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -184,9 +184,11 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): 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, arg_type, boxed): - self._genh.add(gen_event_send_decl(name, arg_type, boxed)) - self._genc.add(gen_event_send(name, arg_type, boxed, self._enum_name)) + 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._event_names.append(name) diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 6ad198ae5b..71d4a779ce 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -18,6 +18,15 @@ def to_qlit(obj, level=0, suppress_first_indent=False): def indent(level): return level * 4 * ' ' + if isinstance(obj, tuple): + ifobj, ifcond = obj + ret = gen_if(ifcond) + ret += to_qlit(ifobj, level) + endif = gen_endif(ifcond) + if endif: + ret += '\n' + endif + return ret + ret = '' if not suppress_first_indent: ret += indent(level) @@ -26,11 +35,11 @@ def to_qlit(obj, level=0, suppress_first_indent=False): elif isinstance(obj, str): ret += 'QLIT_QSTR(' + to_c_string(obj) + ')' elif isinstance(obj, list): - elts = [to_qlit(elt, level + 1) + elts = [to_qlit(elt, level + 1).strip('\n') for elt in obj] elts.append(indent(level + 1) + "{}") ret += 'QLIT_QLIST(((QLitObject[]) {\n' - ret += ',\n'.join(elts) + '\n' + ret += '\n'.join(elts) + '\n' ret += indent(level) + '}))' elif isinstance(obj, dict): elts = [] @@ -45,6 +54,8 @@ def to_qlit(obj, level=0, suppress_first_indent=False): ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false') else: assert False # not implemented + if level > 0: + ret += ',' return ret @@ -126,12 +137,12 @@ const QLitObject %(c_name)s = %(c_string)s; return '[' + self._use_type(typ.element_type) + ']' return self._name(typ.name) - def _gen_qlit(self, name, mtype, obj): + def _gen_qlit(self, name, mtype, obj, ifcond): if mtype not in ('command', 'event', 'builtin', 'array'): name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._qlits.append(obj) + self._qlits.append((obj, ifcond)) def _gen_member(self, member): ret = {'name': member.name, 'type': self._use_type(member.type)} @@ -147,28 +158,29 @@ const QLitObject %(c_name)s = %(c_string)s; return {'case': variant.name, 'type': self._use_type(variant.type)} def visit_builtin_type(self, name, info, json_type): - self._gen_qlit(name, 'builtin', {'json-type': json_type}) + self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) - def visit_enum_type(self, name, info, values, prefix): - self._gen_qlit(name, 'enum', {'values': values}) + def visit_enum_type(self, name, info, ifcond, values, prefix): + self._gen_qlit(name, 'enum', {'values': values}, ifcond) - def visit_array_type(self, name, info, element_type): + def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) - self._gen_qlit('[' + element + ']', 'array', {'element-type': element}) + self._gen_qlit('[' + element + ']', 'array', {'element-type': element}, + ifcond) - def visit_object_type_flat(self, name, info, members, variants): + def visit_object_type_flat(self, name, info, ifcond, members, variants): obj = {'members': [self._gen_member(m) for m in members]} if variants: obj.update(self._gen_variants(variants.tag_member.name, variants.variants)) - self._gen_qlit(name, 'object', obj) + self._gen_qlit(name, 'object', obj, ifcond) - def visit_alternate_type(self, name, info, variants): + def visit_alternate_type(self, name, info, ifcond, variants): self._gen_qlit(name, 'alternate', {'members': [{'type': self._use_type(m.type)} - for m in variants.variants]}) + for m in variants.variants]}, ifcond) - def visit_command(self, name, info, arg_type, ret_type, gen, + def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type @@ -176,11 +188,12 @@ const QLitObject %(c_name)s = %(c_string)s; {'arg-type': self._use_type(arg_type), 'ret-type': self._use_type(ret_type), 'allow-oob': allow_oob, - 'allow-preconfig': allow_preconfig}) + 'allow-preconfig': allow_preconfig}, ifcond) - def visit_event(self, name, info, arg_type, boxed): + def visit_event(self, name, info, ifcond, arg_type, boxed): arg_type = arg_type or self._schema.the_empty_object_type - self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}) + self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)}, + ifcond) def gen_introspect(schema, output_dir, prefix, opt_unmask): diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index a599352e59..fd7808103c 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -55,7 +55,7 @@ def gen_struct_members(members): return ret -def gen_object(name, base, members, variants): +def gen_object(name, ifcond, base, members, variants): if name in objects_seen: return '' objects_seen.add(name) @@ -64,11 +64,14 @@ def gen_object(name, base, members, variants): if variants: for v in variants.variants: if isinstance(v.type, QAPISchemaObjectType): - ret += gen_object(v.type.name, v.type.base, + ret += gen_object(v.type.name, v.type.ifcond, v.type.base, v.type.local_members, v.type.variants) ret += mcgen(''' +''') + ret += gen_if(ifcond) + ret += mcgen(''' struct %(c_name)s { ''', c_name=c_name(name)) @@ -101,6 +104,7 @@ struct %(c_name)s { ret += mcgen(''' }; ''') + ret += gen_endif(ifcond) return ret @@ -208,34 +212,40 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, values, prefix): - self._genh.preamble_add(gen_enum(name, values, prefix)) - self._genc.add(gen_enum_lookup(name, values, prefix)) + def visit_enum_type(self, name, info, ifcond, values, prefix): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_enum(name, values, prefix)) + self._genc.add(gen_enum_lookup(name, values, prefix)) - def visit_array_type(self, name, info, element_type): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_array(name, element_type)) - self._gen_type_cleanup(name) + def visit_array_type(self, name, info, ifcond, element_type): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_array(name, element_type)) + self._gen_type_cleanup(name) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, base, members, variants)) - if base and not base.is_implicit(): - self._genh.add(gen_upcast(name, base)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # implicit types won't be directly allocated/freed - self._gen_type_cleanup(name) - - def visit_alternate_type(self, name, info, variants): - self._genh.preamble_add(gen_fwd_object_or_array(name)) - self._genh.add(gen_object(name, None, + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, base, members, variants)) + with ifcontext(ifcond, self._genh, self._genc): + if base and not base.is_implicit(): + self._genh.add(gen_upcast(name, base)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # implicit types won't be directly allocated/freed + self._gen_type_cleanup(name) + + def visit_alternate_type(self, name, info, ifcond, variants): + with ifcontext(ifcond, self._genh): + self._genh.preamble_add(gen_fwd_object_or_array(name)) + self._genh.add(gen_object(name, ifcond, None, [variants.tag_member], variants)) - self._gen_type_cleanup(name) + with ifcontext(ifcond, self._genh, self._genc): + self._gen_type_cleanup(name) def gen_types(schema, output_dir, prefix, opt_builtins): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index bdcafb64ee..dd5034a66a 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -310,30 +310,35 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, values, prefix): - self._genh.add(gen_visit_decl(name, scalar=True)) - self._genc.add(gen_visit_enum(name)) + def visit_enum_type(self, name, info, ifcond, values, prefix): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name, scalar=True)) + self._genc.add(gen_visit_enum(name)) - def visit_array_type(self, name, info, element_type): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_list(name, element_type)) + def visit_array_type(self, name, info, ifcond, element_type): + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_list(name, element_type)) - def visit_object_type(self, name, info, base, members, variants): + def visit_object_type(self, name, info, ifcond, base, members, variants): # Nothing to do for the special empty builtin if name == 'q_empty': return - self._genh.add(gen_visit_members_decl(name)) - self._genc.add(gen_visit_object_members(name, base, members, variants)) - # TODO Worth changing the visitor signature, so we could - # directly use rather than repeat type.is_implicit()? - if not name.startswith('q_'): - # only explicit types need an allocating visit + with ifcontext(ifcond, self._genh, self._genc): + self._genh.add(gen_visit_members_decl(name)) + self._genc.add(gen_visit_object_members(name, base, + members, variants)) + # TODO Worth changing the visitor signature, so we could + # directly use rather than repeat type.is_implicit()? + if not name.startswith('q_'): + # only explicit types need an allocating visit + self._genh.add(gen_visit_decl(name)) + self._genc.add(gen_visit_object(name, base, members, variants)) + + def visit_alternate_type(self, name, info, ifcond, variants): + with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_object(name, base, members, variants)) - - def visit_alternate_type(self, name, info, variants): - self._genh.add(gen_visit_decl(name)) - self._genc.add(gen_visit_alternate(name, variants)) + self._genc.add(gen_visit_alternate(name, variants)) def gen_visit(schema, output_dir, prefix, opt_builtins): |