qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH RFC 19/19] qapi: New QMP command query-schema for QM


From: Markus Armbruster
Subject: [Qemu-devel] [PATCH RFC 19/19] qapi: New QMP command query-schema for QMP schema introspection
Date: Thu, 2 Apr 2015 19:29:03 +0200

Caution, rough edges.

qapi/introspect.json defines the introspection schema.  It should do
for uses other than QMP.
FIXME it's almost entirely devoid of comments.

The introspection schema does not reflect all the rules and
restrictions that apply to QAPI schemata.  A valid QAPI schema has an
introspection value conforming to the introspection schema, but the
converse is not true.

Introspection lowers away a number of schema details:

* The builtin types are declared with their JSON type.

  TODO should we map all the integer types to just int?

* Implicit type definitions are made explicit, and given
  auto-generated names.  These names start with ':' so they don't
  clash with the user's names.

  Example: a simple union implicitly defines an enumeration type for
  its discriminator.

* All type references are by name.

* Base types are flattened.

* The top type (named '**') can hold any value.

  It can currently occur only in commands, but the introspection
  schema doesn't reflect that.

* The struct and union types are generalized into an object type.

* Commands take a single argument and return a single result.

  Dictionary argument/result or list result is an implicit type
  definition.

  Missing argument/result is an implicit definition of the empty
  object.

  The argument is always of object type, but the introspection schema
  doesn't reflect that.

  The 'gen': false directive is omitted as implementation detail.

* Events carry a single data value.

  Implicit type definition as above.

  The value is of object type, but the introspection schema doesn't
  reflect that.

* Types not used by commands or events are omitted.

  Indirect use counts as use.

* Optional members have a default, which can only be null right now

  Instead of a mandatory "optional" flag, we have an optionial
  default.  No default means mandatory, default null means optional
  without default value.  Non-null is available for optional with
  default.

TODO much of the above should go into docs.

New generator scripts/qapi-introspect.py computes an introspection
value for its input, and generates a C variable holding it.
Pythonistas may find it ugly and/or clumsy.

FIXME it can generate awfully long lines

The schema generation proper is pretty trivial.  Much of the
qapi-introspect.py's code actually does something else: convert the
output of parse_schema() into a more usable data structure, lowering
away uninteresting stuff.  This should really be done in qapi.py, so
the other generators can use it, too.

For testing, I feed it tests/qapi-schema/qapi-schema-test.json, but no
more.

New QMP command query-schema parses its return value from that
variable.  Its reply is less than 75KiBytes right now.

A compile time test that the value can be parsed would be nice.

Signed-off-by: Markus Armbruster <address@hidden>
---
 .gitignore                 |   1 +
 Makefile                   |   9 +-
 Makefile.objs              |   1 +
 monitor.c                  |   8 +
 qapi-schema.json           |   3 +
 qapi/introspect.json       |  72 ++++++++
 qmp-commands.hx            |  16 ++
 scripts/qapi-introspect.py | 430 +++++++++++++++++++++++++++++++++++++++++++++
 tests/.gitignore           |   1 +
 tests/Makefile             |   8 +-
 10 files changed, 547 insertions(+), 2 deletions(-)
 create mode 100644 qapi/introspect.json
 create mode 100644 scripts/qapi-introspect.py

diff --git a/.gitignore b/.gitignore
index e32a584..91569ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,6 +32,7 @@
 /qapi-visit.[ch]
 /qapi-event.[ch]
 /qmp-commands.h
+/qmp-introspect.[ch]
 /qmp-marshal.c
 /qemu-doc.html
 /qemu-tech.html
diff --git a/Makefile b/Makefile
index 1e26f4d..cda999e 100644
--- a/Makefile
+++ b/Makefile
@@ -47,6 +47,8 @@ endif
 GENERATED_HEADERS = config-host.h qemu-options.def
 GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-event.h
 GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c qapi-event.c
+GENERATED_HEADERS += qmp-introspect.h
+GENERATED_SOURCES += qmp-introspect.c
 
 GENERATED_HEADERS += trace/generated-events.h
 GENERATED_SOURCES += trace/generated-events.c
@@ -258,7 +260,7 @@ $(SRC_PATH)/qga/qapi-schema.json 
$(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
 
 qapi-modules = $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/qapi/common.json \
                $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \
-               $(SRC_PATH)/qapi/event.json
+               $(SRC_PATH)/qapi/event.json $(SRC_PATH)/qapi/introspect.json
 
 qapi-types.c qapi-types.h :\
 $(qapi-modules) $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@@ -280,6 +282,11 @@ $(qapi-modules) $(SRC_PATH)/scripts/qapi-commands.py 
$(qapi-py)
        $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py \
                $(gen-out-type) -o "." -m $<, \
                "  GEN   $@")
+qmp-introspect.h qmp-introspect.c :\
+$(qapi-modules) $(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+       $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
+               $(gen-out-type) -o "." $<, \
+               "  GEN   $@")
 
 QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h 
qga-qmp-commands.h)
 $(qga-obj-y) qemu-ga.o: $(QGALIB_GEN)
diff --git a/Makefile.objs b/Makefile.objs
index 28999d3..414229f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -80,6 +80,7 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y)
 # qapi
 
 common-obj-y += qmp-marshal.o
+common-obj-y += qmp-introspect.o
 common-obj-y += qmp.o hmp.o
 endif
 
diff --git a/monitor.c b/monitor.c
index 68873ec..fcb9242 100644
--- a/monitor.c
+++ b/monitor.c
@@ -73,6 +73,7 @@
 #include "block/qapi.h"
 #include "qapi/qmp-event.h"
 #include "qapi-event.h"
+#include "qmp-introspect.h"
 #include "sysemu/block-backend.h"
 
 /* for hmp_info_irq/pic */
@@ -997,6 +998,13 @@ EventInfoList *qmp_query_events(Error **errp)
     return ev_list;
 }
 
+static int qmp_query_schema(Monitor *mon, const QDict *qdict,
+                            QObject **ret_data)
+{
+    *ret_data = qobject_from_json(qmp_schema_json);
+    return 0;
+}
+
 /* set the current CPU defined by the user */
 int monitor_set_cpu(int cpu_index)
 {
diff --git a/qapi-schema.json b/qapi-schema.json
index 6b280b7..0efeb80 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -14,6 +14,9 @@
 # Tracing commands
 { 'include': 'qapi/trace.json' }
 
+# QAPI introspection
+{ 'include': 'qapi/introspect.json' }
+
 ##
 # LostTickPolicy:
 #
diff --git a/qapi/introspect.json b/qapi/introspect.json
new file mode 100644
index 0000000..87de856
--- /dev/null
+++ b/qapi/introspect.json
@@ -0,0 +1,72 @@
+# -*- Mode: Python -*-
+#
+# QAPI introspection
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Authors:
+#  Markus Armbruster <address@hidden>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or later.
+# See the COPYING file in the top-level directory.
+
+{ 'enum': 'SchemaMetaType',
+  'data': [ 'builtin', 'enum', 'array', 'object', 'alternate',
+            'command', 'event' ] }
+
+{ 'type': 'SchemaInfoBase',
+  'data': { 'name': 'str', 'meta-type': 'SchemaMetaType' } }
+
+{ 'enum': 'JSONPrimitiveType',
+  'data': [ 'str', 'int', 'number', 'bool', 'null' ] }
+
+{ 'type': 'SchemaInfoBuiltin',
+  'data': { 'json-type': 'JSONPrimitiveType' } }
+
+{ 'type': 'SchemaInfoEnum',
+  'data': { 'values': ['str'] } }
+
+{ 'type': 'SchemaInfoArray',
+  'data': { 'element-type': 'str' } }
+
+{ 'alternate': 'Value',
+  'data': { 'int': 'int', 'number': 'number', 'str': 'str', 'bool': 'bool' } }
+
+{ 'type': 'SchemaInfoObjectMember',
+  'data': { 'name': 'str', 'type': 'str', '*default': 'Value' } }
+# @default's type must match @type
+# can only default simple types, not objects or arrays
+
+{ 'type': 'SchemaInfoObjectVariant',
+  'data': { 'case': 'str',
+            'members': [ 'SchemaInfoObjectMember' ] } }
+
+{ 'type': 'SchemaInfoObject',
+  'data': { 'members': [ 'SchemaInfoObjectMember' ],
+            '*discriminator': 'str',
+            '*variants': [ 'SchemaInfoObjectVariant' ] } }
+
+{ 'type': 'SchemaInfoAlternate',
+  'data': { 'members': [ 'SchemaInfoObjectMember' ] } }
+
+{ 'type': 'SchemaInfoCommand',
+  'data': { 'args': 'str', 'returns': 'str' } }
+
+{ 'type': 'SchemaInfoEvent',
+  'data': { 'data': 'str' } }
+
+{ 'union': 'SchemaInfo',
+  'base': 'SchemaInfoBase',
+  'discriminator': 'meta-type',
+  'data': {
+      'builtin': 'SchemaInfoBuiltin',
+      'enum': 'SchemaInfoEnum',
+      'array': 'SchemaInfoArray',
+      'object': 'SchemaInfoObject',
+      'alternate': 'SchemaInfoAlternate',
+      'command': 'SchemaInfoCommand',
+      'event': 'SchemaInfoEvent' } }
+
+{ 'command': 'query-schema',
+  'returns': [ 'SchemaInfo' ],
+  'gen': false }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index 7f68760..27ab209 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -2039,6 +2039,22 @@ EQMP
     },
 
 SQMP
+query-schema
+------------
+
+Return the QMP schema.
+
+FIXME explain
+
+EQMP
+
+    {
+        .name       = "query-schema",
+        .args_type  = "",
+        .mhandler.cmd_new = qmp_query_schema,
+    },
+
+SQMP
 query-chardev
 -------------
 
diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
new file mode 100644
index 0000000..c09276b
--- /dev/null
+++ b/scripts/qapi-introspect.py
@@ -0,0 +1,430 @@
+#
+# QAPI introspection generator
+#
+# Copyright (C) 2015 Red Hat, Inc.
+#
+# Authors:
+#  Markus Armbruster <address@hidden>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+from ordereddict import OrderedDict
+from qapi import *
+import getopt
+import errno
+
+class QAPISchemaEntity:
+    def __init__(self, name, info):
+        assert isinstance(name, str)
+        self.name = name
+        self.info = info
+    def check(self):
+        pass
+    def collect(self, seen):
+        seen.add(self.name)
+
+entity_dict = {}
+
+def def_entity(ent):
+    assert ent.name not in entity_dict
+    entity_dict[ent.name] = ent
+
+def gen_json_helper(name, mtype, extra):
+    if extra:
+        extra = ", " + extra
+    else:
+        extra = ""
+    return "{ 'name': '%s', 'meta-type': '%s'%s }" % (name, mtype, extra)
+
+class QAPISchemaType(QAPISchemaEntity):
+    pass
+
+def get_type(name):
+    if isinstance(entity_dict.get(name), QAPISchemaType):
+        return entity_dict[name]
+    return None
+
+def check_schema():
+    for ent in entity_dict.values():
+        ent.check()
+
+class QAPISchemaBuiltinType(QAPISchemaType):
+    def __init__(self, name, json_type):
+        QAPISchemaType.__init__(self, name, None)
+        assert json_type in ('string', 'number', 'int', 'boolean', 'null', 
'value')
+        self.json_type = json_type
+    def gen_json(self):
+        return gen_json_helper(self.name, 'builtin',
+                               "'json-type': '%s'" % self.json_type)
+
+class QAPISchemaEnumType(QAPISchemaType):
+    def __init__(self, name, info, values):
+        QAPISchemaType.__init__(self, name, info)
+        for v in values:
+            assert isinstance(v, str)
+        self.values = values
+    def gen_json(self):
+        return gen_json_helper(self.name, 'enum',
+                               "'values': [ %s ]" % ", ".join(map(lambda v: 
"'%s'" % v, self.values)))
+
+def make_implicit_enum_type(name, role, values):
+    name = ':enum-%s-%s' % (name, role)
+    def_entity(QAPISchemaEnumType(name, None, values))
+    return name
+
+class QAPISchemaArrayType(QAPISchemaType):
+    def __init__(self, name, info, element_type):
+        QAPISchemaType.__init__(self, name, info)
+        assert isinstance(element_type, str)
+        self.element_type = element_type
+    def check(self):
+        assert get_type(self.element_type)
+    def collect(self, seen):
+        seen.add(self.name)
+        get_type(self.element_type).collect(seen)
+    def gen_json(self):
+        return gen_json_helper(self.name, 'array',
+                               "'element-type': '%s'" % self.element_type)
+
+def make_array_type(element_type):
+    name = ':[%s]' % element_type
+    if not get_type(name):
+        def_entity(QAPISchemaArrayType(name, None, element_type))
+    return name
+
+class QAPISchemaObjectType(QAPISchemaType):
+    def __init__(self, name, info, base, local_members, variants):
+        QAPISchemaType.__init__(self, name, info)
+        assert base == None or isinstance(base, str)
+        assert isinstance(local_members, QAPISchemaObjectTypeMembers)
+        if variants != None:
+            assert isinstance(variants, QAPISchemaObjectTypeVariants)
+        self.base = base
+        self.local_members = local_members
+        self.variants = variants
+        self.members = None
+    def check(self):
+        if self.members:
+            return                      # already checked
+        assert self.members == None     # not running in cycles
+        self.members = False            # mark as being checked
+        if self.base:
+            base = get_type(self.base)
+            base.check()
+            members = list(base.members.members)
+        else:
+            base = None
+            members = []
+        seen = {}
+        for m in members:
+            seen[m.name] = m
+        self.local_members.check(members, seen)
+        if self.variants:
+            self.variants.check(seen)
+        self.members = QAPISchemaObjectTypeMembers(members)
+    def collect(self, seen):
+        if self.name in seen:
+            return
+        seen.add(self.name)
+        self.members.collect(seen)
+        if self.variants:
+            self.variants.collect(seen)
+    def gen_json(self):
+        extra = "'members': %s" % self.members.gen_json()
+        if self.variants:
+            extra += ", %s" % self.variants.gen_json()
+        return gen_json_helper(self.name, 'object', extra)
+
+def make_implicit_object_type(name, role, members):
+    if members.members == []:
+        name = ':empty'
+        if get_type(name):
+            return name
+    else:
+        name = ':obj-%s-%s' % (name, role)
+    def_entity(QAPISchemaObjectType(name, None, None, members, None))
+    return name
+
+class QAPISchemaObjectTypeMember:
+    def __init__(self, name, typ, optional):
+        assert isinstance(name, str)
+        assert isinstance(typ, str)
+        assert isinstance(optional, bool)
+        self.name = name
+        self.type = typ
+        self.optional = optional
+    def check(self, seen):
+        assert self.name not in seen
+        assert get_type(self.type)
+    def collect(self, seen):
+        get_type(self.type).collect(seen)
+    def gen_json(self):
+        default = ''
+        if self.optional:
+            default = ", 'default': null"
+        return "{ 'name': '%s', 'type': '%s'%s }" % (self.name, self.type, 
default)
+
+class QAPISchemaObjectTypeMembers:
+    def __init__(self, members):
+        for m in members:
+            assert isinstance(m, QAPISchemaObjectTypeMember)
+        self.members = members
+    def check(self, all_members, seen):
+        for m in self.members:
+            m.check(seen)
+            all_members.append(m)
+            seen[m.name] = m
+    def collect(self, seen):
+        for m in self.members:
+            m.collect(seen)
+    def gen_json(self):
+        return "[ " + ", ".join(map(lambda m: m.gen_json(), self.members)) + " 
]"
+
+class QAPISchemaObjectTypeVariant:
+    def __init__(self, case, members):
+        assert isinstance(case, str)
+        if not isinstance(members, str):
+            assert isinstance(members, QAPISchemaObjectTypeMembers)
+        self.case = case
+        self.members = members
+    def check(self, disc_type, seen):
+        assert self.case in disc_type.values
+        if isinstance(self.members, str):
+            get_type(self.members).check()
+            self.members = get_type(self.members).members
+        self.members.check([], seen)
+    def collect(self, seen):
+        self.members.collect(seen)
+    def gen_json(self):
+        return "{ 'case': '%s', 'members': %s }" % (self.case, 
self.members.gen_json())
+
+class QAPISchemaObjectTypeVariants:
+    def __init__(self, discriminator, variants):
+        assert isinstance(discriminator, str)
+        for v in variants:
+            assert isinstance(v, QAPISchemaObjectTypeVariant)
+        self.discriminator = discriminator
+        self.variants = variants
+    def check(self, seen):
+        disc_type = get_type(seen[self.discriminator].type)
+        assert isinstance(disc_type, QAPISchemaEnumType)
+        for v in self.variants:
+            vseen = dict(seen)
+            v.check(disc_type, vseen)
+    def collect(self, seen):
+        for v in self.variants:
+            v.collect(seen)
+    def gen_json(self):
+        return "'discriminator': '%s', 'variants': [ %s ]" % 
(self.discriminator, ", ".join(map(lambda v: v.gen_json(), self.variants)))
+
+class QAPISchemaAlternateType(QAPISchemaType):
+    def __init__(self, name, info, members):
+        QAPISchemaType.__init__(self, name, info)
+        assert isinstance(members, QAPISchemaObjectTypeMembers)
+        self.members = members
+    def check(self):
+        self.members.check([], {})
+    def collect(self, seen):
+        if self.name in seen:
+            return
+        seen.add(self.name)
+        self.members.collect(seen)
+    def gen_json(self):
+        return gen_json_helper(self.name, 'alternate',
+                               "'members': %s" % self.members.gen_json())
+
+class QAPISchemaCommand(QAPISchemaEntity):
+    def __init__(self, name, info, args, rets):
+        QAPISchemaEntity.__init__(self, name, info)
+        if not isinstance(args, str):
+            assert isinstance(args, QAPISchemaObjectTypeMembers)
+        if not isinstance(rets, str):
+            assert isinstance(rets, QAPISchemaObjectTypeMembers)
+        self.args = args
+        self.rets = rets
+    def check(self):
+        if not isinstance(self.args, str):
+            self.args = make_implicit_object_type(self.name, 'args', self.args)
+            entity_dict[self.args].check()
+        if not isinstance(self.rets, str):
+            self.rets = make_implicit_object_type(self.name, 'rets', self.rets)
+            entity_dict[self.rets].check()
+    def collect(self, seen):
+        seen.add(self.name)
+        get_type(self.args).collect(seen)
+        get_type(self.rets).collect(seen)
+    def gen_json(self):
+        return gen_json_helper(self.name, 'command',
+                               "'args': '%s', 'returns': '%s'" % (self.args, 
self.rets))
+
+class QAPISchemaEvent(QAPISchemaEntity):
+    def __init__(self, name, info, data):
+        QAPISchemaEntity.__init__(self, name, info)
+        if not isinstance(data, str):
+            assert isinstance(data, QAPISchemaObjectTypeMembers)
+        self.data = data
+    def check(self):
+        if not isinstance(self.data, str):
+            self.data = make_implicit_object_type(self.name, 'data', self.data)
+            entity_dict[self.data].check()
+    def collect(self, seen):
+        seen.add(self.name)
+        get_type(self.data).collect(seen)
+    def gen_json(self):
+        return gen_json_helper(self.name, 'event', "'data': '%s'" % self.data)
+
+def def_builtin_types():
+    def_entity(QAPISchemaBuiltinType('str', 'string'))
+    def_entity(QAPISchemaBuiltinType('number', 'number'))
+    for i in ('int', 'int8', 'int16', 'int32', 'int64',
+              'uint8', 'uint16', 'uint32', 'uint64', 'size'):
+        def_entity(QAPISchemaBuiltinType(i, 'int'))
+    def_entity(QAPISchemaBuiltinType('bool', 'boolean'))
+    def_entity(QAPISchemaBuiltinType('**', 'value'))
+
+def def_enum_type(expr):
+    def_entity(QAPISchemaEnumType(expr['enum'], None, expr['data']))
+
+def make_member(name, typ):
+    optional = False
+    if name.startswith('*'):
+        name = name[1:]
+        optional = True
+    if isinstance(typ, list):
+        typ = make_array_type(typ[0])
+    return QAPISchemaObjectTypeMember(name, typ, optional)
+
+def make_members(data):
+    return QAPISchemaObjectTypeMembers(map(lambda mname: make_member(mname, 
data[mname]), data))
+
+def make_flat_variant(case, typ):
+    return QAPISchemaObjectTypeVariant(case, typ)
+
+def make_simple_variant(case, typ):
+    return QAPISchemaObjectTypeVariant(case,
+                                       make_members(OrderedDict({ 'data': typ 
})))
+
+def def_struct_type(expr):
+    def_entity(QAPISchemaObjectType(expr['type'], None, expr.get('base'),
+                                    make_members(expr['data']), None))
+
+def def_union_type(expr):
+    name = expr['union']
+    data = expr['data']
+    discriminator = expr.get('discriminator')
+    if discriminator:
+        base = expr['base']
+        members = OrderedDict()
+        variants = map(lambda dval: make_flat_variant(dval, data[dval]),
+                       data)
+    else:
+        base = None
+        enum = make_implicit_enum_type(name, 'kind', data.keys())
+        members = OrderedDict({ 'type': enum })
+        discriminator = 'type'
+        variants = map(lambda dval: make_simple_variant(dval, data[dval]),
+                       data)
+    def_entity(QAPISchemaObjectType(name, None, base,
+                                    make_members(members),
+                                    QAPISchemaObjectTypeVariants(discriminator,
+                                                                 variants)))
+
+def def_alternate_type(expr):
+    def_entity(QAPISchemaAlternateType(expr['alternate'], None,
+                                       make_members(expr['data'])))
+
+def def_command(expr):
+    args = expr.get('data', {})
+    rets = expr.get('returns', {})
+    if not isinstance(args, str):
+        args = make_members(args)
+    if isinstance(rets, list):
+        assert len(rets) == 1
+        rets = make_array_type(rets[0])
+    elif not isinstance(rets, str):
+        rets = make_members(rets)
+    def_entity(QAPISchemaCommand(expr['command'], None, args, rets))
+
+def def_event(expr):
+    data = expr.get('data', {})
+    if not isinstance(data, str):
+        data = make_members(data)
+    def_entity(QAPISchemaEvent(expr['event'], None, data))
+
+def def_schema(exprs):
+    for expr in exprs:
+        if 'enum' in expr:
+            def_enum_type(expr)
+        elif 'type' in expr:
+            def_struct_type(expr)
+        elif 'union' in expr:
+            def_union_type(expr)
+        elif 'alternate' in expr:
+            def_alternate_type(expr)
+        elif 'command' in expr:
+            def_command(expr)
+        elif 'event' in expr:
+            def_event(expr)
+        else:
+            assert False
+
+def gen_qmp_schema_json():
+    seen = set()
+    for ent in entity_dict.values():
+        if not isinstance(ent, QAPISchemaType):
+            ent.collect(seen)
+    return filter(None, map(lambda name: entity_dict[name].gen_json(),
+                            sorted(seen)))
+
+(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line()
+
+c_comment = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+'''
+h_comment = '''
+/*
+ * QAPI/QMP schema introspection
+ *
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+'''
+
+(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
+                            'qmp-introspect.c', 'qmp-introspect.h',
+                            c_comment, h_comment)
+
+fdef.write(mcgen('''
+#include "%(prefix)sqmp-introspect.h"
+
+''',
+                 prefix=prefix))
+
+fdecl.write(mcgen('''
+extern char qmp_schema_json[];
+'''))
+
+exprs = parse_schema(input_file)
+
+def_builtin_types()
+def_schema(exprs)
+check_schema()
+
+jsons = gen_qmp_schema_json()
+fdef.write('char qmp_schema_json[] = "["\n    "'
+           + ',"\n    "'.join(jsons)
+           + ']";\n')
+
+close_output(fdef, fdecl)
diff --git a/tests/.gitignore b/tests/.gitignore
index 0dcb618..a583380 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -18,6 +18,7 @@ test-opts-visitor
 test-qapi-event.[ch]
 test-qapi-types.[ch]
 test-qapi-visit.[ch]
+test-qapi-introspect.[ch]
 test-qdev-global-props
 test-qemu-opts
 test-qmp-commands
diff --git a/tests/Makefile b/tests/Makefile
index 597919c..a3a171f 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -241,7 +241,8 @@ check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
        include-repetition.json event-nest-struct.json event-case.json)
 
 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
-                    tests/test-qmp-commands.h tests/test-qapi-event.h
+                    tests/test-qmp-commands.h tests/test-qapi-event.h \
+                     tests/test-qmp-introspect.h
 
 test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
        tests/check-qlist.o tests/check-qfloat.o tests/check-qjson.o \
@@ -314,6 +315,11 @@ $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json 
$(SRC_PATH)/scripts/qapi-eve
        $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-event.py \
                $(gen-out-type) -o tests -p "test-" $<, \
                "  GEN   $@")
+tests/test-qmp-introspect.c tests/test-qmp-introspect.h :\
+$(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json 
$(SRC_PATH)/scripts/qapi-introspect.py $(qapi-py)
+       $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-introspect.py \
+               $(gen-out-type) -o tests -p "test-" $<, \
+               "  GEN   $@")
 
 tests/test-string-output-visitor$(EXESUF): tests/test-string-output-visitor.o 
$(test-qapi-obj-y) libqemuutil.a libqemustub.a
 tests/test-string-input-visitor$(EXESUF): tests/test-string-input-visitor.o 
$(test-qapi-obj-y) libqemuutil.a libqemustub.a
-- 
1.9.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]