This is a code generator for qapi introspection. It will parse qapi-schema.json, extend schema definitions and generate a schema table with metadata, it references to the new structs which we used to describe dynamic data structs. The metadata will help C code to allocate right structs and provide useful information to management to checking suported feature and QMP commandline detail. The schema table will be saved to qapi-introspect.h.
The $(prefix) is used to as a namespace to keep the generated code from one schema/code-generation separated from others so code and be generated from multiple schemas with clobbering previously created code. Signed-off-by: Amos Kong <ak...@redhat.com> --- .gitignore | 1 + Makefile | 5 +- docs/qmp-full-introspection.txt | 17 ++++ scripts/qapi-introspect.py | 172 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 scripts/qapi-introspect.py diff --git a/.gitignore b/.gitignore index 1c9d63d..de3cb80 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ linux-headers/asm qapi-generated qapi-types.[ch] qapi-visit.[ch] +qapi-introspect.h qmp-commands.h qmp-marshal.c qemu-doc.html diff --git a/Makefile b/Makefile index bdff4e4..1dac5e7 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ endif endif GENERATED_HEADERS = config-host.h qemu-options.def -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h +GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qapi-introspect.h GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c GENERATED_HEADERS += trace/generated-events.h @@ -229,6 +229,9 @@ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py $(qapi-py) qmp-commands.h qmp-marshal.c :\ $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@") +qapi-introspect.h:\ +$(SRC_PATH)/qapi-schema.json $(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/docs/qmp-full-introspection.txt b/docs/qmp-full-introspection.txt index d2cf7b3..8ecbc0c 100644 --- a/docs/qmp-full-introspection.txt +++ b/docs/qmp-full-introspection.txt @@ -42,3 +42,20 @@ types. 'anonymous-struct' will be used to describe arbitrary structs (dictionary, list or string). + +== Avoid dead loop in recursive extending == + +We have four types (ImageInfo, BlockStats, PciDeviceInfo, ObjectData) +that uses themself in their own define data directly or indirectly, +we will not repeatedly extend them to avoid dead loop. + +We use a 'parents List' to record the visit path, type name of each +extended node will be saved to the List. + +Append type name to the list before extending, and remove type name +from the list after extending. + +If the type name is already extended in parents List, we won't extend +it repeatedly for avoiding dead loop. + +'recursive' indicates if the type is extended or not. diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py new file mode 100644 index 0000000..03179fa --- /dev/null +++ b/scripts/qapi-introspect.py @@ -0,0 +1,172 @@ +# +# QAPI introspection info generator +# +# Copyright (C) 2014 Red Hat, Inc. +# +# Authors: +# Amos Kong <ak...@redhat.com> +# +# This work is licensed under the terms of the GNU GPLv2. +# See the COPYING.LIB file in the top-level directory. + +from ordereddict import OrderedDict +from qapi import * +import sys +import os +import getopt +import errno + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "hp:o:", + ["header", "prefix=", "output-dir="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +h_file = 'qapi-introspect.h' + +do_h = False + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-h", "--header"): + do_h = True + +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +def maybe_open(really, name, opt): + if really: + return open(name, opt) + else: + import StringIO + return StringIO.StringIO() + +fdecl = maybe_open(do_h, h_file, 'w') + +fdecl.write(mcgen(''' +/* AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * Head file to store parsed information of QAPI schema + * + * Copyright (C) 2014 Red Hat, Inc. + * + * Authors: + * Amos Kong <ak...@redhat.com> + * + * 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. + * + */ + +#ifndef %(guard)s +#define %(guard)s + +''', + guard=guardname(h_file))) + +def extend_schema(expr, parents=[], member=True): + ret = {} + recu = 'False' + name = "" + + if type(expr) is OrderedDict: + if not member: + e = expr.popitem(last=False) + typ = e[0] + name = e[1] + else: + typ = "anonymous-struct" + + if typ == 'enum': + for key in expr.keys(): + ret[key] = expr[key] + else: + ret = {} + for key in expr.keys(): + ret[key], parents = extend_schema(expr[key], parents) + + elif type(expr) is list: + typ = 'anonymous-struct' + ret = [] + for i in expr: + tmp, parents = extend_schema(i, parents) + ret.append(tmp) + elif type(expr) is str: + name = expr + if schema_dict.has_key(expr) and expr not in parents: + parents.append(expr) + typ = schema_dict[expr][1] + recu = 'True' + ret, parents = extend_schema(schema_dict[expr][0].copy(), + parents, False) + parents.remove(expr) + ret['_obj_recursive'] = 'True' + return ret, parents + else: + return expr, parents + + return {'_obj_member': "%s" % member, '_obj_type': typ, + '_obj_name': name, '_obj_recursive': recu, + '_obj_data': ret}, parents + + +exprs = parse_schema(sys.stdin) +schema_dict = {} + +for expr in exprs: + if expr.has_key('type') or expr.has_key('enum') or expr.has_key('union'): + e = expr.copy() + + first = e.popitem(last=False) + schema_dict[first[1]] = [expr.copy(), first[0]] + +fdecl.write('''const char *const qmp_schema_table[] = { +''') + +def convert(odict): + d = {} + for k, v in odict.items(): + if type(v) is OrderedDict: + d[k] = convert(v) + elif type(v) is list: + l = [] + for j in v: + if type(j) is OrderedDict: + l.append(convert(j)) + else: + l.append(j) + d[k] = l + else: + d[k] = v + return d + +count = 0 +for expr in exprs: + fdecl.write(''' /* %s */ +''' % expr) + + expr, parents = extend_schema(expr, [], False) + fdecl.write(''' "%s", + +''' % convert(expr)) + +fdecl.write(''' NULL }; + +#endif +''') + +fdecl.flush() +fdecl.close() -- 1.8.4.2