xref: /qemu/scripts/qapi/visit.py (revision be3c771796cb5ca4e7de2e04ed3cd2e3b6b76185)
1#
2# QAPI visitor generator
3#
4# Copyright IBM, Corp. 2011
5#
6# Authors:
7#  Anthony Liguori <aliguori@us.ibm.com>
8#  Michael Roth    <mdroth@linux.vnet.ibm.com>
9#
10# This work is licensed under the terms of the GNU GPL, version 2.
11# See the COPYING file in the top-level directory.
12
13from ordereddict import OrderedDict
14from qapi import *
15import sys
16import os
17import getopt
18import errno
19
20implicit_structs = []
21
22def generate_visit_implicit_struct(type):
23    global implicit_structs
24    if type in implicit_structs:
25        return ''
26    implicit_structs.append(type)
27    return mcgen('''
28
29static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error **errp)
30{
31    Error *err = NULL;
32
33    visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
34    if (!err) {
35        visit_type_%(c_type)s_fields(m, obj, &err);
36        error_propagate(errp, err);
37        err = NULL;
38        visit_end_implicit_struct(m, &err);
39    }
40    error_propagate(errp, err);
41}
42''',
43                 c_type=type_name(type))
44
45def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
46    substructs = []
47    ret = ''
48    if not fn_prefix:
49        full_name = name
50    else:
51        full_name = "%s_%s" % (name, fn_prefix)
52
53    for argname, argentry, optional, structured in parse_args(members):
54        if structured:
55            if not fn_prefix:
56                nested_fn_prefix = argname
57            else:
58                nested_fn_prefix = "%s_%s" % (fn_prefix, argname)
59
60            nested_field_prefix = "%s%s." % (field_prefix, argname)
61            ret += generate_visit_struct_fields(name, nested_field_prefix,
62                                                nested_fn_prefix, argentry)
63            ret += mcgen('''
64
65static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
66{
67    Error *err = NULL;
68''',
69                         name=name, full_name=full_name, c_name=c_var(argname))
70            push_indent()
71            ret += generate_visit_struct_body(full_name, argname, argentry)
72            pop_indent()
73            ret += mcgen('''
74}
75''')
76
77    if base:
78        ret += generate_visit_implicit_struct(base)
79
80    ret += mcgen('''
81
82static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error **errp)
83{
84    Error *err = NULL;
85''',
86        name=name, full_name=full_name)
87    push_indent()
88
89    if base:
90        ret += mcgen('''
91visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
92''',
93                     c_prefix=c_var(field_prefix),
94                     type=type_name(base), c_name=c_var('base'))
95
96    for argname, argentry, optional, structured in parse_args(members):
97        if optional:
98            ret += mcgen('''
99visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
100if ((*obj)->%(prefix)shas_%(c_name)s) {
101''',
102                         c_prefix=c_var(field_prefix), prefix=field_prefix,
103                         c_name=c_var(argname), name=argname)
104            push_indent()
105
106        if structured:
107            ret += mcgen('''
108visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
109''',
110                         full_name=full_name, c_name=c_var(argname))
111        else:
112            ret += mcgen('''
113visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
114''',
115                         c_prefix=c_var(field_prefix), prefix=field_prefix,
116                         type=type_name(argentry), c_name=c_var(argname),
117                         name=argname)
118
119        if optional:
120            pop_indent()
121            ret += mcgen('''
122}
123''')
124
125    pop_indent()
126    ret += mcgen('''
127
128    error_propagate(errp, err);
129}
130''')
131    return ret
132
133
134def generate_visit_struct_body(field_prefix, name, members):
135    ret = mcgen('''
136if (!error_is_set(errp)) {
137''')
138    push_indent()
139
140    if not field_prefix:
141        full_name = name
142    else:
143        full_name = "%s_%s" % (field_prefix, name)
144
145    if len(field_prefix):
146        ret += mcgen('''
147visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
148''',
149                name=name)
150    else:
151        ret += mcgen('''
152Error *err = NULL;
153visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
154''',
155                name=name)
156
157    ret += mcgen('''
158if (!err) {
159    if (*obj) {
160        visit_type_%(name)s_fields(m, obj, &err);
161        error_propagate(errp, err);
162        err = NULL;
163    }
164''',
165        name=full_name)
166
167    ret += mcgen('''
168    /* Always call end_struct if start_struct succeeded.  */
169    visit_end_struct(m, &err);
170}
171error_propagate(errp, err);
172''')
173    pop_indent()
174    ret += mcgen('''
175}
176''')
177    return ret
178
179def generate_visit_struct(expr):
180
181    name = expr['type']
182    members = expr['data']
183    base = expr.get('base')
184
185    ret = generate_visit_struct_fields(name, "", "", members, base)
186
187    ret += mcgen('''
188
189void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
190{
191''',
192                name=name)
193
194    push_indent()
195    ret += generate_visit_struct_body("", name, members)
196    pop_indent()
197
198    ret += mcgen('''
199}
200''')
201    return ret
202
203def generate_visit_list(name, members):
204    return mcgen('''
205
206void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
207{
208    GenericList *i, **prev = (GenericList **)obj;
209    Error *err = NULL;
210
211    if (!error_is_set(errp)) {
212        visit_start_list(m, name, &err);
213        if (!err) {
214            for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
215                %(name)sList *native_i = (%(name)sList *)i;
216                visit_type_%(name)s(m, &native_i->value, NULL, &err);
217            }
218            error_propagate(errp, err);
219            err = NULL;
220
221            /* Always call end_list if start_list succeeded.  */
222            visit_end_list(m, &err);
223        }
224        error_propagate(errp, err);
225    }
226}
227''',
228                name=name)
229
230def generate_visit_enum(name, members):
231    return mcgen('''
232
233void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp)
234{
235    visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
236}
237''',
238                 name=name)
239
240def generate_visit_anon_union(name, members):
241    ret = mcgen('''
242
243void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
244{
245    Error *err = NULL;
246
247    if (!error_is_set(errp)) {
248        visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
249        visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
250        switch ((*obj)->kind) {
251''',
252    name=name)
253
254    # For anon union, always use the default enum type automatically generated
255    # as "'%sKind' % (name)"
256    disc_type = '%sKind' % (name)
257
258    for key in members:
259        assert (members[key] in builtin_types
260            or find_struct(members[key])
261            or find_union(members[key])), "Invalid anonymous union member"
262
263        enum_full_value = generate_enum_full_value(disc_type, key)
264        ret += mcgen('''
265        case %(enum_full_value)s:
266            visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
267            break;
268''',
269                enum_full_value = enum_full_value,
270                c_type = type_name(members[key]),
271                c_name = c_fun(key))
272
273    ret += mcgen('''
274        default:
275            abort();
276        }
277        error_propagate(errp, err);
278        err = NULL;
279        visit_end_implicit_struct(m, &err);
280    }
281}
282''')
283
284    return ret
285
286
287def generate_visit_union(expr):
288
289    name = expr['union']
290    members = expr['data']
291
292    base = expr.get('base')
293    discriminator = expr.get('discriminator')
294
295    if discriminator == {}:
296        assert not base
297        return generate_visit_anon_union(name, members)
298
299    enum_define = discriminator_find_enum_define(expr)
300    if enum_define:
301        # Use the enum type as discriminator
302        ret = ""
303        disc_type = enum_define['enum_name']
304    else:
305        # There will always be a discriminator in the C switch code, by default it
306        # is an enum type generated silently as "'%sKind' % (name)"
307        ret = generate_visit_enum('%sKind' % name, members.keys())
308        disc_type = '%sKind' % (name)
309
310    if base:
311        base_fields = find_struct(base)['data']
312        if discriminator:
313            base_fields = base_fields.copy()
314            del base_fields[discriminator]
315        ret += generate_visit_struct_fields(name, "", "", base_fields)
316
317    if discriminator:
318        for key in members:
319            ret += generate_visit_implicit_struct(members[key])
320
321    ret += mcgen('''
322
323void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
324{
325    Error *err = NULL;
326
327    if (!error_is_set(errp)) {
328        visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
329        if (!err) {
330            if (*obj) {
331''',
332                 name=name)
333
334    push_indent()
335    push_indent()
336
337    if base:
338        ret += mcgen('''
339        visit_type_%(name)s_fields(m, obj, &err);
340''',
341            name=name)
342
343    if not discriminator:
344        disc_key = "type"
345    else:
346        disc_key = discriminator
347    ret += mcgen('''
348        visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err);
349        if (!err) {
350            switch ((*obj)->kind) {
351''',
352                 disc_type = disc_type,
353                 disc_key = disc_key)
354
355    for key in members:
356        if not discriminator:
357            fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
358        else:
359            fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);'
360
361        enum_full_value = generate_enum_full_value(disc_type, key)
362        ret += mcgen('''
363            case %(enum_full_value)s:
364                ''' + fmt + '''
365                break;
366''',
367                enum_full_value = enum_full_value,
368                c_type=type_name(members[key]),
369                c_name=c_fun(key))
370
371    ret += mcgen('''
372            default:
373                abort();
374            }
375        }
376        error_propagate(errp, err);
377        err = NULL;
378''')
379    pop_indent()
380    pop_indent()
381
382    ret += mcgen('''
383            }
384            /* Always call end_struct if start_struct succeeded.  */
385            visit_end_struct(m, &err);
386        }
387        error_propagate(errp, err);
388    }
389}
390''')
391
392    return ret
393
394def generate_declaration(name, members, genlist=True, builtin_type=False):
395    ret = ""
396    if not builtin_type:
397        ret += mcgen('''
398
399void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp);
400''',
401                    name=name)
402
403    if genlist:
404        ret += mcgen('''
405void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
406''',
407                 name=name)
408
409    return ret
410
411def generate_enum_declaration(name, members, genlist=True):
412    ret = ""
413    if genlist:
414        ret += mcgen('''
415void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
416''',
417                     name=name)
418
419    return ret
420
421def generate_decl_enum(name, members, genlist=True):
422    return mcgen('''
423
424void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
425''',
426                name=name)
427
428try:
429    opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:i:o:",
430                                   ["source", "header", "builtins", "prefix=",
431                                    "input-file=", "output-dir="])
432except getopt.GetoptError, err:
433    print str(err)
434    sys.exit(1)
435
436input_file = ""
437output_dir = ""
438prefix = ""
439c_file = 'qapi-visit.c'
440h_file = 'qapi-visit.h'
441
442do_c = False
443do_h = False
444do_builtins = False
445
446for o, a in opts:
447    if o in ("-p", "--prefix"):
448        prefix = a
449    elif o in ("-i", "--input-file"):
450        input_file = a
451    elif o in ("-o", "--output-dir"):
452        output_dir = a + "/"
453    elif o in ("-c", "--source"):
454        do_c = True
455    elif o in ("-h", "--header"):
456        do_h = True
457    elif o in ("-b", "--builtins"):
458        do_builtins = True
459
460if not do_c and not do_h:
461    do_c = True
462    do_h = True
463
464c_file = output_dir + prefix + c_file
465h_file = output_dir + prefix + h_file
466
467try:
468    os.makedirs(output_dir)
469except os.error, e:
470    if e.errno != errno.EEXIST:
471        raise
472
473def maybe_open(really, name, opt):
474    if really:
475        return open(name, opt)
476    else:
477        import StringIO
478        return StringIO.StringIO()
479
480fdef = maybe_open(do_c, c_file, 'w')
481fdecl = maybe_open(do_h, h_file, 'w')
482
483fdef.write(mcgen('''
484/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
485
486/*
487 * schema-defined QAPI visitor functions
488 *
489 * Copyright IBM, Corp. 2011
490 *
491 * Authors:
492 *  Anthony Liguori   <aliguori@us.ibm.com>
493 *
494 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
495 * See the COPYING.LIB file in the top-level directory.
496 *
497 */
498
499#include "qemu-common.h"
500#include "%(header)s"
501''',
502                 header=basename(h_file)))
503
504fdecl.write(mcgen('''
505/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
506
507/*
508 * schema-defined QAPI visitor function
509 *
510 * Copyright IBM, Corp. 2011
511 *
512 * Authors:
513 *  Anthony Liguori   <aliguori@us.ibm.com>
514 *
515 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
516 * See the COPYING.LIB file in the top-level directory.
517 *
518 */
519
520#ifndef %(guard)s
521#define %(guard)s
522
523#include "qapi/visitor.h"
524#include "%(prefix)sqapi-types.h"
525
526''',
527                  prefix=prefix, guard=guardname(h_file)))
528
529exprs = parse_schema(input_file)
530
531# to avoid header dependency hell, we always generate declarations
532# for built-in types in our header files and simply guard them
533fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
534for typename in builtin_types:
535    fdecl.write(generate_declaration(typename, None, genlist=True,
536                                     builtin_type=True))
537fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
538
539# ...this doesn't work for cases where we link in multiple objects that
540# have the functions defined, so we use -b option to provide control
541# over these cases
542if do_builtins:
543    for typename in builtin_types:
544        fdef.write(generate_visit_list(typename, None))
545
546for expr in exprs:
547    if expr.has_key('type'):
548        ret = generate_visit_struct(expr)
549        ret += generate_visit_list(expr['type'], expr['data'])
550        fdef.write(ret)
551
552        ret = generate_declaration(expr['type'], expr['data'])
553        fdecl.write(ret)
554    elif expr.has_key('union'):
555        ret = generate_visit_union(expr)
556        ret += generate_visit_list(expr['union'], expr['data'])
557        fdef.write(ret)
558
559        enum_define = discriminator_find_enum_define(expr)
560        ret = ""
561        if not enum_define:
562            ret = generate_decl_enum('%sKind' % expr['union'],
563                                     expr['data'].keys())
564        ret += generate_declaration(expr['union'], expr['data'])
565        fdecl.write(ret)
566    elif expr.has_key('enum'):
567        ret = generate_visit_list(expr['enum'], expr['data'])
568        ret += generate_visit_enum(expr['enum'], expr['data'])
569        fdef.write(ret)
570
571        ret = generate_decl_enum(expr['enum'], expr['data'])
572        ret += generate_enum_declaration(expr['enum'], expr['data'])
573        fdecl.write(ret)
574
575fdecl.write('''
576#endif
577''')
578
579fdecl.flush()
580fdecl.close()
581
582fdef.flush()
583fdef.close()
584