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