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