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