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