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 <aligu...@us.ibm.com> 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 <aligu...@us.ibm.com> +# +# 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 <aligu...@us.ibm.com> + * + * 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