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