qemu/scripts/qapi-commands.py
Eric Blake 05372f708a qapi: Consistent generated code: minimize push_indent() usage
We had some pointless differences in the generated code for visit,
command marshalling, and events; unifying them makes it easier for
future patches to consolidate to common helper functions.
This is one patch of a series to clean up these differences.

This patch reduces the number of push_indent()/pop_indent() pairs
so that generated code is typically already at its natural output
indentation in the python files.  It is easier to reason about
generated code if the reader does not have to track how much
spacing will be inserted alongside the code, and moreso when all
of the generators use the same patterns (qapi-type and qapi-event
were already using in-place indentation).

Arguably, the resulting python may be a bit harder to read with C
code at the same indentation as python; on the other hand, not
having to think about push_indent() is a win, and most decent
editors provide syntax highlighting that makes it easier to
visually distinguish python code from string literals that will
become C code.

There is no change to the generated output.

Signed-off-by: Eric Blake <eblake@redhat.com>
Message-Id: <1443565276-4535-15-git-send-email-eblake@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-10-12 18:46:49 +02:00

366 lines
8.4 KiB
Python

#
# QAPI command marshaller generator
#
# Copyright IBM, Corp. 2011
# Copyright (C) 2014-2015 Red Hat, Inc.
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
# Michael Roth <mdroth@linux.vnet.ibm.com>
# Markus Armbruster <armbru@redhat.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from qapi import *
import re
def gen_command_decl(name, arg_type, ret_type):
return mcgen('''
%(c_type)s qmp_%(c_name)s(%(params)s);
''',
c_type=(ret_type and ret_type.c_type()) or 'void',
c_name=c_name(name),
params=gen_params(arg_type, 'Error **errp'))
def gen_err_check(err):
if not err:
return ''
return mcgen('''
if (%(err)s) {
goto out;
}
''',
err=err)
def gen_call(name, arg_type, ret_type):
ret = ''
argstr = ''
if arg_type:
for memb in arg_type.members:
if memb.optional:
argstr += 'has_%s, ' % c_name(memb.name)
argstr += '%s, ' % c_name(memb.name)
lhs = ''
if ret_type:
lhs = 'retval = '
ret = mcgen('''
%(lhs)sqmp_%(c_name)s(%(args)s&err);
''',
c_name=c_name(name), args=argstr, lhs=lhs)
if ret_type:
ret += gen_err_check('err')
ret += mcgen('''
qmp_marshal_output_%(c_name)s(retval, ret, &err);
''',
c_name=ret_type.c_name())
return ret
def gen_marshal_vars(arg_type, ret_type):
ret = mcgen('''
Error *err = NULL;
''')
if ret_type:
ret += mcgen('''
%(c_type)s retval;
''',
c_type=ret_type.c_type())
if arg_type:
ret += mcgen('''
QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args));
QapiDeallocVisitor *qdv;
Visitor *v;
''')
for memb in arg_type.members:
if memb.optional:
ret += mcgen('''
bool has_%(c_name)s = false;
''',
c_name=c_name(memb.name))
ret += mcgen('''
%(c_type)s %(c_name)s = %(c_null)s;
''',
c_name=c_name(memb.name),
c_type=memb.type.c_type(),
c_null=memb.type.c_null())
ret += '\n'
else:
ret += mcgen('''
(void)args;
''')
return ret
def gen_marshal_input_visit(arg_type, dealloc=False):
ret = ''
if not arg_type:
return ret
if dealloc:
errparg = 'NULL'
errarg = None
ret += mcgen('''
qmp_input_visitor_cleanup(qiv);
qdv = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(qdv);
''')
else:
errparg = '&err'
errarg = 'err'
ret += mcgen('''
v = qmp_input_get_visitor(qiv);
''')
for memb in arg_type.members:
if memb.optional:
ret += mcgen('''
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
''',
c_name=c_name(memb.name), name=memb.name,
errp=errparg)
ret += gen_err_check(errarg)
ret += mcgen('''
if (has_%(c_name)s) {
''',
c_name=c_name(memb.name))
push_indent()
ret += mcgen('''
visit_type_%(c_type)s(v, &%(c_name)s, "%(name)s", %(errp)s);
''',
c_name=c_name(memb.name), name=memb.name,
c_type=memb.type.c_name(), errp=errparg)
ret += gen_err_check(errarg)
if memb.optional:
pop_indent()
ret += mcgen('''
}
''')
if dealloc:
ret += mcgen('''
qapi_dealloc_visitor_cleanup(qdv);
''')
return ret
def gen_marshal_output(ret_type):
return mcgen('''
static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp)
{
Error *err = NULL;
QmpOutputVisitor *qov = qmp_output_visitor_new();
QapiDeallocVisitor *qdv;
Visitor *v;
v = qmp_output_get_visitor(qov);
visit_type_%(c_name)s(v, &ret_in, "unused", &err);
if (err) {
goto out;
}
*ret_out = qmp_output_get_qobject(qov);
out:
error_propagate(errp, err);
qmp_output_visitor_cleanup(qov);
qdv = qapi_dealloc_visitor_new();
v = qapi_dealloc_get_visitor(qdv);
visit_type_%(c_name)s(v, &ret_in, "unused", NULL);
qapi_dealloc_visitor_cleanup(qdv);
}
''',
c_type=ret_type.c_type(), c_name=ret_type.c_name())
def gen_marshal_proto(name):
ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name)
if not middle_mode:
ret = 'static ' + ret
return ret
def gen_marshal_decl(name):
return mcgen('''
%(proto)s;
''',
proto=gen_marshal_proto(name))
def gen_marshal(name, arg_type, ret_type):
ret = mcgen('''
%(proto)s
{
''',
proto=gen_marshal_proto(name))
ret += gen_marshal_vars(arg_type, ret_type)
ret += gen_marshal_input_visit(arg_type)
ret += gen_call(name, arg_type, ret_type)
if re.search('^ *goto out;', ret, re.MULTILINE):
ret += mcgen('''
out:
''')
ret += mcgen('''
error_propagate(errp, err);
''')
ret += gen_marshal_input_visit(arg_type, dealloc=True)
ret += mcgen('''
}
''')
return ret
def gen_register_command(name, success_response):
options = 'QCO_NO_OPTIONS'
if not success_response:
options = 'QCO_NO_SUCCESS_RESP'
ret = mcgen('''
qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
''',
name=name, c_name=c_name(name),
opts=options)
return ret
def gen_registry(registry):
ret = mcgen('''
static void qmp_init_marshal(void)
{
''')
ret += registry
ret += mcgen('''
}
qapi_init(qmp_init_marshal);
''')
return ret
class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
def __init__(self):
self.decl = None
self.defn = None
self._regy = None
self._visited_ret_types = None
def visit_begin(self, schema):
self.decl = ''
self.defn = ''
self._regy = ''
self._visited_ret_types = set()
def visit_end(self):
if not middle_mode:
self.defn += gen_registry(self._regy)
self._regy = None
self._visited_ret_types = None
def visit_command(self, name, info, arg_type, ret_type,
gen, success_response):
if not gen:
return
self.decl += gen_command_decl(name, arg_type, ret_type)
if ret_type and ret_type not in self._visited_ret_types:
self._visited_ret_types.add(ret_type)
self.defn += gen_marshal_output(ret_type)
if middle_mode:
self.decl += gen_marshal_decl(name)
self.defn += gen_marshal(name, arg_type, ret_type)
if not middle_mode:
self._regy += gen_register_command(name, success_response)
middle_mode = False
(input_file, output_dir, do_c, do_h, prefix, opts) = \
parse_command_line("m", ["middle"])
for o, a in opts:
if o in ("-m", "--middle"):
middle_mode = True
c_comment = '''
/*
* schema-defined QMP->QAPI command dispatch
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.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.
*
*/
'''
h_comment = '''
/*
* schema-defined QAPI function prototypes
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.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.
*
*/
'''
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
'qmp-marshal.c', 'qmp-commands.h',
c_comment, h_comment)
fdef.write(mcgen('''
#include "qemu-common.h"
#include "qemu/module.h"
#include "qapi/qmp/types.h"
#include "qapi/qmp/dispatch.h"
#include "qapi/visitor.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp-input-visitor.h"
#include "qapi/dealloc-visitor.h"
#include "%(prefix)sqapi-types.h"
#include "%(prefix)sqapi-visit.h"
#include "%(prefix)sqmp-commands.h"
''',
prefix=prefix))
fdecl.write(mcgen('''
#include "%(prefix)sqapi-types.h"
#include "qapi/qmp/qdict.h"
#include "qapi/error.h"
''',
prefix=prefix))
schema = QAPISchema(input_file)
gen = QAPISchemaGenCommandVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
close_output(fdef, fdecl)