1 # 2 # QAPI command marshaller generator 3 # 4 # Copyright IBM, Corp. 2011 5 # 6 # Authors: 7 # Anthony Liguori <aliguori (at] us.ibm.com> 8 # Michael Roth <mdroth (at] linux.vnet.ibm.com> 9 # 10 # This work is licensed under the terms of the GNU GPLv2. 11 # See the COPYING.LIB file in the top-level directory. 12 13 from ordereddict import OrderedDict 14 from qapi import * 15 import sys 16 import os 17 import getopt 18 import errno 19 20 def type_visitor(name): 21 if type(name) == list: 22 return 'visit_type_%sList' % name[0] 23 else: 24 return 'visit_type_%s' % name 25 26 def generate_decl_enum(name, members, genlist=True): 27 return mcgen(''' 28 29 void %(visitor)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); 30 ''', 31 visitor=type_visitor(name)) 32 33 def generate_command_decl(name, args, ret_type): 34 arglist="" 35 for argname, argtype, optional, structured in parse_args(args): 36 argtype = c_type(argtype) 37 if argtype == "char *": 38 argtype = "const char *" 39 if optional: 40 arglist += "bool has_%s, " % c_var(argname) 41 arglist += "%s %s, " % (argtype, c_var(argname)) 42 return mcgen(''' 43 %(ret_type)s qmp_%(name)s(%(args)sError **errp); 44 ''', 45 ret_type=c_type(ret_type), name=c_fun(name), args=arglist).strip() 46 47 def gen_sync_call(name, args, ret_type, indent=0): 48 ret = "" 49 arglist="" 50 retval="" 51 if ret_type: 52 retval = "retval = " 53 for argname, argtype, optional, structured in parse_args(args): 54 if optional: 55 arglist += "has_%s, " % c_var(argname) 56 arglist += "%s, " % (c_var(argname)) 57 push_indent(indent) 58 ret = mcgen(''' 59 %(retval)sqmp_%(name)s(%(args)serrp); 60 61 ''', 62 name=c_fun(name), args=arglist, retval=retval).rstrip() 63 if ret_type: 64 ret += "\n" + mcgen('''' 65 if (!error_is_set(errp)) { 66 %(marshal_output_call)s 67 } 68 ''', 69 marshal_output_call=gen_marshal_output_call(name, ret_type)).rstrip() 70 pop_indent(indent) 71 return ret.rstrip() 72 73 74 def gen_marshal_output_call(name, ret_type): 75 if not ret_type: 76 return "" 77 return "qmp_marshal_output_%s(retval, ret, errp);" % c_fun(name) 78 79 def gen_visitor_output_containers_decl(ret_type): 80 ret = "" 81 push_indent() 82 if ret_type: 83 ret += mcgen(''' 84 QmpOutputVisitor *mo; 85 QapiDeallocVisitor *md; 86 Visitor *v; 87 ''') 88 pop_indent() 89 90 return ret 91 92 def gen_visitor_input_containers_decl(args): 93 ret = "" 94 95 push_indent() 96 if len(args) > 0: 97 ret += mcgen(''' 98 QmpInputVisitor *mi; 99 QapiDeallocVisitor *md; 100 Visitor *v; 101 ''') 102 pop_indent() 103 104 return ret.rstrip() 105 106 def gen_visitor_input_vars_decl(args): 107 ret = "" 108 push_indent() 109 for argname, argtype, optional, structured in parse_args(args): 110 if optional: 111 ret += mcgen(''' 112 bool has_%(argname)s = false; 113 ''', 114 argname=c_var(argname)) 115 if c_type(argtype).endswith("*"): 116 ret += mcgen(''' 117 %(argtype)s %(argname)s = NULL; 118 ''', 119 argname=c_var(argname), argtype=c_type(argtype)) 120 else: 121 ret += mcgen(''' 122 %(argtype)s %(argname)s; 123 ''', 124 argname=c_var(argname), argtype=c_type(argtype)) 125 126 pop_indent() 127 return ret.rstrip() 128 129 def gen_visitor_input_block(args, obj, dealloc=False): 130 ret = "" 131 errparg = 'errp' 132 133 if len(args) == 0: 134 return ret 135 136 push_indent() 137 138 if dealloc: 139 errparg = 'NULL' 140 ret += mcgen(''' 141 md = qapi_dealloc_visitor_new(); 142 v = qapi_dealloc_get_visitor(md); 143 ''') 144 else: 145 ret += mcgen(''' 146 mi = qmp_input_visitor_new_strict(%(obj)s); 147 v = qmp_input_get_visitor(mi); 148 ''', 149 obj=obj) 150 151 for argname, argtype, optional, structured in parse_args(args): 152 if optional: 153 ret += mcgen(''' 154 visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); 155 if (has_%(c_name)s) { 156 ''', 157 c_name=c_var(argname), name=argname, errp=errparg) 158 push_indent() 159 ret += mcgen(''' 160 %(visitor)s(v, &%(c_name)s, "%(name)s", %(errp)s); 161 ''', 162 c_name=c_var(argname), name=argname, argtype=argtype, 163 visitor=type_visitor(argtype), errp=errparg) 164 if optional: 165 pop_indent() 166 ret += mcgen(''' 167 } 168 visit_end_optional(v, %(errp)s); 169 ''', errp=errparg) 170 171 if dealloc: 172 ret += mcgen(''' 173 qapi_dealloc_visitor_cleanup(md); 174 ''') 175 else: 176 ret += mcgen(''' 177 qmp_input_visitor_cleanup(mi); 178 ''') 179 pop_indent() 180 return ret.rstrip() 181 182 def gen_marshal_output(name, args, ret_type, middle_mode): 183 if not ret_type: 184 return "" 185 186 ret = mcgen(''' 187 static void qmp_marshal_output_%(c_name)s(%(c_ret_type)s ret_in, QObject **ret_out, Error **errp) 188 { 189 QapiDeallocVisitor *md = qapi_dealloc_visitor_new(); 190 QmpOutputVisitor *mo = qmp_output_visitor_new(); 191 Visitor *v; 192 193 v = qmp_output_get_visitor(mo); 194 %(visitor)s(v, &ret_in, "unused", errp); 195 if (!error_is_set(errp)) { 196 *ret_out = qmp_output_get_qobject(mo); 197 } 198 qmp_output_visitor_cleanup(mo); 199 v = qapi_dealloc_get_visitor(md); 200 %(visitor)s(v, &ret_in, "unused", NULL); 201 qapi_dealloc_visitor_cleanup(md); 202 } 203 ''', 204 c_ret_type=c_type(ret_type), c_name=c_fun(name), 205 visitor=type_visitor(ret_type)) 206 207 return ret 208 209 def gen_marshal_input_decl(name, args, ret_type, middle_mode): 210 if middle_mode: 211 return 'int qmp_marshal_input_%s(Monitor *mon, const QDict *qdict, QObject **ret)' % c_fun(name) 212 else: 213 return 'static void qmp_marshal_input_%s(QDict *args, QObject **ret, Error **errp)' % c_fun(name) 214 215 216 217 def gen_marshal_input(name, args, ret_type, middle_mode): 218 hdr = gen_marshal_input_decl(name, args, ret_type, middle_mode) 219 220 ret = mcgen(''' 221 %(header)s 222 { 223 ''', 224 header=hdr) 225 226 if middle_mode: 227 ret += mcgen(''' 228 Error *local_err = NULL; 229 Error **errp = &local_err; 230 QDict *args = (QDict *)qdict; 231 ''') 232 233 if ret_type: 234 if c_type(ret_type).endswith("*"): 235 retval = " %s retval = NULL;" % c_type(ret_type) 236 else: 237 retval = " %s retval;" % c_type(ret_type) 238 ret += mcgen(''' 239 %(retval)s 240 ''', 241 retval=retval) 242 243 if len(args) > 0: 244 ret += mcgen(''' 245 %(visitor_input_containers_decl)s 246 %(visitor_input_vars_decl)s 247 248 %(visitor_input_block)s 249 250 ''', 251 visitor_input_containers_decl=gen_visitor_input_containers_decl(args), 252 visitor_input_vars_decl=gen_visitor_input_vars_decl(args), 253 visitor_input_block=gen_visitor_input_block(args, "QOBJECT(args)")) 254 else: 255 ret += mcgen(''' 256 (void)args; 257 ''') 258 259 ret += mcgen(''' 260 if (error_is_set(errp)) { 261 goto out; 262 } 263 %(sync_call)s 264 ''', 265 sync_call=gen_sync_call(name, args, ret_type, indent=4)) 266 ret += mcgen(''' 267 268 out: 269 ''') 270 ret += mcgen(''' 271 %(visitor_input_block_cleanup)s 272 ''', 273 visitor_input_block_cleanup=gen_visitor_input_block(args, None, 274 dealloc=True)) 275 276 if middle_mode: 277 ret += mcgen(''' 278 279 if (local_err) { 280 qerror_report_err(local_err); 281 error_free(local_err); 282 return -1; 283 } 284 return 0; 285 ''') 286 else: 287 ret += mcgen(''' 288 return; 289 ''') 290 291 ret += mcgen(''' 292 } 293 ''') 294 295 return ret 296 297 def option_value_matches(opt, val, cmd): 298 if opt in cmd and cmd[opt] == val: 299 return True 300 return False 301 302 def gen_registry(commands): 303 registry="" 304 push_indent() 305 for cmd in commands: 306 options = 'QCO_NO_OPTIONS' 307 if option_value_matches('success-response', 'no', cmd): 308 options = 'QCO_NO_SUCCESS_RESP' 309 310 registry += mcgen(''' 311 qmp_register_command("%(name)s", qmp_marshal_input_%(c_name)s, %(opts)s); 312 ''', 313 name=cmd['command'], c_name=c_fun(cmd['command']), 314 opts=options) 315 pop_indent() 316 ret = mcgen(''' 317 static void qmp_init_marshal(void) 318 { 319 %(registry)s 320 } 321 322 qapi_init(qmp_init_marshal); 323 ''', 324 registry=registry.rstrip()) 325 return ret 326 327 def gen_command_decl_prologue(header, guard, prefix=""): 328 ret = mcgen(''' 329 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 330 331 /* 332 * schema-defined QAPI function prototypes 333 * 334 * Copyright IBM, Corp. 2011 335 * 336 * Authors: 337 * Anthony Liguori <aliguori@us.ibm.com> 338 * 339 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 340 * See the COPYING.LIB file in the top-level directory. 341 * 342 */ 343 344 #ifndef %(guard)s 345 #define %(guard)s 346 347 #include "%(prefix)sqapi-types.h" 348 #include "qapi/qmp/qdict.h" 349 #include "qapi/error.h" 350 351 ''', 352 header=basename(header), guard=guardname(header), prefix=prefix) 353 return ret 354 355 def gen_command_def_prologue(prefix="", proxy=False): 356 ret = mcgen(''' 357 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 358 359 /* 360 * schema-defined QMP->QAPI command dispatch 361 * 362 * Copyright IBM, Corp. 2011 363 * 364 * Authors: 365 * Anthony Liguori <aliguori@us.ibm.com> 366 * 367 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 368 * See the COPYING.LIB file in the top-level directory. 369 * 370 */ 371 372 #include "qemu-common.h" 373 #include "qemu/module.h" 374 #include "qapi/qmp/qerror.h" 375 #include "qapi/qmp/types.h" 376 #include "qapi/qmp/dispatch.h" 377 #include "qapi/visitor.h" 378 #include "qapi/qmp-output-visitor.h" 379 #include "qapi/qmp-input-visitor.h" 380 #include "qapi/dealloc-visitor.h" 381 #include "%(prefix)sqapi-types.h" 382 #include "%(prefix)sqapi-visit.h" 383 384 ''', 385 prefix=prefix) 386 if not proxy: 387 ret += '#include "%sqmp-commands.h"' % prefix 388 return ret + "\n\n" 389 390 391 try: 392 opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:m", 393 ["source", "header", "prefix=", 394 "output-dir=", "type=", "middle"]) 395 except getopt.GetoptError, err: 396 print str(err) 397 sys.exit(1) 398 399 output_dir = "" 400 prefix = "" 401 dispatch_type = "sync" 402 c_file = 'qmp-marshal.c' 403 h_file = 'qmp-commands.h' 404 middle_mode = False 405 406 do_c = False 407 do_h = False 408 409 for o, a in opts: 410 if o in ("-p", "--prefix"): 411 prefix = a 412 elif o in ("-o", "--output-dir"): 413 output_dir = a + "/" 414 elif o in ("-t", "--type"): 415 dispatch_type = a 416 elif o in ("-m", "--middle"): 417 middle_mode = True 418 elif o in ("-c", "--source"): 419 do_c = True 420 elif o in ("-h", "--header"): 421 do_h = True 422 423 if not do_c and not do_h: 424 do_c = True 425 do_h = True 426 427 c_file = output_dir + prefix + c_file 428 h_file = output_dir + prefix + h_file 429 430 def maybe_open(really, name, opt): 431 if really: 432 return open(name, opt) 433 else: 434 import StringIO 435 return StringIO.StringIO() 436 437 try: 438 os.makedirs(output_dir) 439 except os.error, e: 440 if e.errno != errno.EEXIST: 441 raise 442 443 exprs = parse_schema(sys.stdin) 444 commands = filter(lambda expr: expr.has_key('command'), exprs) 445 commands = filter(lambda expr: not expr.has_key('gen'), commands) 446 447 if dispatch_type == "sync": 448 fdecl = maybe_open(do_h, h_file, 'w') 449 fdef = maybe_open(do_c, c_file, 'w') 450 ret = gen_command_decl_prologue(header=basename(h_file), guard=guardname(h_file), prefix=prefix) 451 fdecl.write(ret) 452 ret = gen_command_def_prologue(prefix=prefix) 453 fdef.write(ret) 454 455 for cmd in commands: 456 arglist = [] 457 ret_type = None 458 if cmd.has_key('data'): 459 arglist = cmd['data'] 460 if cmd.has_key('returns'): 461 ret_type = cmd['returns'] 462 ret = generate_command_decl(cmd['command'], arglist, ret_type) + "\n" 463 fdecl.write(ret) 464 if ret_type: 465 ret = gen_marshal_output(cmd['command'], arglist, ret_type, middle_mode) + "\n" 466 fdef.write(ret) 467 468 if middle_mode: 469 fdecl.write('%s;\n' % gen_marshal_input_decl(cmd['command'], arglist, ret_type, middle_mode)) 470 471 ret = gen_marshal_input(cmd['command'], arglist, ret_type, middle_mode) + "\n" 472 fdef.write(ret) 473 474 fdecl.write("\n#endif\n"); 475 476 if not middle_mode: 477 ret = gen_registry(commands) 478 fdef.write(ret) 479 480 fdef.flush() 481 fdef.close() 482 fdecl.flush() 483 fdecl.close() 484