1# 2# QAPI command marshaller 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 GPLv2. 11# See the COPYING.LIB file in the top-level directory. 12 13from ordereddict import OrderedDict 14from qapi import * 15import sys 16import os 17import getopt 18import errno 19 20def type_visitor(name): 21 if type(name) == list: 22 return 'visit_type_%sList' % name[0] 23 else: 24 return 'visit_type_%s' % name 25 26def generate_decl_enum(name, members, genlist=True): 27 return mcgen(''' 28 29void %(visitor)s(Visitor *m, %(name)s * obj, const char *name, Error **errp); 30''', 31 visitor=type_visitor(name)) 32 33def 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 47def 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('''' 65if (!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 74def 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 79def gen_visitor_output_containers_decl(ret_type): 80 ret = "" 81 push_indent() 82 if ret_type: 83 ret += mcgen(''' 84QmpOutputVisitor *mo; 85QapiDeallocVisitor *md; 86Visitor *v; 87''') 88 pop_indent() 89 90 return ret 91 92def gen_visitor_input_containers_decl(args): 93 ret = "" 94 95 push_indent() 96 if len(args) > 0: 97 ret += mcgen(''' 98QmpInputVisitor *mi; 99QapiDeallocVisitor *md; 100Visitor *v; 101''') 102 pop_indent() 103 104 return ret.rstrip() 105 106def 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(''' 112bool 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 129def 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(''' 141md = qapi_dealloc_visitor_new(); 142v = qapi_dealloc_get_visitor(md); 143''') 144 else: 145 ret += mcgen(''' 146mi = qmp_input_visitor_new_strict(%(obj)s); 147v = 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(''' 154visit_start_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s); 155if (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} 168visit_end_optional(v, %(errp)s); 169''', errp=errparg) 170 171 if dealloc: 172 ret += mcgen(''' 173qapi_dealloc_visitor_cleanup(md); 174''') 175 else: 176 ret += mcgen(''' 177qmp_input_visitor_cleanup(mi); 178''') 179 pop_indent() 180 return ret.rstrip() 181 182def gen_marshal_output(name, args, ret_type, middle_mode): 183 if not ret_type: 184 return "" 185 186 ret = mcgen(''' 187static 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 209def 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 217def 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 268out: 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 297def option_value_matches(opt, val, cmd): 298 if opt in cmd and cmd[opt] == val: 299 return True 300 return False 301 302def 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(''' 311qmp_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(''' 317static void qmp_init_marshal(void) 318{ 319%(registry)s 320} 321 322qapi_init(qmp_init_marshal); 323''', 324 registry=registry.rstrip()) 325 return ret 326 327def 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 355def 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 391try: 392 opts, args = getopt.gnu_getopt(sys.argv[1:], "chp:o:m", 393 ["source", "header", "prefix=", 394 "output-dir=", "type=", "middle"]) 395except getopt.GetoptError, err: 396 print str(err) 397 sys.exit(1) 398 399output_dir = "" 400prefix = "" 401dispatch_type = "sync" 402c_file = 'qmp-marshal.c' 403h_file = 'qmp-commands.h' 404middle_mode = False 405 406do_c = False 407do_h = False 408 409for 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 423if not do_c and not do_h: 424 do_c = True 425 do_h = True 426 427c_file = output_dir + prefix + c_file 428h_file = output_dir + prefix + h_file 429 430def maybe_open(really, name, opt): 431 if really: 432 return open(name, opt) 433 else: 434 import StringIO 435 return StringIO.StringIO() 436 437try: 438 os.makedirs(output_dir) 439except os.error, e: 440 if e.errno != errno.EEXIST: 441 raise 442 443exprs = parse_schema(sys.stdin) 444commands = filter(lambda expr: expr.has_key('command'), exprs) 445commands = filter(lambda expr: not expr.has_key('gen'), commands) 446 447if 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