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