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