1# 2# QAPI command marshaller generator 3# 4# Copyright IBM, Corp. 2011 5# Copyright (C) 2014-2016 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 qapi import * 16import re 17 18 19def gen_command_decl(name, arg_type, ret_type): 20 return mcgen(''' 21%(c_type)s qmp_%(c_name)s(%(params)s); 22''', 23 c_type=(ret_type and ret_type.c_type()) or 'void', 24 c_name=c_name(name), 25 params=gen_params(arg_type, 'Error **errp')) 26 27 28def gen_call(name, arg_type, ret_type): 29 ret = '' 30 31 argstr = '' 32 if arg_type: 33 assert not arg_type.variants 34 for memb in arg_type.members: 35 if memb.optional: 36 argstr += 'arg.has_%s, ' % c_name(memb.name) 37 argstr += 'arg.%s, ' % c_name(memb.name) 38 39 lhs = '' 40 if ret_type: 41 lhs = 'retval = ' 42 43 ret = mcgen(''' 44 45 %(lhs)sqmp_%(c_name)s(%(args)s&err); 46''', 47 c_name=c_name(name), args=argstr, lhs=lhs) 48 if ret_type: 49 ret += gen_err_check() 50 ret += mcgen(''' 51 52 qmp_marshal_output_%(c_name)s(retval, ret, &err); 53''', 54 c_name=ret_type.c_name()) 55 return ret 56 57 58def gen_marshal_output(ret_type): 59 return mcgen(''' 60 61static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in, QObject **ret_out, Error **errp) 62{ 63 Error *err = NULL; 64 QmpOutputVisitor *qov = qmp_output_visitor_new(); 65 QapiDeallocVisitor *qdv; 66 Visitor *v; 67 68 v = qmp_output_get_visitor(qov); 69 visit_type_%(c_name)s(v, "unused", &ret_in, &err); 70 if (err) { 71 goto out; 72 } 73 *ret_out = qmp_output_get_qobject(qov); 74 75out: 76 error_propagate(errp, err); 77 qmp_output_visitor_cleanup(qov); 78 qdv = qapi_dealloc_visitor_new(); 79 v = qapi_dealloc_get_visitor(qdv); 80 visit_type_%(c_name)s(v, "unused", &ret_in, NULL); 81 qapi_dealloc_visitor_cleanup(qdv); 82} 83''', 84 c_type=ret_type.c_type(), c_name=ret_type.c_name()) 85 86 87def gen_marshal_proto(name): 88 ret = 'void qmp_marshal_%s(QDict *args, QObject **ret, Error **errp)' % c_name(name) 89 if not middle_mode: 90 ret = 'static ' + ret 91 return ret 92 93 94def gen_marshal_decl(name): 95 return mcgen(''' 96%(proto)s; 97''', 98 proto=gen_marshal_proto(name)) 99 100 101def gen_marshal(name, arg_type, ret_type): 102 ret = mcgen(''' 103 104%(proto)s 105{ 106 Error *err = NULL; 107''', 108 proto=gen_marshal_proto(name)) 109 110 if ret_type: 111 ret += mcgen(''' 112 %(c_type)s retval; 113''', 114 c_type=ret_type.c_type()) 115 116 if arg_type and arg_type.members: 117 ret += mcgen(''' 118 QmpInputVisitor *qiv = qmp_input_visitor_new_strict(QOBJECT(args)); 119 QapiDeallocVisitor *qdv; 120 Visitor *v; 121 %(c_name)s arg = {0}; 122 123 v = qmp_input_get_visitor(qiv); 124 visit_type_%(c_name)s_members(v, &arg, &err); 125 if (err) { 126 goto out; 127 } 128''', 129 c_name=arg_type.c_name()) 130 131 else: 132 ret += mcgen(''' 133 134 (void)args; 135''') 136 137 ret += gen_call(name, arg_type, ret_type) 138 139 # 'goto out' produced above for arg_type, and by gen_call() for ret_type 140 if (arg_type and arg_type.members) or ret_type: 141 ret += mcgen(''' 142 143out: 144''') 145 ret += mcgen(''' 146 error_propagate(errp, err); 147''') 148 if arg_type and arg_type.members: 149 ret += mcgen(''' 150 qmp_input_visitor_cleanup(qiv); 151 qdv = qapi_dealloc_visitor_new(); 152 v = qapi_dealloc_get_visitor(qdv); 153 visit_type_%(c_name)s_members(v, &arg, NULL); 154 qapi_dealloc_visitor_cleanup(qdv); 155''', 156 c_name=arg_type.c_name()) 157 158 ret += mcgen(''' 159} 160''') 161 return ret 162 163 164def gen_register_command(name, success_response): 165 options = 'QCO_NO_OPTIONS' 166 if not success_response: 167 options = 'QCO_NO_SUCCESS_RESP' 168 169 ret = mcgen(''' 170 qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s); 171''', 172 name=name, c_name=c_name(name), 173 opts=options) 174 return ret 175 176 177def gen_registry(registry): 178 ret = mcgen(''' 179 180static void qmp_init_marshal(void) 181{ 182''') 183 ret += registry 184 ret += mcgen(''' 185} 186 187qapi_init(qmp_init_marshal); 188''') 189 return ret 190 191 192class QAPISchemaGenCommandVisitor(QAPISchemaVisitor): 193 def __init__(self): 194 self.decl = None 195 self.defn = None 196 self._regy = None 197 self._visited_ret_types = None 198 199 def visit_begin(self, schema): 200 self.decl = '' 201 self.defn = '' 202 self._regy = '' 203 self._visited_ret_types = set() 204 205 def visit_end(self): 206 if not middle_mode: 207 self.defn += gen_registry(self._regy) 208 self._regy = None 209 self._visited_ret_types = None 210 211 def visit_command(self, name, info, arg_type, ret_type, 212 gen, success_response): 213 if not gen: 214 return 215 self.decl += gen_command_decl(name, arg_type, ret_type) 216 if ret_type and ret_type not in self._visited_ret_types: 217 self._visited_ret_types.add(ret_type) 218 self.defn += gen_marshal_output(ret_type) 219 if middle_mode: 220 self.decl += gen_marshal_decl(name) 221 self.defn += gen_marshal(name, arg_type, ret_type) 222 if not middle_mode: 223 self._regy += gen_register_command(name, success_response) 224 225 226middle_mode = False 227 228(input_file, output_dir, do_c, do_h, prefix, opts) = \ 229 parse_command_line("m", ["middle"]) 230 231for o, a in opts: 232 if o in ("-m", "--middle"): 233 middle_mode = True 234 235c_comment = ''' 236/* 237 * schema-defined QMP->QAPI command dispatch 238 * 239 * Copyright IBM, Corp. 2011 240 * 241 * Authors: 242 * Anthony Liguori <aliguori@us.ibm.com> 243 * 244 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 245 * See the COPYING.LIB file in the top-level directory. 246 * 247 */ 248''' 249h_comment = ''' 250/* 251 * schema-defined QAPI function prototypes 252 * 253 * Copyright IBM, Corp. 2011 254 * 255 * Authors: 256 * Anthony Liguori <aliguori@us.ibm.com> 257 * 258 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 259 * See the COPYING.LIB file in the top-level directory. 260 * 261 */ 262''' 263 264(fdef, fdecl) = open_output(output_dir, do_c, do_h, prefix, 265 'qmp-marshal.c', 'qmp-commands.h', 266 c_comment, h_comment) 267 268fdef.write(mcgen(''' 269#include "qemu/osdep.h" 270#include "qemu-common.h" 271#include "qemu/module.h" 272#include "qapi/qmp/types.h" 273#include "qapi/qmp/dispatch.h" 274#include "qapi/visitor.h" 275#include "qapi/qmp-output-visitor.h" 276#include "qapi/qmp-input-visitor.h" 277#include "qapi/dealloc-visitor.h" 278#include "%(prefix)sqapi-types.h" 279#include "%(prefix)sqapi-visit.h" 280#include "%(prefix)sqmp-commands.h" 281 282''', 283 prefix=prefix)) 284 285fdecl.write(mcgen(''' 286#include "%(prefix)sqapi-types.h" 287#include "qapi/qmp/qdict.h" 288#include "qapi/error.h" 289 290''', 291 prefix=prefix)) 292 293schema = QAPISchema(input_file) 294gen = QAPISchemaGenCommandVisitor() 295schema.visit(gen) 296fdef.write(gen.defn) 297fdecl.write(gen.decl) 298 299close_output(fdef, fdecl) 300