As a followup to commit cbf2115
, clean up the includes in files
generated by QAPI so that osdep.h is included first in .c files,
and headers which it implies are not included manually. This
patch is done manually, since Coccinelle (and therefore
scripts/clean-includes) doesn't see into the generator scripts.
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Eric Blake <eblake@redhat.com>
459 lines
12 KiB
Python
459 lines
12 KiB
Python
#
|
|
# QAPI visitor generator
|
|
#
|
|
# Copyright IBM, Corp. 2011
|
|
# Copyright (C) 2014-2016 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
|
|
|
|
# visit_type_FOO_implicit() is emitted as needed; track if it has already
|
|
# been output.
|
|
implicit_structs_seen = set()
|
|
|
|
# visit_type_FOO_fields() is always emitted; track if a forward declaration
|
|
# or implementation has already been output.
|
|
struct_fields_seen = set()
|
|
|
|
|
|
def gen_visit_decl(name, scalar=False):
|
|
c_type = c_name(name) + ' *'
|
|
if not scalar:
|
|
c_type += '*'
|
|
return mcgen('''
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_type)sobj, Error **errp);
|
|
''',
|
|
c_name=c_name(name), c_type=c_type)
|
|
|
|
|
|
def gen_visit_fields_decl(typ):
|
|
ret = ''
|
|
if typ.name not in struct_fields_seen:
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_%(c_type)s_fields(Visitor *v, %(c_type)s **obj, Error **errp);
|
|
''',
|
|
c_type=typ.c_name())
|
|
struct_fields_seen.add(typ.name)
|
|
return ret
|
|
|
|
|
|
def gen_visit_implicit_struct(typ):
|
|
if typ in implicit_structs_seen:
|
|
return ''
|
|
implicit_structs_seen.add(typ)
|
|
|
|
ret = gen_visit_fields_decl(typ)
|
|
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_implicit_%(c_type)s(Visitor *v, %(c_type)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_implicit_struct(v, (void **)obj, sizeof(%(c_type)s), &err);
|
|
if (!err) {
|
|
visit_type_%(c_type)s_fields(v, obj, errp);
|
|
visit_end_implicit_struct(v);
|
|
}
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
c_type=typ.c_name())
|
|
return ret
|
|
|
|
|
|
def gen_visit_struct_fields(name, base, members):
|
|
ret = ''
|
|
|
|
if base:
|
|
ret += gen_visit_fields_decl(base)
|
|
|
|
struct_fields_seen.add(name)
|
|
ret += mcgen('''
|
|
|
|
static void visit_type_%(c_name)s_fields(Visitor *v, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
if base:
|
|
ret += mcgen('''
|
|
visit_type_%(c_type)s_fields(v, (%(c_type)s **)obj, &err);
|
|
''',
|
|
c_type=base.c_name())
|
|
ret += gen_err_check()
|
|
|
|
ret += gen_visit_fields(members, prefix='(*obj)->')
|
|
|
|
# 'goto out' produced for base, and by gen_visit_fields() for each member
|
|
if base or members:
|
|
ret += mcgen('''
|
|
|
|
out:
|
|
''')
|
|
ret += mcgen('''
|
|
error_propagate(errp, err);
|
|
}
|
|
''')
|
|
return ret
|
|
|
|
|
|
def gen_visit_struct(name, base, members):
|
|
ret = gen_visit_struct_fields(name, base, members)
|
|
|
|
# FIXME: if *obj is NULL on entry, and visit_start_struct() assigns to
|
|
# *obj, but then visit_type_FOO_fields() fails, we should clean up *obj
|
|
# rather than leaving it non-NULL. As currently written, the caller must
|
|
# call qapi_free_FOO() to avoid a memory leak of the partial FOO.
|
|
ret += mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
if (!*obj) {
|
|
goto out_obj;
|
|
}
|
|
visit_type_%(c_name)s_fields(v, obj, &err);
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
out_obj:
|
|
visit_end_struct(v, &err);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
return ret
|
|
|
|
|
|
def gen_visit_list(name, element_type):
|
|
# FIXME: if *obj is NULL on entry, and the first visit_next_list()
|
|
# assigns to *obj, while a later one fails, we should clean up *obj
|
|
# rather than leaving it non-NULL. As currently written, the caller must
|
|
# call qapi_free_FOOList() to avoid a memory leak of the partial FOOList.
|
|
return mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
GenericList *i, **prev;
|
|
|
|
visit_start_list(v, name, &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
|
|
for (prev = (GenericList **)obj;
|
|
!err && (i = visit_next_list(v, prev)) != NULL;
|
|
prev = &i) {
|
|
%(c_name)s *native_i = (%(c_name)s *)i;
|
|
visit_type_%(c_elt_type)s(v, NULL, &native_i->value, &err);
|
|
}
|
|
|
|
visit_end_list(v);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
c_name=c_name(name), c_elt_type=element_type.c_name())
|
|
|
|
|
|
def gen_visit_enum(name):
|
|
return mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s *obj, Error **errp)
|
|
{
|
|
int value = *obj;
|
|
visit_type_enum(v, name, &value, %(c_name)s_lookup, errp);
|
|
*obj = value;
|
|
}
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
|
|
def gen_visit_alternate(name, variants):
|
|
promote_int = 'true'
|
|
for var in variants.variants:
|
|
if var.type.alternate_qtype() == 'QTYPE_QINT':
|
|
promote_int = 'false'
|
|
|
|
ret = mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_implicit_struct(v, (void**) obj, sizeof(%(c_name)s), &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
visit_get_next_type(v, name, &(*obj)->type, %(promote_int)s, &err);
|
|
if (err) {
|
|
goto out_obj;
|
|
}
|
|
switch ((*obj)->type) {
|
|
''',
|
|
c_name=c_name(name), promote_int=promote_int)
|
|
|
|
for var in variants.variants:
|
|
ret += mcgen('''
|
|
case %(case)s:
|
|
visit_type_%(c_type)s(v, name, &(*obj)->u.%(c_name)s, &err);
|
|
break;
|
|
''',
|
|
case=var.type.alternate_qtype(),
|
|
c_type=var.type.c_name(),
|
|
c_name=c_name(var.name))
|
|
|
|
ret += mcgen('''
|
|
default:
|
|
error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
"%(name)s");
|
|
}
|
|
out_obj:
|
|
visit_end_implicit_struct(v);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''',
|
|
name=name)
|
|
|
|
return ret
|
|
|
|
|
|
def gen_visit_union(name, base, variants):
|
|
ret = ''
|
|
|
|
if base:
|
|
ret += gen_visit_fields_decl(base)
|
|
|
|
for var in variants.variants:
|
|
# Ugly special case for simple union TODO get rid of it
|
|
if not var.simple_union_type():
|
|
ret += gen_visit_implicit_struct(var.type)
|
|
|
|
ret += mcgen('''
|
|
|
|
void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error **errp)
|
|
{
|
|
Error *err = NULL;
|
|
|
|
visit_start_struct(v, name, (void **)obj, sizeof(%(c_name)s), &err);
|
|
if (err) {
|
|
goto out;
|
|
}
|
|
if (!*obj) {
|
|
goto out_obj;
|
|
}
|
|
''',
|
|
c_name=c_name(name))
|
|
|
|
if base:
|
|
ret += mcgen('''
|
|
visit_type_%(c_name)s_fields(v, (%(c_name)s **)obj, &err);
|
|
''',
|
|
c_name=base.c_name())
|
|
else:
|
|
ret += mcgen('''
|
|
visit_type_%(c_type)s(v, "%(name)s", &(*obj)->%(c_name)s, &err);
|
|
''',
|
|
c_type=variants.tag_member.type.c_name(),
|
|
c_name=c_name(variants.tag_member.name),
|
|
name=variants.tag_member.name)
|
|
ret += gen_err_check(label='out_obj')
|
|
ret += mcgen('''
|
|
if (!visit_start_union(v, !!(*obj)->u.data, &err) || err) {
|
|
goto out_obj;
|
|
}
|
|
switch ((*obj)->%(c_name)s) {
|
|
''',
|
|
c_name=c_name(variants.tag_member.name))
|
|
|
|
for var in variants.variants:
|
|
# TODO ugly special case for simple union
|
|
simple_union_type = var.simple_union_type()
|
|
ret += mcgen('''
|
|
case %(case)s:
|
|
''',
|
|
case=c_enum_const(variants.tag_member.type.name,
|
|
var.name))
|
|
if simple_union_type:
|
|
ret += mcgen('''
|
|
visit_type_%(c_type)s(v, "data", &(*obj)->u.%(c_name)s, &err);
|
|
''',
|
|
c_type=simple_union_type.c_name(),
|
|
c_name=c_name(var.name))
|
|
else:
|
|
ret += mcgen('''
|
|
visit_type_implicit_%(c_type)s(v, &(*obj)->u.%(c_name)s, &err);
|
|
''',
|
|
c_type=var.type.c_name(),
|
|
c_name=c_name(var.name))
|
|
ret += mcgen('''
|
|
break;
|
|
''')
|
|
|
|
ret += mcgen('''
|
|
default:
|
|
abort();
|
|
}
|
|
out_obj:
|
|
error_propagate(errp, err);
|
|
err = NULL;
|
|
visit_end_struct(v, &err);
|
|
out:
|
|
error_propagate(errp, err);
|
|
}
|
|
''')
|
|
|
|
return ret
|
|
|
|
|
|
class QAPISchemaGenVisitVisitor(QAPISchemaVisitor):
|
|
def __init__(self):
|
|
self.decl = None
|
|
self.defn = None
|
|
self._btin = None
|
|
|
|
def visit_begin(self, schema):
|
|
self.decl = ''
|
|
self.defn = ''
|
|
self._btin = guardstart('QAPI_VISIT_BUILTIN')
|
|
|
|
def visit_end(self):
|
|
# To avoid header dependency hell, we always generate
|
|
# declarations for built-in types in our header files and
|
|
# simply guard them. See also do_builtins (command line
|
|
# option -b).
|
|
self._btin += guardend('QAPI_VISIT_BUILTIN')
|
|
self.decl = self._btin + self.decl
|
|
self._btin = None
|
|
|
|
def visit_needed(self, entity):
|
|
# Visit everything except implicit objects
|
|
return not (entity.is_implicit() and
|
|
isinstance(entity, QAPISchemaObjectType))
|
|
|
|
def visit_enum_type(self, name, info, values, prefix):
|
|
# Special case for our lone builtin enum type
|
|
# TODO use something cleaner than existence of info
|
|
if not info:
|
|
self._btin += gen_visit_decl(name, scalar=True)
|
|
if do_builtins:
|
|
self.defn += gen_visit_enum(name)
|
|
else:
|
|
self.decl += gen_visit_decl(name, scalar=True)
|
|
self.defn += gen_visit_enum(name)
|
|
|
|
def visit_array_type(self, name, info, element_type):
|
|
decl = gen_visit_decl(name)
|
|
defn = gen_visit_list(name, element_type)
|
|
if isinstance(element_type, QAPISchemaBuiltinType):
|
|
self._btin += decl
|
|
if do_builtins:
|
|
self.defn += defn
|
|
else:
|
|
self.decl += decl
|
|
self.defn += defn
|
|
|
|
def visit_object_type(self, name, info, base, members, variants):
|
|
self.decl += gen_visit_decl(name)
|
|
if variants:
|
|
if members:
|
|
# Members other than variants.tag_member not implemented
|
|
assert len(members) == 1
|
|
assert members[0] == variants.tag_member
|
|
self.defn += gen_visit_union(name, base, variants)
|
|
else:
|
|
self.defn += gen_visit_struct(name, base, members)
|
|
|
|
def visit_alternate_type(self, name, info, variants):
|
|
self.decl += gen_visit_decl(name)
|
|
self.defn += gen_visit_alternate(name, variants)
|
|
|
|
# If you link code generated from multiple schemata, you want only one
|
|
# instance of the code for built-in types. Generate it only when
|
|
# do_builtins, enabled by command line option -b. See also
|
|
# QAPISchemaGenVisitVisitor.visit_end().
|
|
do_builtins = False
|
|
|
|
(input_file, output_dir, do_c, do_h, prefix, opts) = \
|
|
parse_command_line("b", ["builtins"])
|
|
|
|
for o, a in opts:
|
|
if o in ("-b", "--builtins"):
|
|
do_builtins = True
|
|
|
|
c_comment = '''
|
|
/*
|
|
* schema-defined QAPI visitor functions
|
|
*
|
|
* 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 visitor functions
|
|
*
|
|
* 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,
|
|
'qapi-visit.c', 'qapi-visit.h',
|
|
c_comment, h_comment)
|
|
|
|
fdef.write(mcgen('''
|
|
#include "qemu/osdep.h"
|
|
#include "qemu-common.h"
|
|
#include "%(prefix)sqapi-visit.h"
|
|
''',
|
|
prefix=prefix))
|
|
|
|
fdecl.write(mcgen('''
|
|
#include "qapi/visitor.h"
|
|
#include "qapi/qmp/qerror.h"
|
|
#include "%(prefix)sqapi-types.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
schema = QAPISchema(input_file)
|
|
gen = QAPISchemaGenVisitVisitor()
|
|
schema.visit(gen)
|
|
fdef.write(gen.defn)
|
|
fdecl.write(gen.decl)
|
|
|
|
close_output(fdef, fdecl)
|