From patchwork Thu Jul 21 20:01:06 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Luiz Capitulino X-Patchwork-Id: 106148 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 0A7CEB6F71 for ; Fri, 22 Jul 2011 06:12:51 +1000 (EST) Received: from localhost ([::1]:37947 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Qjzbu-0007g5-NU for incoming@patchwork.ozlabs.org; Thu, 21 Jul 2011 16:12:42 -0400 Received: from eggs.gnu.org ([140.186.70.92]:39767) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QjzTL-0004MV-Ny for qemu-devel@nongnu.org; Thu, 21 Jul 2011 16:03:53 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QjzTJ-0001FI-P4 for qemu-devel@nongnu.org; Thu, 21 Jul 2011 16:03:51 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46530) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QjzTJ-0001FB-EW for qemu-devel@nongnu.org; Thu, 21 Jul 2011 16:03:49 -0400 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id p6LK3kDV003688 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Thu, 21 Jul 2011 16:03:46 -0400 Received: from localhost (ovpn-113-126.phx2.redhat.com [10.3.113.126]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p6LK3jWG013231; Thu, 21 Jul 2011 16:03:46 -0400 From: Luiz Capitulino To: aliguori@us.ibm.com Date: Thu, 21 Jul 2011 17:01:06 -0300 Message-Id: <1311278474-24336-18-git-send-email-lcapitulino@redhat.com> In-Reply-To: <1311278474-24336-1-git-send-email-lcapitulino@redhat.com> References: <1311278474-24336-1-git-send-email-lcapitulino@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: Luiz Capitulino , qemu-devel@nongnu.org, Michael Roth Subject: [Qemu-devel] [PATCH 17/25] qapi: add qapi-commands.py code generator X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Michael Roth This is the code generator for qapi command marshaling/dispatch. Currently only generators for synchronous qapi/qmp functions are supported. This script generates the following files: $(prefix)qmp-marshal.c: command marshal/dispatch functions for each QMP command defined in the schema. Functions generated by qapi-visit.py are used to convert qobjects recieved from the wire into function parameters, and uses the same visiter functions to convert native C return values to qobjects from transmission back over the wire. $(prefix)qmp-commands.h: Function prototypes for the QMP commands specified in the schema. $(prefix) is used in the same manner as with qapi-types.py Signed-off-by: Michael Roth Signed-off-by: Luiz Capitulino --- scripts/qapi-commands.py | 385 ++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 385 insertions(+), 0 deletions(-) create mode 100644 scripts/qapi-commands.py diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py new file mode 100644 index 0000000..9ad4c54 --- /dev/null +++ b/scripts/qapi-commands.py @@ -0,0 +1,385 @@ +# +# QAPI command marshaller generator +# +# Copyright IBM, Corp. 2011 +# +# Authors: +# Anthony Liguori +# Michael Roth +# +# 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 + +def generate_decl_enum(name, members, genlist=True): + return mcgen(''' + +void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); +''', + name=name) + +def generate_command_decl(name, args, ret_type): + arglist="" + for argname, argtype, optional, structured in parse_args(args): + argtype = c_type(argtype) + if argtype == "char *": + argtype = "const char *" + if optional: + arglist += "bool has_%s, " % c_var(argname) + arglist += "%s %s, " % (argtype, c_var(argname)) + return mcgen(''' +%(ret_type)s qmp_%(name)s(%(args)sError **errp); +''', + ret_type=c_type(ret_type), name=c_var(name), args=arglist).strip() + +def gen_sync_call(name, args, ret_type, indent=0): + ret = "" + arglist="" + retval="" + if ret_type: + retval = "retval = " + for argname, argtype, optional, structured in parse_args(args): + if optional: + arglist += "has_%s, " % c_var(argname) + arglist += "%s, " % (c_var(argname)) + push_indent(indent) + ret = mcgen(''' +%(retval)sqmp_%(name)s(%(args)serrp); + +''', + name=c_var(name), args=arglist, retval=retval).rstrip() + if ret_type: + ret += "\n" + mcgen('''' +%(marshal_output_call)s +''', + marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() + pop_indent(indent) + return ret.rstrip() + + +def gen_marshal_output_call(name, ret_type): + if not ret_type: + return "" + return "qmp_marshal_output_%s(retval, ret, errp);" % c_var(name) + +def gen_visitor_output_containers_decl(ret_type): + ret = "" + push_indent() + if ret_type: + ret += mcgen(''' +QmpOutputVisitor *mo; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret + +def gen_visitor_input_containers_decl(args): + ret = "" + + push_indent() + if len(args) > 0: + ret += mcgen(''' +QmpInputVisitor *mi; +QapiDeallocVisitor *md; +Visitor *v; +''') + pop_indent() + + return ret.rstrip() + +def gen_visitor_input_vars_decl(args): + ret = "" + push_indent() + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +bool has_%(argname)s = false; +''', + argname=c_var(argname)) + if c_type(argtype).endswith("*"): + ret += mcgen(''' +%(argtype)s %(argname)s = NULL; +''', + argname=c_var(argname), argtype=c_type(argtype)) + else: + ret += mcgen(''' +%(argtype)s %(argname)s; +''', + argname=c_var(argname), argtype=c_type(argtype)) + + pop_indent() + return ret.rstrip() + +def gen_visitor_input_block(args, obj, dealloc=False): + ret = "" + if len(args) == 0: + return ret + + push_indent() + + if dealloc: + ret += mcgen(''' +md = qapi_dealloc_visitor_new(); +v = qapi_dealloc_get_visitor(md); +''') + else: + ret += mcgen(''' +mi = qmp_input_visitor_new(%(obj)s); +v = qmp_input_get_visitor(mi); +''', + obj=obj) + + for argname, argtype, optional, structured in parse_args(args): + if optional: + ret += mcgen(''' +visit_start_optional(v, &has_%(c_name)s, "%(name)s", errp); +if (has_%(c_name)s) { +''', + c_name=c_var(argname), name=argname) + push_indent() + ret += mcgen(''' +visit_type_%(argtype)s(v, &%(c_name)s, "%(name)s", errp); +''', + c_name=c_var(argname), name=argname, argtype=argtype) + if optional: + pop_indent() + ret += mcgen(''' +} +visit_end_optional(v, errp); +''') + + if dealloc: + ret += mcgen(''' +qapi_dealloc_visitor_cleanup(md); +''') + else: + ret += mcgen(''' +qmp_input_visitor_cleanup(mi); +''') + pop_indent() + return ret.rstrip() + +def gen_marshal_output(name, args, ret_type): + if not ret_type: + return "" + ret = mcgen(''' +static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) +{ + QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); + QmpOutputVisitor *mo = qmp_output_visitor_new(); + Visitor *v; + + v = qmp_output_get_visitor(mo); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + if (!error_is_set(errp)) { + *ret_out = qmp_output_get_qobject(mo); + } + qmp_output_visitor_cleanup(mo); + v = qapi_dealloc_get_visitor(md); + visit_type_%(ret_type)s(v, &ret_in, "unused", errp); + qapi_dealloc_visitor_cleanup(md); +} +''', + c_ret_type=c_type(ret_type), c_name=c_var(name), ret_type=ret_type) + + return ret + +def gen_marshal_input(name, args, ret_type): + ret = mcgen(''' +static void qmp_marshal_input_%(c_name)s(QDict *args, QObject **ret, Error **errp) +{ +''', + c_name=c_var(name)) + + if ret_type: + if c_type(ret_type).endswith("*"): + retval = " %s retval = NULL;" % c_type(ret_type) + else: + retval = " %s retval;" % c_type(ret_type) + ret += mcgen(''' +%(retval)s +''', + retval=retval) + + if len(args) > 0: + ret += mcgen(''' +%(visitor_input_containers_decl)s +%(visitor_input_vars_decl)s + +%(visitor_input_block)s + +''', + visitor_input_containers_decl=gen_visitor_input_containers_decl(args), + visitor_input_vars_decl=gen_visitor_input_vars_decl(args), + visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) + + ret += mcgen(''' + if (error_is_set(errp)) { + goto out; + } +%(sync_call)s +''', + sync_call=gen_sync_call(name, args, ret_type, indent=4)) + ret += mcgen(''' + +out: +''') + ret += mcgen(''' +%(visitor_input_block_cleanup)s + return; +} +''', + visitor_input_block_cleanup=gen_visitor_input_block(args, None, dealloc=True)) + return ret + +def gen_registry(commands): + registry="" + push_indent() + for cmd in commands: + registry += mcgen(''' +qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s); +''', + name=cmd['command'], c_name=c_var(cmd['command'])) + pop_indent() + ret = mcgen(''' +static void qmp_init_marshal(void) +{ +%(registry)s +} + +qapi_init(qmp_init_marshal); +''', + registry=registry.rstrip()) + return ret + +def gen_command_decl_prologue(header, guard, prefix=""): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QAPI function prototypes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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 + +#include "%(prefix)sqapi-types.h" +#include "error.h" + +''', + header=basename(h_file), guard=guardname(h_file), prefix=prefix) + return ret + +def gen_command_def_prologue(prefix="", proxy=False): + ret = mcgen(''' +/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ + +/* + * schema-defined QMP->QAPI command dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * 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. + * + */ + +#include "qemu-objects.h" +#include "qapi/qmp-core.h" +#include "qapi/qapi-visit-core.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qapi-dealloc-visitor.h" +#include "%(prefix)sqapi-types.h" +#include "%(prefix)sqapi-visit.h" + +''', + prefix=prefix) + if not proxy: + ret += '#include "%sqmp-commands.h"' % prefix + return ret + "\n" + + +try: + opts, args = getopt.gnu_getopt(sys.argv[1:], "p:o:", ["prefix=", "output-dir=", "type="]) +except getopt.GetoptError, err: + print str(err) + sys.exit(1) + +output_dir = "" +prefix = "" +dispatch_type = "sync" +c_file = 'qmp-marshal.c' +h_file = 'qmp-commands.h' + +for o, a in opts: + if o in ("-p", "--prefix"): + prefix = a + elif o in ("-o", "--output-dir"): + output_dir = a + "/" + elif o in ("-t", "--type"): + dispatch_type = a + +c_file = output_dir + prefix + c_file +h_file = output_dir + prefix + h_file + +try: + os.makedirs(output_dir) +except os.error, e: + if e.errno != errno.EEXIST: + raise + +exprs = parse_schema(sys.stdin) +commands = filter(lambda expr: expr.has_key('command'), exprs) + +if dispatch_type == "sync": + fdecl = open(h_file, 'w') + fdef = open(c_file, 'w') + ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) + fdecl.write(ret) + ret = gen_command_def_prologue(prefix=prefix) + fdef.write(ret) + + for cmd in commands: + arglist = [] + ret_type = None + if cmd.has_key('data'): + arglist = cmd['data'] + if cmd.has_key('returns'): + ret_type = cmd['returns'] + ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" + fdecl.write(ret) + if ret_type: + ret = gen_marshal_output(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + ret = gen_marshal_input(cmd['command'], arglist, ret_type) + "\n" + fdef.write(ret) + + fdecl.write("\n#endif"); + ret = gen_registry(commands) + fdef.write(ret) + + fdef.flush() + fdef.close() + fdecl.flush() + fdecl.close()