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