qapi-types.py 8.1 KB
Newer Older
1 2 3 4
#
# QAPI types generator
#
# Copyright IBM, Corp. 2011
5
# Copyright (c) 2013-2016 Red Hat Inc.
6 7 8
#
# Authors:
#  Anthony Liguori <aliguori@us.ibm.com>
9
#  Markus Armbruster <armbru@redhat.com>
10
#
11 12
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
13 14 15

from qapi import *

16

17 18 19 20 21
# variants must be emitted before their container; track what has already
# been output
objects_seen = set()


22
def gen_fwd_object_or_array(name):
23
    return mcgen('''
24

25
typedef struct %(c_name)s %(c_name)s;
26
''',
27 28
                 c_name=c_name(name))

29

30
def gen_array(name, element_type):
31
    return mcgen('''
M
Markus Armbruster 已提交
32

33 34
struct %(c_name)s {
    %(c_name)s *next;
35
    %(c_type)s value;
36
};
37
''',
38 39
                 c_name=c_name(name), c_type=element_type.c_type())

40

41
def gen_struct_members(members):
42
    ret = ''
43 44 45
    for memb in members:
        if memb.optional:
            ret += mcgen('''
46 47
    bool has_%(c_name)s;
''',
48 49
                         c_name=c_name(memb.name))
        ret += mcgen('''
50 51
    %(c_type)s %(c_name)s;
''',
52
                     c_type=memb.type.c_type(), c_name=c_name(memb.name))
53 54
    return ret

55

56
def gen_object(name, base, members, variants):
57 58 59 60 61 62 63 64 65 66 67 68 69
    if name in objects_seen:
        return ''
    objects_seen.add(name)

    ret = ''
    if variants:
        for v in variants.variants:
            if (isinstance(v.type, QAPISchemaObjectType) and
                    not v.type.is_implicit()):
                ret += gen_object(v.type.name, v.type.base,
                                  v.type.local_members, v.type.variants)

    ret += mcgen('''
70 71 72

struct %(c_name)s {
''',
73
                 c_name=c_name(name))
74

75 76 77 78 79
    if base:
        ret += mcgen('''
    /* Members inherited from %(c_name)s: */
''',
                     c_name=base.c_name())
80
        ret += gen_struct_members(base.members)
81 82 83
        ret += mcgen('''
    /* Own members: */
''')
84
    ret += gen_struct_members(members)
85

86 87 88
    if variants:
        ret += gen_variants(variants)

89
    # Make sure that all structs have at least one member; this avoids
90 91 92
    # potential issues with attempting to malloc space for zero-length
    # structs in C, and also incompatibility with C++ (where an empty
    # struct is size 1).
93
    if not (base and base.members) and not members and not variants:
94
        ret += mcgen('''
95
    char qapi_dummy_for_empty_struct;
96 97
''')

98
    ret += mcgen('''
99 100
};
''')
101 102 103

    return ret

104

105 106 107 108 109 110 111 112 113 114 115 116 117
def gen_upcast(name, base):
    # C makes const-correctness ugly.  We have to cast away const to let
    # this function work for both const and non-const obj.
    return mcgen('''

static inline %(base)s *qapi_%(c_name)s_base(const %(c_name)s *obj)
{
    return (%(base)s *)obj;
}
''',
                 c_name=c_name(name), base=base.c_name())


118 119
def gen_variants(variants):
    ret = mcgen('''
120
    union { /* union tag is @%(c_name)s */
121
''',
122
                c_name=c_name(variants.tag_member.name))
123 124 125

    for var in variants.variants:
        # Ugly special case for simple union TODO get rid of it
126
        simple_union_type = var.simple_union_type()
E
Eric Blake 已提交
127 128 129 130
        if simple_union_type:
            typ = simple_union_type.c_type()
        else:
            typ = var.type.c_unboxed_type()
131
        ret += mcgen('''
132 133
        %(c_type)s %(c_name)s;
''',
E
Eric Blake 已提交
134
                     c_type=typ,
135
                     c_name=c_name(var.name))
136 137

    ret += mcgen('''
138
    } u;
139 140 141 142
''')

    return ret

143 144

def gen_type_cleanup_decl(name):
145
    ret = mcgen('''
146

147
void qapi_free_%(c_name)s(%(c_name)s *obj);
148
''',
149
                c_name=c_name(name))
150 151
    return ret

152 153

def gen_type_cleanup(name):
154
    ret = mcgen('''
155

156
void qapi_free_%(c_name)s(%(c_name)s *obj)
157
{
158
    QapiDeallocVisitor *qdv;
159 160 161 162 163 164
    Visitor *v;

    if (!obj) {
        return;
    }

165 166
    qdv = qapi_dealloc_visitor_new();
    v = qapi_dealloc_get_visitor(qdv);
167
    visit_type_%(c_name)s(v, NULL, &obj, NULL);
168
    qapi_dealloc_visitor_cleanup(qdv);
169 170
}
''',
171
                c_name=c_name(name))
172 173
    return ret

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198

class QAPISchemaGenTypeVisitor(QAPISchemaVisitor):
    def __init__(self):
        self.decl = None
        self.defn = None
        self._fwdecl = None
        self._btin = None

    def visit_begin(self, schema):
        self.decl = ''
        self.defn = ''
        self._fwdecl = ''
        self._btin = guardstart('QAPI_TYPES_BUILTIN')

    def visit_end(self):
        self.decl = self._fwdecl + self.decl
        self._fwdecl = None
        # 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_TYPES_BUILTIN')
        self.decl = self._btin + self.decl
        self._btin = None

199 200
    def visit_needed(self, entity):
        # Visit everything except implicit objects
201 202
        return not (entity.is_implicit() and
                    isinstance(entity, QAPISchemaObjectType))
203

204
    def _gen_type_cleanup(self, name):
205 206
        self.decl += gen_type_cleanup_decl(name)
        self.defn += gen_type_cleanup(name)
207 208

    def visit_enum_type(self, name, info, values, prefix):
209 210 211 212 213 214 215 216
        # Special case for our lone builtin enum type
        # TODO use something cleaner than existence of info
        if not info:
            self._btin += gen_enum(name, values, prefix)
            if do_builtins:
                self.defn += gen_enum_lookup(name, values, prefix)
        else:
            self._fwdecl += gen_enum(name, values, prefix)
E
Eric Blake 已提交
217
            self.defn += gen_enum_lookup(name, values, prefix)
218 219 220 221 222

    def visit_array_type(self, name, info, element_type):
        if isinstance(element_type, QAPISchemaBuiltinType):
            self._btin += gen_fwd_object_or_array(name)
            self._btin += gen_array(name, element_type)
223
            self._btin += gen_type_cleanup_decl(name)
224
            if do_builtins:
225
                self.defn += gen_type_cleanup(name)
226 227 228 229 230 231
        else:
            self._fwdecl += gen_fwd_object_or_array(name)
            self.decl += gen_array(name, element_type)
            self._gen_type_cleanup(name)

    def visit_object_type(self, name, info, base, members, variants):
232
        self._fwdecl += gen_fwd_object_or_array(name)
233
        self.decl += gen_object(name, base, members, variants)
E
Eric Blake 已提交
234 235
        if base:
            self.decl += gen_upcast(name, base)
236
        self._gen_type_cleanup(name)
237 238 239

    def visit_alternate_type(self, name, info, variants):
        self._fwdecl += gen_fwd_object_or_array(name)
240
        self.decl += gen_object(name, None, [variants.tag_member], variants)
241 242 243 244 245 246
        self._gen_type_cleanup(name)

# 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
# QAPISchemaGenTypeVisitor.visit_end().
247
do_builtins = False
248

249 250 251
(input_file, output_dir, do_c, do_h, prefix, opts) = \
    parse_command_line("b", ["builtins"])

252
for o, a in opts:
253
    if o in ("-b", "--builtins"):
254
        do_builtins = True
255

256
c_comment = '''
257 258 259 260 261 262 263 264 265 266 267 268 269
/*
 * deallocation functions for schema-defined QAPI types
 *
 * Copyright IBM, Corp. 2011
 *
 * Authors:
 *  Anthony Liguori   <aliguori@us.ibm.com>
 *  Michael Roth      <mdroth@linux.vnet.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.
 *
 */
270 271
'''
h_comment = '''
272 273 274 275 276 277 278 279 280 281 282 283
/*
 * schema-defined QAPI types
 *
 * 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.
 *
 */
284
'''
285

286 287 288
(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix,
                            'qapi-types.c', 'qapi-types.h',
                            c_comment, h_comment)
289

290
fdef.write(mcgen('''
291
#include "qemu/osdep.h"
292 293 294 295 296 297
#include "qapi/dealloc-visitor.h"
#include "%(prefix)sqapi-types.h"
#include "%(prefix)sqapi-visit.h"
''',
                 prefix=prefix))

298
# To avoid circular headers, use only typedefs.h here, not qobject.h
299
fdecl.write(mcgen('''
300
#include "qemu/typedefs.h"
301
'''))
302

303 304 305 306 307
schema = QAPISchema(input_file)
gen = QAPISchemaGenTypeVisitor()
schema.visit(gen)
fdef.write(gen.defn)
fdecl.write(gen.decl)
308

309
close_output(fdef, fdecl)