1# 2# QAPI event generator 3# 4# Copyright (c) 2014 Wenchao Xia 5# 6# Authors: 7# Wenchao Xia <wenchaoqemu@gmail.com> 8# 9# This work is licensed under the terms of the GNU GPL, version 2. 10# See the COPYING file in the top-level directory. 11 12from ordereddict import OrderedDict 13from qapi import * 14import os 15import errno 16 17def _generate_event_api_name(event_name, params): 18 api_name = "void qapi_event_send_%s(" % c_name(event_name).lower(); 19 l = len(api_name) 20 21 if params: 22 for argname, argentry, optional in parse_args(params): 23 if optional: 24 api_name += "bool has_%s,\n" % c_name(argname) 25 api_name += "".ljust(l) 26 27 api_name += "%s %s,\n" % (c_type(argentry, is_param=True), 28 c_name(argname)) 29 api_name += "".ljust(l) 30 31 api_name += "Error **errp)" 32 return api_name; 33 34 35# Following are the core functions that generate C APIs to emit event. 36 37def generate_event_declaration(api_name): 38 return mcgen(''' 39 40%(api_name)s; 41''', 42 api_name = api_name) 43 44def generate_event_implement(api_name, event_name, params): 45 # step 1: declare any variables 46 ret = mcgen(""" 47 48%(api_name)s 49{ 50 QDict *qmp; 51 Error *local_err = NULL; 52 QMPEventFuncEmit emit; 53""", 54 api_name = api_name) 55 56 if params: 57 ret += mcgen(""" 58 QmpOutputVisitor *qov; 59 Visitor *v; 60 QObject *obj; 61 62""") 63 64 # step 2: check emit function, create a dict 65 ret += mcgen(""" 66 emit = qmp_event_get_func_emit(); 67 if (!emit) { 68 return; 69 } 70 71 qmp = qmp_event_build_dict("%(event_name)s"); 72 73""", 74 event_name = event_name) 75 76 # step 3: visit the params if params != None 77 if params: 78 ret += mcgen(""" 79 qov = qmp_output_visitor_new(); 80 g_assert(qov); 81 82 v = qmp_output_get_visitor(qov); 83 g_assert(v); 84 85 /* Fake visit, as if all members are under a structure */ 86 visit_start_struct(v, NULL, "", "%(event_name)s", 0, &local_err); 87 if (local_err) { 88 goto clean; 89 } 90 91""", 92 event_name = event_name) 93 94 for argname, argentry, optional in parse_args(params): 95 if optional: 96 ret += mcgen(""" 97 if (has_%(var)s) { 98""", 99 var = c_name(argname)) 100 push_indent() 101 102 if argentry == "str": 103 var_type = "(char **)" 104 else: 105 var_type = "" 106 107 ret += mcgen(""" 108 visit_type_%(type)s(v, %(var_type)s&%(var)s, "%(name)s", &local_err); 109 if (local_err) { 110 goto clean; 111 } 112""", 113 var_type = var_type, 114 var = c_name(argname), 115 type = type_name(argentry), 116 name = argname) 117 118 if optional: 119 pop_indent() 120 ret += mcgen(""" 121 } 122""") 123 124 ret += mcgen(""" 125 126 visit_end_struct(v, &local_err); 127 if (local_err) { 128 goto clean; 129 } 130 131 obj = qmp_output_get_qobject(qov); 132 g_assert(obj != NULL); 133 134 qdict_put_obj(qmp, "data", obj); 135""") 136 137 # step 4: call qmp event api 138 ret += mcgen(""" 139 emit(%(event_enum_value)s, qmp, &local_err); 140 141""", 142 event_enum_value = event_enum_value) 143 144 # step 5: clean up 145 if params: 146 ret += mcgen(""" 147 clean: 148 qmp_output_visitor_cleanup(qov); 149""") 150 ret += mcgen(""" 151 error_propagate(errp, local_err); 152 QDECREF(qmp); 153} 154""") 155 156 return ret 157 158 159# Following are the functions that generate an enum type for all defined 160# events, similar to qapi-types.py. Here we already have enum name and 161# values which were generated before and recorded in event_enum_*. It also 162# works around the issue that "import qapi-types" can't work. 163 164def generate_event_enum_decl(event_enum_name, event_enum_values): 165 lookup_decl = mcgen(''' 166 167extern const char *%(event_enum_name)s_lookup[]; 168''', 169 event_enum_name = event_enum_name) 170 171 enum_decl = mcgen(''' 172typedef enum %(event_enum_name)s 173{ 174''', 175 event_enum_name = event_enum_name) 176 177 # append automatically generated _MAX value 178 enum_max_value = c_enum_const(event_enum_name, "MAX") 179 enum_values = event_enum_values + [ enum_max_value ] 180 181 i = 0 182 for value in enum_values: 183 enum_decl += mcgen(''' 184 %(value)s = %(i)d, 185''', 186 value = value, 187 i = i) 188 i += 1 189 190 enum_decl += mcgen(''' 191} %(event_enum_name)s; 192''', 193 event_enum_name = event_enum_name) 194 195 return lookup_decl + enum_decl 196 197def generate_event_enum_lookup(event_enum_name, event_enum_strings): 198 ret = mcgen(''' 199 200const char *%(event_enum_name)s_lookup[] = { 201''', 202 event_enum_name = event_enum_name) 203 204 i = 0 205 for string in event_enum_strings: 206 ret += mcgen(''' 207 "%(string)s", 208''', 209 string = string) 210 211 ret += mcgen(''' 212 NULL, 213}; 214''') 215 return ret 216 217 218# Start the real job 219 220c_file = 'qapi-event.c' 221h_file = 'qapi-event.h' 222 223(input_file, output_dir, do_c, do_h, prefix, dummy) = parse_command_line() 224 225c_file = output_dir + prefix + c_file 226h_file = output_dir + prefix + h_file 227 228try: 229 os.makedirs(output_dir) 230except os.error, e: 231 if e.errno != errno.EEXIST: 232 raise 233 234def maybe_open(really, name, opt): 235 if really: 236 return open(name, opt) 237 else: 238 import StringIO 239 return StringIO.StringIO() 240 241fdef = maybe_open(do_c, c_file, 'w') 242fdecl = maybe_open(do_h, h_file, 'w') 243 244fdef.write(mcgen(''' 245/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 246 247/* 248 * schema-defined QAPI event functions 249 * 250 * Copyright (c) 2014 Wenchao Xia 251 * 252 * Authors: 253 * Wenchao Xia <wenchaoqemu@gmail.com> 254 * 255 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 256 * See the COPYING.LIB file in the top-level directory. 257 * 258 */ 259 260#include "qemu-common.h" 261#include "%(header)s" 262#include "%(prefix)sqapi-visit.h" 263#include "qapi/qmp-output-visitor.h" 264#include "qapi/qmp-event.h" 265 266''', 267 prefix=prefix, header=basename(h_file))) 268 269fdecl.write(mcgen(''' 270/* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */ 271 272/* 273 * schema-defined QAPI event functions 274 * 275 * Copyright (c) 2014 Wenchao Xia 276 * 277 * Authors: 278 * Wenchao Xia <wenchaoqemu@gmail.com> 279 * 280 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 281 * See the COPYING.LIB file in the top-level directory. 282 * 283 */ 284 285#ifndef %(guard)s 286#define %(guard)s 287 288#include "qapi/error.h" 289#include "qapi/qmp/qdict.h" 290#include "%(prefix)sqapi-types.h" 291 292''', 293 prefix=prefix, guard=guardname(h_file))) 294 295exprs = parse_schema(input_file) 296 297event_enum_name = prefix.upper().replace('-', '_') + "QAPIEvent" 298event_enum_values = [] 299event_enum_strings = [] 300 301for expr in exprs: 302 if expr.has_key('event'): 303 event_name = expr['event'] 304 params = expr.get('data') 305 if params and len(params) == 0: 306 params = None 307 308 api_name = _generate_event_api_name(event_name, params) 309 ret = generate_event_declaration(api_name) 310 fdecl.write(ret) 311 312 # We need an enum value per event 313 event_enum_value = c_enum_const(event_enum_name, event_name) 314 ret = generate_event_implement(api_name, event_name, params) 315 fdef.write(ret) 316 317 # Record it, and generate enum later 318 event_enum_values.append(event_enum_value) 319 event_enum_strings.append(event_name) 320 321ret = generate_event_enum_decl(event_enum_name, event_enum_values) 322fdecl.write(ret) 323ret = generate_event_enum_lookup(event_enum_name, event_enum_strings) 324fdef.write(ret) 325 326fdecl.write(''' 327#endif 328''') 329 330fdecl.flush() 331fdecl.close() 332 333fdef.flush() 334fdef.close() 335