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