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 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['struct'] 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_alternate(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 alternate, 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.keys() 265 or find_struct(members[key]) 266 or find_union(members[key]) 267 or find_enum(members[key])), "Invalid alternate member" 268 269 enum_full_value = generate_enum_full_value(disc_type, key) 270 ret += mcgen(''' 271 case %(enum_full_value)s: 272 visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err); 273 break; 274''', 275 enum_full_value = enum_full_value, 276 c_type = type_name(members[key]), 277 c_name = c_fun(key)) 278 279 ret += mcgen(''' 280 default: 281 abort(); 282 } 283out_end: 284 error_propagate(errp, err); 285 err = NULL; 286 visit_end_implicit_struct(m, &err); 287out: 288 error_propagate(errp, err); 289} 290''') 291 292 return ret 293 294 295def generate_visit_union(expr): 296 297 name = expr['union'] 298 members = expr['data'] 299 300 base = expr.get('base') 301 discriminator = expr.get('discriminator') 302 303 enum_define = discriminator_find_enum_define(expr) 304 if enum_define: 305 # Use the enum type as discriminator 306 ret = "" 307 disc_type = enum_define['enum_name'] 308 else: 309 # There will always be a discriminator in the C switch code, by default 310 # it is an enum type generated silently as "'%sKind' % (name)" 311 ret = generate_visit_enum('%sKind' % name, members.keys()) 312 disc_type = '%sKind' % (name) 313 314 if base: 315 assert discriminator 316 base_fields = find_struct(base)['data'].copy() 317 del base_fields[discriminator] 318 ret += generate_visit_struct_fields(name, "", "", base_fields) 319 320 if discriminator: 321 for key in members: 322 ret += generate_visit_implicit_struct(members[key]) 323 324 ret += mcgen(''' 325 326void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp) 327{ 328 Error *err = NULL; 329 330 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err); 331 if (err) { 332 goto out; 333 } 334 if (*obj) { 335''', 336 name=name) 337 338 if base: 339 ret += mcgen(''' 340 visit_type_%(name)s_fields(m, obj, &err); 341 if (err) { 342 goto out_obj; 343 } 344''', 345 name=name) 346 347 if not discriminator: 348 disc_key = "type" 349 else: 350 disc_key = discriminator 351 ret += mcgen(''' 352 visit_type_%(disc_type)s(m, &(*obj)->kind, "%(disc_key)s", &err); 353 if (err) { 354 goto out_obj; 355 } 356 if (!visit_start_union(m, !!(*obj)->data, &err) || err) { 357 goto out_obj; 358 } 359 switch ((*obj)->kind) { 360''', 361 disc_type = disc_type, 362 disc_key = disc_key) 363 364 for key in members: 365 if not discriminator: 366 fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);' 367 else: 368 fmt = 'visit_type_implicit_%(c_type)s(m, &(*obj)->%(c_name)s, &err);' 369 370 enum_full_value = generate_enum_full_value(disc_type, key) 371 ret += mcgen(''' 372 case %(enum_full_value)s: 373 ''' + fmt + ''' 374 break; 375''', 376 enum_full_value = enum_full_value, 377 c_type=type_name(members[key]), 378 c_name=c_fun(key)) 379 380 ret += mcgen(''' 381 default: 382 abort(); 383 } 384out_obj: 385 error_propagate(errp, err); 386 err = NULL; 387 visit_end_union(m, !!(*obj)->data, &err); 388 error_propagate(errp, err); 389 err = NULL; 390 } 391 visit_end_struct(m, &err); 392out: 393 error_propagate(errp, err); 394} 395''') 396 397 return ret 398 399def generate_declaration(name, members, builtin_type=False): 400 ret = "" 401 if not builtin_type: 402 ret += mcgen(''' 403 404void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp); 405''', 406 name=name) 407 408 ret += mcgen(''' 409void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); 410''', 411 name=name) 412 413 return ret 414 415def generate_enum_declaration(name, members): 416 ret = mcgen(''' 417void visit_type_%(name)sList(Visitor *m, %(name)sList **obj, const char *name, Error **errp); 418''', 419 name=name) 420 421 return ret 422 423def generate_decl_enum(name, members): 424 return mcgen(''' 425 426void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **errp); 427''', 428 name=name) 429 430try: 431 opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:i:o:", 432 ["source", "header", "builtins", "prefix=", 433 "input-file=", "output-dir="]) 434except getopt.GetoptError, err: 435 print str(err) 436 sys.exit(1) 437 438input_file = "" 439output_dir = "" 440prefix = "" 441c_file = 'qapi-visit.c' 442h_file = 'qapi-visit.h' 443 444do_c = False 445do_h = False 446do_builtins = False 447 448for o, a in opts: 449 if o in ("-p", "--prefix"): 450 prefix = a 451 elif o in ("-i", "--input-file"): 452 input_file = a 453 elif o in ("-o", "--output-dir"): 454 output_dir = a + "/" 455 elif o in ("-c", "--source"): 456 do_c = True 457 elif o in ("-h", "--header"): 458 do_h = True 459 elif o in ("-b", "--builtins"): 460 do_builtins = True 461 462if not do_c and not do_h: 463 do_c = True 464 do_h = True 465 466c_file = output_dir + prefix + c_file 467h_file = output_dir + prefix + h_file 468 469try: 470 os.makedirs(output_dir) 471except os.error, e: 472 if e.errno != errno.EEXIST: 473 raise 474 475def maybe_open(really, name, opt): 476 if really: 477 return open(name, opt) 478 else: 479 import StringIO 480 return StringIO.StringIO() 481 482fdef = maybe_open(do_c, c_file, 'w') 483fdecl = maybe_open(do_h, h_file, 'w') 484 485fdef.write(mcgen(''' 486/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 487 488/* 489 * schema-defined QAPI visitor functions 490 * 491 * Copyright IBM, Corp. 2011 492 * 493 * Authors: 494 * Anthony Liguori <aliguori@us.ibm.com> 495 * 496 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 497 * See the COPYING.LIB file in the top-level directory. 498 * 499 */ 500 501#include "qemu-common.h" 502#include "%(header)s" 503''', 504 header=basename(h_file))) 505 506fdecl.write(mcgen(''' 507/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 508 509/* 510 * schema-defined QAPI visitor functions 511 * 512 * Copyright IBM, Corp. 2011 513 * 514 * Authors: 515 * Anthony Liguori <aliguori@us.ibm.com> 516 * 517 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 518 * See the COPYING.LIB file in the top-level directory. 519 * 520 */ 521 522#ifndef %(guard)s 523#define %(guard)s 524 525#include "qapi/visitor.h" 526#include "%(prefix)sqapi-types.h" 527 528''', 529 prefix=prefix, guard=guardname(h_file))) 530 531exprs = parse_schema(input_file) 532 533# to avoid header dependency hell, we always generate declarations 534# for built-in types in our header files and simply guard them 535fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL")) 536for typename in builtin_types.keys(): 537 fdecl.write(generate_declaration(typename, None, builtin_type=True)) 538fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL")) 539 540# ...this doesn't work for cases where we link in multiple objects that 541# have the functions defined, so we use -b option to provide control 542# over these cases 543if do_builtins: 544 for typename in builtin_types.keys(): 545 fdef.write(generate_visit_list(typename, None)) 546 547for expr in exprs: 548 if expr.has_key('struct'): 549 ret = generate_visit_struct(expr) 550 ret += generate_visit_list(expr['struct'], expr['data']) 551 fdef.write(ret) 552 553 ret = generate_declaration(expr['struct'], expr['data']) 554 fdecl.write(ret) 555 elif expr.has_key('union'): 556 ret = generate_visit_union(expr) 557 ret += generate_visit_list(expr['union'], expr['data']) 558 fdef.write(ret) 559 560 enum_define = discriminator_find_enum_define(expr) 561 ret = "" 562 if not enum_define: 563 ret = generate_decl_enum('%sKind' % expr['union'], 564 expr['data'].keys()) 565 ret += generate_declaration(expr['union'], expr['data']) 566 fdecl.write(ret) 567 elif expr.has_key('alternate'): 568 ret = generate_visit_alternate(expr['alternate'], expr['data']) 569 ret += generate_visit_list(expr['alternate'], expr['data']) 570 fdef.write(ret) 571 572 ret = generate_decl_enum('%sKind' % expr['alternate'], 573 expr['data'].keys()) 574 ret += generate_declaration(expr['alternate'], expr['data']) 575 fdecl.write(ret) 576 elif expr.has_key('enum'): 577 ret = generate_visit_list(expr['enum'], expr['data']) 578 ret += generate_visit_enum(expr['enum'], expr['data']) 579 fdef.write(ret) 580 581 ret = generate_decl_enum(expr['enum'], expr['data']) 582 ret += generate_enum_declaration(expr['enum'], expr['data']) 583 fdecl.write(ret) 584 585fdecl.write(''' 586#endif 587''') 588 589fdecl.flush() 590fdecl.close() 591 592fdef.flush() 593fdef.close() 594