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