qapi-event.py 8.2 KB

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