[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types
From: |
Anthony Liguori |
Subject: |
[Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types |
Date: |
Sun, 6 Mar 2011 19:22:50 -0600 |
Only generate qmp-types.[ch]. These files contain the type definitions for
QMP along with the alloc/free functions for these types. Functions to convert
enum values to integers and vice versa are also included.
qmp-types is used both within QEMU and within libqmp
Special alloc/free functions are provided to ensure that all structures are
padded when allocated. This makes sure that libqmp can provide a forward
compatible interface since all additions to a structure will have a boolean
enable flag.
The free function is convenient since individual structures may have pointers
that also require freeing.
Signed-off-by: Anthony Liguori <address@hidden>
diff --git a/Makefile b/Makefile
index 6b1d716..6b9fd69 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@ GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifeq ($(TRACE_BACKEND),dtrace)
GENERATED_HEADERS += trace-dtrace.h
endif
+GENERATED_HEADERS += qmp-types.h
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -146,6 +147,14 @@ trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
+qmp-types.c: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-body < $< >
$@, " GEN $@")
+
+qmp-types.h: $(SRC_PATH)/qmp-schema.json $(SRC_PATH)/qmp-gen.py
+ $(call quiet-command,python $(SRC_PATH)/qmp-gen.py --types-header < $<
> $@, " GEN $@")
+
+qmp-types.o: qmp-types.c qmp-types.h
+
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
diff --git a/Makefile.objs b/Makefile.objs
index 69f0383..710d99f 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -15,7 +15,7 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o
-block-obj-y += error.o
+block-obj-y += error.o qmp-types.o
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
diff --git a/ordereddict.py b/ordereddict.py
new file mode 100644
index 0000000..e17269f
--- /dev/null
+++ b/ordereddict.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2009 Raymond Hettinger
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+from UserDict import DictMixin
+
+class OrderedDict(dict, DictMixin):
+
+ def __init__(self, *args, **kwds):
+ if len(args) > 1:
+ raise TypeError('expected at most 1 arguments, got %d' % len(args))
+ try:
+ self.__end
+ except AttributeError:
+ self.clear()
+ self.update(*args, **kwds)
+
+ def clear(self):
+ self.__end = end = []
+ end += [None, end, end] # sentinel node for doubly linked list
+ self.__map = {} # key --> [key, prev, next]
+ dict.clear(self)
+
+ def __setitem__(self, key, value):
+ if key not in self:
+ end = self.__end
+ curr = end[1]
+ curr[2] = end[1] = self.__map[key] = [key, curr, end]
+ dict.__setitem__(self, key, value)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ key, prev, next = self.__map.pop(key)
+ prev[2] = next
+ next[1] = prev
+
+ def __iter__(self):
+ end = self.__end
+ curr = end[2]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[2]
+
+ def __reversed__(self):
+ end = self.__end
+ curr = end[1]
+ while curr is not end:
+ yield curr[0]
+ curr = curr[1]
+
+ def popitem(self, last=True):
+ if not self:
+ raise KeyError('dictionary is empty')
+ if last:
+ key = reversed(self).next()
+ else:
+ key = iter(self).next()
+ value = self.pop(key)
+ return key, value
+
+ def __reduce__(self):
+ items = [[k, self[k]] for k in self]
+ tmp = self.__map, self.__end
+ del self.__map, self.__end
+ inst_dict = vars(self).copy()
+ self.__map, self.__end = tmp
+ if inst_dict:
+ return (self.__class__, (items,), inst_dict)
+ return self.__class__, (items,)
+
+ def keys(self):
+ return list(self)
+
+ setdefault = DictMixin.setdefault
+ update = DictMixin.update
+ pop = DictMixin.pop
+ values = DictMixin.values
+ items = DictMixin.items
+ iterkeys = DictMixin.iterkeys
+ itervalues = DictMixin.itervalues
+ iteritems = DictMixin.iteritems
+
+ def __repr__(self):
+ if not self:
+ return '%s()' % (self.__class__.__name__,)
+ return '%s(%r)' % (self.__class__.__name__, self.items())
+
+ def copy(self):
+ return self.__class__(self)
+
+ @classmethod
+ def fromkeys(cls, iterable, value=None):
+ d = cls()
+ for key in iterable:
+ d[key] = value
+ return d
+
+ def __eq__(self, other):
+ if isinstance(other, OrderedDict):
+ if len(self) != len(other):
+ return False
+ for p, q in zip(self.items(), other.items()):
+ if p != q:
+ return False
+ return True
+ return dict.__eq__(self, other)
+
+ def __ne__(self, other):
+ return not self == other
+
diff --git a/qmp-gen.py b/qmp-gen.py
new file mode 100644
index 0000000..0889fa3
--- /dev/null
+++ b/qmp-gen.py
@@ -0,0 +1,393 @@
+##
+# QAPI Code Generator
+#
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# Anthony Liguori <address@hidden>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+##
+import sys
+from ordereddict import OrderedDict
+
+enum_types = []
+event_types = {}
+
+def c_var(name):
+ return '_'.join(name.split('-'))
+
+def genindent(count):
+ ret = ""
+ for i in range(count):
+ ret += " "
+ return ret
+
+def is_dict(obj):
+ if type(obj) in [dict, OrderedDict]:
+ return True
+ return False
+
+def qmp_array_type_to_c(typename):
+ if type(typename) == list or is_dict(typename):
+ return qmp_type_to_c(typename)
+ elif typename == 'int':
+ return 'IntArray *'
+ elif typename == 'str':
+ return 'StringArray *'
+ elif typename == 'bool':
+ return 'BoolArray *'
+ elif typename == 'number':
+ return 'DoubleArray *'
+ else:
+ return qmp_type_to_c(typename)
+
+def qmp_type_should_free(typename):
+ if (type(typename) == list or
+ typename == 'str' or
+ (typename not in ['int', 'bool', 'number'] and
+ typename not in enum_types and not typename.isupper())):
+ return True
+ return False
+
+def qmp_free_func(typename):
+ if type(typename) == list:
+ return qmp_free_func(typename[0])
+ elif typename == 'str':
+ return 'qemu_free'
+ else:
+ return 'qmp_free_%s' % (de_camel_case(typename))
+
+def qmp_type_is_event(typename):
+ if type(typename) == str and typename.isupper():
+ return True
+ return False
+
+def qmp_type_to_c(typename, retval=False, indent=0):
+ if type(typename) == list:
+ return qmp_array_type_to_c(typename[0])
+ elif is_dict(typename):
+ string = 'struct {\n'
+ for key in typename:
+ name = key
+ if key.startswith('*'):
+ name = key[1:]
+ string += "%sbool has_%s;\n" % (genindent(indent + 4),
c_var(name))
+ string += "%s%s %s;\n" % (genindent(indent + 4),
+ qmp_type_to_c(typename[key],
+ True,
+ indent=(indent + 4)),
+ c_var(name))
+ string += "%s}" % genindent(indent)
+ return string
+ elif typename == 'int':
+ return 'int64_t'
+ elif not retval and typename == 'str':
+ return 'const char *'
+ elif retval and typename == 'str':
+ return 'char *'
+ elif typename == 'bool':
+ return 'bool'
+ elif typename == 'number':
+ return 'double'
+ elif typename == 'none':
+ return 'void'
+ elif typename in enum_types:
+ return typename
+ elif qmp_type_is_event(typename):
+ return 'struct %s *' % qmp_event_to_c(typename)
+ else:
+ return 'struct %s *' % typename
+
+def qmp_type_to_qobj_ctor(typename):
+ return 'qmp_marshal_type_%s' % typename
+
+def qmp_type_from_qobj(typename):
+ return qobj_to_c(typename)
+
+def parse_args(typeinfo):
+ for member in typeinfo:
+ argname = member
+ argtype = typeinfo[member]
+ optional = False
+ if member.startswith('*'):
+ argname = member[1:]
+ optional = True
+ yield (argname, argtype, optional)
+
+def de_camel_case(name):
+ new_name = ''
+ for ch in name:
+ if ch.isupper() and new_name:
+ new_name += '_'
+ new_name += ch.lower()
+ return new_name
+
+def camel_case(name):
+ new_name = ''
+ first = True
+ for ch in name:
+ if ch == '_':
+ first = True
+ elif first:
+ new_name += ch.upper()
+ first = False
+ else:
+ new_name += ch.lower()
+ return new_name
+
+def qmp_event_to_c(name):
+ return '%sEvent' % camel_case(name)
+
+def qmp_event_func_to_c(name):
+ return '%sFunc' % camel_case(name)
+
+def inprint(string, indent):
+ print '%s%s' % (genindent(indent), string)
+
+def enum_abbreviation(name):
+ ab = ''
+ for ch in name:
+ if ch.isupper():
+ ab += ch
+ return ab
+
+def print_enum_declaration(name, entries):
+ print
+ print 'typedef enum %s {' % name
+ i = 0
+ for entry in entries:
+ print ' %s_%s = %d,' % (enum_abbreviation(name), entry.upper(), i)
+ i += 1
+ print '} %s;' % name
+ print
+ print '%s qmp_type_%s_from_str(const char *str, Error **errp);' % (name,
de_camel_case(name))
+ print 'const char *qmp_type_%s_to_str(%s value, Error **errp);' %
(de_camel_case(name), name)
+
+def print_enum_definition(name, entries):
+ print '''
+%s qmp_type_%s_from_str(const char *str, Error **errp)
+{''' % (name, de_camel_case(name))
+ first = True
+ for entry in entries:
+ if first:
+ print ' if (strcmp(str, "%s") == 0) {' % entry
+ first = False
+ else:
+ print ' } else if (strcmp(str, "%s") == 0) {' % entry
+ print ' return %s_%s;' % (enum_abbreviation(name),
entry.upper())
+ print ''' } else {
+ error_set(errp, QERR_ENUM_VALUE_INVALID, "%s", str);
+ return %s_%s;
+ }
+}''' % (name, enum_abbreviation(name), entries[0].upper())
+
+ print '''
+const char *qmp_type_%s_to_str(%s value, Error **errp)
+{''' % (de_camel_case(name), name)
+ first = True
+ for entry in entries:
+ enum = '%s_%s' % (enum_abbreviation(name), entry.upper())
+ if first:
+ print ' if (value == %s) {' % enum
+ first = False
+ else:
+ print ' } else if (value == %s) {' % enum
+ print ' return "%s";' % entry
+ print ''' } else {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%%d", value);
+ error_set(errp, QERR_ENUM_VALUE_INVALID, "%s", buf);
+ return NULL;
+ }
+}''' % name
+
+def print_type_declaration(name, typeinfo):
+ if type(typeinfo) == str:
+ print
+ print "typedef %s %s;" % (qmp_type_to_c(typeinfo), name)
+ elif is_dict(typeinfo) and not name.isupper():
+ print
+ print "typedef struct %s %s;" % (name, name)
+ print "struct %s {" % name
+ for key in typeinfo:
+ member = key
+ if key.startswith('*'):
+ member = key[1:]
+ print " bool has_%s;" % c_var(member)
+ print " %s %s;" % (qmp_type_to_c(typeinfo[key], True,
indent=4), c_var(member))
+ print " %s *next;" % c_var(name)
+ print "};"
+ print
+ print "%s *qmp_alloc_%s(void);" % (name, de_camel_case(name))
+ print "void qmp_free_%s(%s *obj);" % (de_camel_case(name), name)
+ elif is_dict(typeinfo) and name.isupper():
+ arglist = ['void *opaque']
+ for argname, argtype, optional in parse_args(typeinfo):
+ arglist.append('%s %s' % (qmp_type_to_c(argtype), argname))
+ print
+ print 'typedef void (%s)(%s);' % (qmp_event_func_to_c(name), ',
'.join(arglist))
+ print
+ print 'typedef struct %s {' % qmp_event_to_c(name)
+ print ' QmpSignal *signal;'
+ print ' %s *func;' % qmp_event_func_to_c(name)
+ print '} %s;' % qmp_event_to_c(name)
+
+def print_type_free(typeinfo, prefix, indent=4):
+ for argname, argtype, optional in parse_args(typeinfo):
+ if type(argtype) == list:
+ argtype = argtype[0]
+
+ if is_dict(argtype):
+ if optional:
+ inprint('if (%shas_%s) {' % (prefix, argname), indent)
+ print_type_free(argtype, '%s%s.' % (prefix, argname), indent +
4)
+ inprint('}', indent)
+ else:
+ print_type_free(argtype, '%s%s.' % (prefix, argname), indent)
+ elif qmp_type_should_free(argtype):
+ if optional:
+ inprint('if (%shas_%s) {' % (prefix, argname), indent)
+ inprint(' %s(%s%s);' % (qmp_free_func(argtype), prefix,
argname), indent)
+ inprint('}', indent)
+ else:
+ inprint('%s(%s%s);' % (qmp_free_func(argtype), prefix,
argname), indent)
+
+def print_type_definition(name, typeinfo):
+ if qmp_type_is_event(name):
+ return
+
+ c_var_name = de_camel_case(name)
+
+ print '''
+void qmp_free_%s(%s *obj)
+{
+ if (!obj) {
+ return;
+ }''' % (c_var_name, name)
+
+ print_type_free(typeinfo, 'obj->')
+
+ print '''
+ %s(obj->next);
+ qemu_free(obj);
+}''' % (qmp_free_func(name))
+
+ print '''
+%s *qmp_alloc_%s(void)
+{
+ BUILD_ASSERT(sizeof(%s) < 512);
+ return qemu_mallocz(512);
+}''' % (name, c_var_name, name)
+
+def tokenize(data):
+ while len(data):
+ if data[0] in ['{', '}', ':', ',', '[', ']']:
+ yield data[0]
+ data = data[1:]
+ elif data[0] in ' \n':
+ data = data[1:]
+ elif data[0] == "'":
+ data = data[1:]
+ string = ''
+ while data[0] != "'":
+ string += data[0]
+ data = data[1:]
+ data = data[1:]
+ yield string
+
+def parse_value(tokens):
+ if tokens[0] == '{':
+ ret = OrderedDict()
+ tokens = tokens[1:]
+ while tokens[0] != '}':
+ key = tokens[0]
+ tokens = tokens[1:]
+
+ tokens = tokens[1:] # :
+
+ value, tokens = parse_value(tokens)
+
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+
+ ret[key] = value
+ tokens = tokens[1:]
+ return ret, tokens
+ elif tokens[0] == '[':
+ ret = []
+ tokens = tokens[1:]
+ while tokens[0] != ']':
+ value, tokens = parse_value(tokens)
+ if tokens[0] == ',':
+ tokens = tokens[1:]
+ ret.append(value)
+ tokens = tokens[1:]
+ return ret, tokens
+ else:
+ return tokens[0], tokens[1:]
+
+def ordered_eval(string):
+ return parse_value(map(lambda x: x, tokenize(string)))[0]
+
+if len(sys.argv) == 2:
+ if sys.argv[1] == '--types-body':
+ kind = 'types-body'
+ elif sys.argv[1] == '--types-header':
+ kind = 'types-header'
+
+if kind == 'types-header':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+#ifndef QMP_TYPES_H
+#define QMP_TYPES_H
+
+#include "qmp-types-core.h"
+
+'''
+elif kind == 'types-body':
+ print '''/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT */
+
+#include "qmp-types.h"
+#include "qemu-common.h"
+'''
+
+exprs = []
+expr = ''
+
+for line in sys.stdin:
+ if line.startswith('#') or line == '\n':
+ continue
+
+ if line.startswith(' '):
+ expr += line
+ elif expr:
+ s = ordered_eval(expr)
+ exprs.append(s)
+ expr = line
+ else:
+ expr += line
+
+if expr:
+ s = ordered_eval(expr)
+ exprs.append(s)
+
+for s in exprs:
+ if is_dict(s):
+ key = s.keys()[0]
+ if is_dict(s[key]):
+ if qmp_type_is_event(key):
+ event_types[key] = s[key]
+ if kind == 'types-body':
+ print_type_definition(key, s[key])
+ elif kind == 'types-header':
+ print_type_declaration(key, s[key])
+ else:
+ enum_types.append(key)
+ if kind == 'types-header':
+ print_enum_declaration(key, s[key])
+ elif kind == 'types-body':
+ print_enum_definition(key, s[key])
+
+if kind.endswith('header'):
+ print '#endif'
diff --git a/qmp-schema.json b/qmp-schema.json
new file mode 100644
index 0000000..e69de29
diff --git a/qmp-types-core.h b/qmp-types-core.h
new file mode 100644
index 0000000..dbf1fd8
--- /dev/null
+++ b/qmp-types-core.h
@@ -0,0 +1,27 @@
+/*
+ * QAPI
+ *
+ * Copyright IBM, Corp. 2011
+ *
+ * Authors:
+ * Anthony Liguori <address@hidden>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2. See
+ * the COPYING.LIB file in the top-level directory.
+ */
+#ifndef QMP_TYPES_CORE_H
+#define QMP_TYPES_CORE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "error.h"
+
+typedef struct QmpSignal QmpSignal;
+
+#define BUILD_ASSERT(cond) do { \
+ (void)sizeof(int[-1+!!(cond)]); \
+} while (0)
+
+#define BUILD_BUG() BUILD_ASSERT(0)
+
+#endif
--
1.7.0.4
- Re: [Qemu-devel] [PATCH 19/22] qapi: add QMP put-event command, (continued)
[Qemu-devel] [PATCH 21/22] qapi: add test-libqmp, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 22/22] qapi: generate HTML report for test-libqmp, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 01/22] Add hard build dependency on glib, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 20/22] qapi: add code generator for libqmp, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 04/22] qerror: split out the reporting bits of QError, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 08/22] qapi: add code generator for qmp-types,
Anthony Liguori <=
[Qemu-devel] [PATCH 09/22] qapi: add code generator for type marshallers, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH 05/22] qerror: add new error message for invalid enum values, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH] qapi: qmp-types.c and qmp-types.h, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH] qapi: qmp-marshal-types.c and qmp-marshal-types.h, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH] qapi: add qmp-marshal.c and qmp.h, Anthony Liguori, 2011/03/06
[Qemu-devel] [PATCH] qapi: add libqmp.c and libqmp.h, Anthony Liguori, 2011/03/06
Re: [Qemu-devel] [PATCH 00/22] QAPI Round 1, Stefan Hajnoczi, 2011/03/07