qemu/scripts/qapi-event.py
Eric Blake 6b5abc7df7 qapi: Drop support for inline nested types
A future patch will be using a 'name':{dictionary} entry in the
QAPI schema to specify a default value for an optional argument
(see previous commit messages for more details why); but existing
use of inline nested structs conflicts with that goal. Now that
all commands have been changed to avoid inline nested structs,
nuke support for them, and turn it into a hard error. Update the
testsuite to reflect tighter parsing rules.

Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Markus Armbruster <armbru@redhat.com>
2015-05-05 18:39:02 +02:00

369 lines
8.4 KiB
Python

#
# QAPI event generator
#
# Copyright (c) 2014 Wenchao Xia
#
# Authors:
# Wenchao Xia <wenchaoqemu@gmail.com>
#
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
from ordereddict import OrderedDict
from qapi import *
import sys
import os
import getopt
import errno
def _generate_event_api_name(event_name, params):
api_name = "void qapi_event_send_%s(" % c_fun(event_name).lower();
l = len(api_name)
if params:
for argname, argentry, optional in parse_args(params):
if optional:
api_name += "bool has_%s,\n" % c_var(argname)
api_name += "".ljust(l)
api_name += "%s %s,\n" % (c_type(argentry, is_param=True),
c_var(argname))
api_name += "".ljust(l)
api_name += "Error **errp)"
return api_name;
# Following are the core functions that generate C APIs to emit event.
def generate_event_declaration(api_name):
return mcgen('''
%(api_name)s;
''',
api_name = api_name)
def generate_event_implement(api_name, event_name, params):
# step 1: declare any variables
ret = mcgen("""
%(api_name)s
{
QDict *qmp;
Error *local_err = NULL;
QMPEventFuncEmit emit;
""",
api_name = api_name)
if params:
ret += mcgen("""
QmpOutputVisitor *qov;
Visitor *v;
QObject *obj;
""")
# step 2: check emit function, create a dict
ret += mcgen("""
emit = qmp_event_get_func_emit();
if (!emit) {
return;
}
qmp = qmp_event_build_dict("%(event_name)s");
""",
event_name = event_name)
# step 3: visit the params if params != None
if params:
ret += mcgen("""
qov = qmp_output_visitor_new();
g_assert(qov);
v = qmp_output_get_visitor(qov);
g_assert(v);
/* Fake visit, as if all members are under a structure */
visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err);
if (local_err) {
goto clean;
}
""",
event_name = event_name)
for argname, argentry, optional in parse_args(params):
if optional:
ret += mcgen("""
if (has_%(var)s) {
""",
var = c_var(argname))
push_indent()
if argentry == "str":
var_type = "(char **)"
else:
var_type = ""
ret += mcgen("""
visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err);
if (local_err) {
goto clean;
}
""",
var_type = var_type,
var = c_var(argname),
type = type_name(argentry),
name = argname)
if optional:
pop_indent()
ret += mcgen("""
}
""")
ret += mcgen("""
visit_end_struct(v, &local_err);
if (local_err) {
goto clean;
}
obj = qmp_output_get_qobject(qov);
g_assert(obj != NULL);
qdict_put_obj(qmp, "data", obj);
""")
# step 4: call qmp event api
ret += mcgen("""
emit(%(event_enum_value)s, qmp, &local_err);
""",
event_enum_value = event_enum_value)
# step 5: clean up
if params:
ret += mcgen("""
clean:
qmp_output_visitor_cleanup(qov);
""")
ret += mcgen("""
error_propagate(errp, local_err);
QDECREF(qmp);
}
""")
return ret
# Following are the functions that generate an enum type for all defined
# events, similar to qapi-types.py. Here we already have enum name and
# values which were generated before and recorded in event_enum_*. It also
# works around the issue that "import qapi-types" can't work.
def generate_event_enum_decl(event_enum_name, event_enum_values):
lookup_decl = mcgen('''
extern const char *%(event_enum_name)s_lookup[];
''',
event_enum_name = event_enum_name)
enum_decl = mcgen('''
typedef enum %(event_enum_name)s
{
''',
event_enum_name = event_enum_name)
# append automatically generated _MAX value
enum_max_value = generate_enum_full_value(event_enum_name, "MAX")
enum_values = event_enum_values + [ enum_max_value ]
i = 0
for value in enum_values:
enum_decl += mcgen('''
%(value)s = %(i)d,
''',
value = value,
i = i)
i += 1
enum_decl += mcgen('''
} %(event_enum_name)s;
''',
event_enum_name = event_enum_name)
return lookup_decl + enum_decl
def generate_event_enum_lookup(event_enum_name, event_enum_strings):
ret = mcgen('''
const char *%(event_enum_name)s_lookup[] = {
''',
event_enum_name = event_enum_name)
i = 0
for string in event_enum_strings:
ret += mcgen('''
"%(string)s",
''',
string = string)
ret += mcgen('''
NULL,
};
''')
return ret
# Start the real job
try:
opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:i:o:",
["source", "header", "builtins", "prefix=",
"input-file=", "output-dir="])
except getopt.GetoptError, err:
print str(err)
sys.exit(1)
input_file = ""
output_dir = ""
prefix = ""
c_file = 'qapi-event.c'
h_file = 'qapi-event.h'
do_c = False
do_h = False
do_builtins = False
for o, a in opts:
if o in ("-p", "--prefix"):
prefix = a
elif o in ("-i", "--input-file"):
input_file = a
elif o in ("-o", "--output-dir"):
output_dir = a + "/"
elif o in ("-c", "--source"):
do_c = True
elif o in ("-h", "--header"):
do_h = True
elif o in ("-b", "--builtins"):
do_builtins = True
if not do_c and not do_h:
do_c = True
do_h = True
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
def maybe_open(really, name, opt):
if really:
return open(name, opt)
else:
import StringIO
return StringIO.StringIO()
fdef = maybe_open(do_c, c_file, 'w')
fdecl = maybe_open(do_h, h_file, 'w')
fdef.write(mcgen('''
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
/*
* schema-defined QAPI event functions
*
* Copyright (c) 2014 Wenchao Xia
*
* Authors:
* Wenchao Xia <wenchaoqemu@gmail.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.
*
*/
#include "qemu-common.h"
#include "%(header)s"
#include "%(prefix)sqapi-visit.h"
#include "qapi/qmp-output-visitor.h"
#include "qapi/qmp-event.h"
''',
prefix=prefix, header=basename(h_file)))
fdecl.write(mcgen('''
/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
/*
* schema-defined QAPI event functions
*
* Copyright (c) 2014 Wenchao Xia
*
* Authors:
* Wenchao Xia <wenchaoqemu@gmail.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
#include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "%(prefix)sqapi-types.h"
''',
prefix=prefix, guard=guardname(h_file)))
exprs = parse_schema(input_file)
event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent"
event_enum_values = []
event_enum_strings = []
for expr in exprs:
if expr.has_key('event'):
event_name = expr['event']
params = expr.get('data')
if params and len(params) == 0:
params = None
api_name = _generate_event_api_name(event_name, params)
ret = generate_event_declaration(api_name)
fdecl.write(ret)
# We need an enum value per event
event_enum_value = generate_enum_full_value(event_enum_name,
event_name)
ret = generate_event_implement(api_name, event_name, params)
fdef.write(ret)
# Record it, and generate enum later
event_enum_values.append(event_enum_value)
event_enum_strings.append(event_name)
ret = generate_event_enum_decl(event_enum_name, event_enum_values)
fdecl.write(ret)
ret = generate_event_enum_lookup(event_enum_name, event_enum_strings)
fdef.write(ret)
fdecl.write('''
#endif
''')
fdecl.flush()
fdecl.close()
fdef.flush()
fdef.close()