diff options
author | Markus Armbruster <armbru@redhat.com> | 2018-02-26 13:48:58 -0600 |
---|---|---|
committer | Eric Blake <eblake@redhat.com> | 2018-03-02 13:14:09 -0600 |
commit | fb0bc835e56b894cbc7236294921e5393c786ad8 (patch) | |
tree | c96c6626054c20084fc9fe268fab187c0bed20bf /scripts/qapi-introspect.py | |
parent | 26df4e7fab06422b21e11d039c64243ca4003147 (diff) |
qapi-gen: New common driver for code and doc generators
Whenever qapi-schema.json changes, we run six programs eleven times to
update eleven files. Similar for qga/qapi-schema.json. This is
silly. Replace the six programs by a single program that spits out
all eleven files.
The programs become modules in new Python package qapi, along with the
helper library. This requires moving them to scripts/qapi/. While
moving them, consistently drop executable mode bits.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20180211093607.27351-9-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
[eblake: move change to one-line 'blurb' earlier in series, mention mode
bit change as intentional, update qapi-code-gen.txt to match actual
generated events.c file]
Signed-off-by: Eric Blake <eblake@redhat.com>
Diffstat (limited to 'scripts/qapi-introspect.py')
-rw-r--r-- | scripts/qapi-introspect.py | 208 |
1 files changed, 0 insertions, 208 deletions
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py deleted file mode 100644 index cac219b4d8..0000000000 --- a/scripts/qapi-introspect.py +++ /dev/null @@ -1,208 +0,0 @@ -""" -QAPI introspection generator - -Copyright (C) 2015-2018 Red Hat, Inc. - -Authors: - Markus Armbruster <armbru@redhat.com> - -This work is licensed under the terms of the GNU GPL, version 2. -See the COPYING file in the top-level directory. -""" - -from qapi import * - - -# Caveman's json.dumps() replacement (we're stuck at Python 2.4) -# TODO try to use json.dumps() once we get unstuck -def to_json(obj, level=0): - if obj is None: - ret = 'null' - elif isinstance(obj, str): - ret = '"' + obj.replace('"', r'\"') + '"' - elif isinstance(obj, list): - elts = [to_json(elt, level + 1) - for elt in obj] - ret = '[' + ', '.join(elts) + ']' - elif isinstance(obj, dict): - elts = ['"%s": %s' % (key.replace('"', r'\"'), - to_json(obj[key], level + 1)) - for key in sorted(obj.keys())] - ret = '{' + ', '.join(elts) + '}' - else: - assert False # not implemented - if level == 1: - ret = '\n' + ret - return ret - - -def to_c_string(string): - return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"' - - -class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor): - def __init__(self, prefix, unmask): - self._prefix = prefix - self._unmask = unmask - self.defn = None - self.decl = None - self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None - - def visit_begin(self, schema): - self._schema = schema - self._jsons = [] - self._used_types = [] - self._name_map = {} - - def visit_end(self): - # visit the types that are actually used - jsons = self._jsons - self._jsons = [] - for typ in self._used_types: - typ.visit(self) - # generate C - # TODO can generate awfully long lines - jsons.extend(self._jsons) - name = c_name(self._prefix, protect=False) + 'qmp_schema_json' - self.decl = mcgen(''' -extern const char %(c_name)s[]; -''', - c_name=c_name(name)) - lines = to_json(jsons).split('\n') - c_string = '\n '.join([to_c_string(line) for line in lines]) - self.defn = mcgen(''' -const char %(c_name)s[] = %(c_string)s; -''', - c_name=c_name(name), - c_string=c_string) - self._schema = None - self._jsons = None - self._used_types = None - self._name_map = None - - def visit_needed(self, entity): - # Ignore types on first pass; visit_end() will pick up used types - return not isinstance(entity, QAPISchemaType) - - def _name(self, name): - if self._unmask: - return name - if name not in self._name_map: - self._name_map[name] = '%d' % len(self._name_map) - return self._name_map[name] - - def _use_type(self, typ): - # Map the various integer types to plain int - if typ.json_type() == 'int': - typ = self._schema.lookup_type('int') - elif (isinstance(typ, QAPISchemaArrayType) and - typ.element_type.json_type() == 'int'): - typ = self._schema.lookup_type('intList') - # Add type to work queue if new - if typ not in self._used_types: - self._used_types.append(typ) - # Clients should examine commands and events, not types. Hide - # type names to reduce the temptation. Also saves a few - # characters. - if isinstance(typ, QAPISchemaBuiltinType): - return typ.name - if isinstance(typ, QAPISchemaArrayType): - return '[' + self._use_type(typ.element_type) + ']' - return self._name(typ.name) - - def _gen_json(self, name, mtype, obj): - if mtype not in ('command', 'event', 'builtin', 'array'): - name = self._name(name) - obj['name'] = name - obj['meta-type'] = mtype - self._jsons.append(obj) - - def _gen_member(self, member): - ret = {'name': member.name, 'type': self._use_type(member.type)} - if member.optional: - ret['default'] = None - return ret - - def _gen_variants(self, tag_name, variants): - return {'tag': tag_name, - 'variants': [self._gen_variant(v) for v in variants]} - - def _gen_variant(self, variant): - return {'case': variant.name, 'type': self._use_type(variant.type)} - - def visit_builtin_type(self, name, info, json_type): - self._gen_json(name, 'builtin', {'json-type': json_type}) - - def visit_enum_type(self, name, info, values, prefix): - self._gen_json(name, 'enum', {'values': values}) - - def visit_array_type(self, name, info, element_type): - element = self._use_type(element_type) - self._gen_json('[' + element + ']', 'array', {'element-type': element}) - - def visit_object_type_flat(self, name, info, 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_json(name, 'object', obj) - - def visit_alternate_type(self, name, info, variants): - self._gen_json(name, 'alternate', - {'members': [{'type': self._use_type(m.type)} - for m in variants.variants]}) - - def visit_command(self, name, info, arg_type, ret_type, - gen, success_response, boxed): - arg_type = arg_type or self._schema.the_empty_object_type - ret_type = ret_type or self._schema.the_empty_object_type - self._gen_json(name, 'command', - {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type)}) - - def visit_event(self, name, info, arg_type, boxed): - arg_type = arg_type or self._schema.the_empty_object_type - self._gen_json(name, 'event', {'arg-type': self._use_type(arg_type)}) - - -def main(argv): - # Debugging aid: unmask QAPI schema's type names - # We normally mask them, because they're not QMP wire ABI - opt_unmask = False - - (input_file, output_dir, do_c, do_h, prefix, opts) = \ - parse_command_line('u', ['unmask-non-abi-names']) - - for o, a in opts: - if o in ('-u', '--unmask-non-abi-names'): - opt_unmask = True - - blurb = ' * QAPI/QMP schema introspection' - - genc = QAPIGenC(blurb, __doc__) - genh = QAPIGenH(blurb, __doc__) - - genc.add(mcgen(''' -#include "qemu/osdep.h" -#include "%(prefix)sqmp-introspect.h" - -''', - prefix=prefix)) - - schema = QAPISchema(input_file) - vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask) - schema.visit(vis) - genc.add(vis.defn) - genh.add(vis.decl) - - if do_c: - genc.write(output_dir, prefix + 'qmp-introspect.c') - if do_h: - genh.write(output_dir, prefix + 'qmp-introspect.h') - - -if __name__ == '__main__': - main(sys.argv) |