Home | History | Annotate | Download | only in scripts
      1 #
      2 # QAPI visitor 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 generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
     21     substructs = []
     22     ret = ''
     23     if not fn_prefix:
     24         full_name = name
     25     else:
     26         full_name = "%s_%s" % (name, fn_prefix)
     27 
     28     for argname, argentry, optional, structured in parse_args(members):
     29         if structured:
     30             if not fn_prefix:
     31                 nested_fn_prefix = argname
     32             else:
     33                 nested_fn_prefix = "%s_%s" % (fn_prefix, argname)
     34 
     35             nested_field_prefix = "%s%s." % (field_prefix, argname)
     36             ret += generate_visit_struct_fields(name, nested_field_prefix,
     37                                                 nested_fn_prefix, argentry)
     38 
     39     ret += mcgen('''
     40 
     41 static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s ** obj, Error **errp)
     42 {
     43     Error *err = NULL;
     44 ''',
     45         name=name, full_name=full_name)
     46     push_indent()
     47 
     48     if base:
     49         ret += mcgen('''
     50 visit_start_implicit_struct(m, obj ? (void**) &(*obj)->%(c_name)s : NULL, sizeof(%(type)s), &err);
     51 if (!err) {
     52     visit_type_%(type)s_fields(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, &err);
     53     error_propagate(errp, err);
     54     err = NULL;
     55     visit_end_implicit_struct(m, &err);
     56 }
     57 ''',
     58                      c_prefix=c_var(field_prefix),
     59                      type=type_name(base), c_name=c_var('base'))
     60 
     61     for argname, argentry, optional, structured in parse_args(members):
     62         if optional:
     63             ret += mcgen('''
     64 visit_start_optional(m, obj ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, "%(name)s", &err);
     65 if (obj && (*obj)->%(prefix)shas_%(c_name)s) {
     66 ''',
     67                          c_prefix=c_var(field_prefix), prefix=field_prefix,
     68                          c_name=c_var(argname), name=argname)
     69             push_indent()
     70 
     71         if structured:
     72             ret += generate_visit_struct_body(full_name, argname, argentry)
     73         else:
     74             ret += mcgen('''
     75 visit_type_%(type)s(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, "%(name)s", &err);
     76 ''',
     77                          c_prefix=c_var(field_prefix), prefix=field_prefix,
     78                          type=type_name(argentry), c_name=c_var(argname),
     79                          name=argname)
     80 
     81         if optional:
     82             pop_indent()
     83             ret += mcgen('''
     84 }
     85 visit_end_optional(m, &err);
     86 ''')
     87 
     88     pop_indent()
     89     ret += mcgen('''
     90 
     91     error_propagate(errp, err);
     92 }
     93 ''')
     94     return ret
     95 
     96 
     97 def generate_visit_struct_body(field_prefix, name, members):
     98     ret = mcgen('''
     99 if (!error_is_set(errp)) {
    100 ''')
    101     push_indent()
    102 
    103     if not field_prefix:
    104         full_name = name
    105     else:
    106         full_name = "%s_%s" % (field_prefix, name)
    107 
    108     if len(field_prefix):
    109         ret += mcgen('''
    110 Error **errp = &err; /* from outer scope */
    111 Error *err = NULL;
    112 visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
    113 ''',
    114                 name=name)
    115     else:
    116         ret += mcgen('''
    117 Error *err = NULL;
    118 visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
    119 ''',
    120                 name=name)
    121 
    122     ret += mcgen('''
    123 if (!err) {
    124     if (!obj || *obj) {
    125         visit_type_%(name)s_fields(m, obj, &err);
    126         error_propagate(errp, err);
    127         err = NULL;
    128     }
    129 ''',
    130         name=full_name)
    131 
    132     pop_indent()
    133     ret += mcgen('''
    134         /* Always call end_struct if start_struct succeeded.  */
    135         visit_end_struct(m, &err);
    136     }
    137     error_propagate(errp, err);
    138 }
    139 ''')
    140     return ret
    141 
    142 def generate_visit_struct(expr):
    143 
    144     name = expr['type']
    145     members = expr['data']
    146     base = expr.get('base')
    147 
    148     ret = generate_visit_struct_fields(name, "", "", members, base)
    149 
    150     ret += mcgen('''
    151 
    152 void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
    153 {
    154 ''',
    155                 name=name)
    156 
    157     push_indent()
    158     ret += generate_visit_struct_body("", name, members)
    159     pop_indent()
    160 
    161     ret += mcgen('''
    162 }
    163 ''')
    164     return ret
    165 
    166 def generate_visit_list(name, members):
    167     return mcgen('''
    168 
    169 void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp)
    170 {
    171     GenericList *i, **prev = (GenericList **)obj;
    172     Error *err = NULL;
    173 
    174     if (!error_is_set(errp)) {
    175         visit_start_list(m, name, &err);
    176         if (!err) {
    177             for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
    178                 %(name)sList *native_i = (%(name)sList *)i;
    179                 visit_type_%(name)s(m, &native_i->value, NULL, &err);
    180             }
    181             error_propagate(errp, err);
    182             err = NULL;
    183 
    184             /* Always call end_list if start_list succeeded.  */
    185             visit_end_list(m, &err);
    186         }
    187         error_propagate(errp, err);
    188     }
    189 }
    190 ''',
    191                 name=name)
    192 
    193 def generate_visit_enum(name, members):
    194     return mcgen('''
    195 
    196 void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp)
    197 {
    198     visit_type_enum(m, (int *)obj, %(name)s_lookup, "%(name)s", name, errp);
    199 }
    200 ''',
    201                  name=name)
    202 
    203 def generate_visit_anon_union(name, members):
    204     ret = mcgen('''
    205 
    206 void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
    207 {
    208     Error *err = NULL;
    209 
    210     if (!error_is_set(errp)) {
    211         visit_start_implicit_struct(m, (void**) obj, sizeof(%(name)s), &err);
    212         visit_get_next_type(m, (int*) &(*obj)->kind, %(name)s_qtypes, name, &err);
    213         switch ((*obj)->kind) {
    214 ''',
    215     name=name)
    216 
    217     for key in members:
    218         assert (members[key] in builtin_types
    219             or find_struct(members[key])
    220             or find_union(members[key])), "Invalid anonymous union member"
    221 
    222         ret += mcgen('''
    223         case %(abbrev)s_KIND_%(enum)s:
    224             visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, name, &err);
    225             break;
    226 ''',
    227                 abbrev = de_camel_case(name).upper(),
    228                 enum = c_fun(de_camel_case(key),False).upper(),
    229                 c_type = type_name(members[key]),
    230                 c_name = c_fun(key))
    231 
    232     ret += mcgen('''
    233         default:
    234             abort();
    235         }
    236         error_propagate(errp, err);
    237         err = NULL;
    238         visit_end_implicit_struct(m, &err);
    239     }
    240 }
    241 ''')
    242 
    243     return ret
    244 
    245 
    246 def generate_visit_union(expr):
    247 
    248     name = expr['union']
    249     members = expr['data']
    250 
    251     base = expr.get('base')
    252     discriminator = expr.get('discriminator')
    253 
    254     if discriminator == {}:
    255         assert not base
    256         return generate_visit_anon_union(name, members)
    257 
    258     ret = generate_visit_enum('%sKind' % name, members.keys())
    259 
    260     if base:
    261         base_fields = find_struct(base)['data']
    262         if discriminator:
    263             base_fields = base_fields.copy()
    264             del base_fields[discriminator]
    265         ret += generate_visit_struct_fields(name, "", "", base_fields)
    266 
    267     ret += mcgen('''
    268 
    269 void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp)
    270 {
    271     Error *err = NULL;
    272 
    273     if (!error_is_set(errp)) {
    274         visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
    275         if (!err) {
    276             if (obj && *obj) {
    277 ''',
    278                  name=name)
    279 
    280 
    281     push_indent()
    282     push_indent()
    283     push_indent()
    284 
    285     if base:
    286         ret += mcgen('''
    287     visit_type_%(name)s_fields(m, obj, &err);
    288 ''',
    289             name=name)
    290 
    291     pop_indent()
    292 
    293     if not discriminator:
    294         desc_type = "type"
    295     else:
    296         desc_type = discriminator
    297     ret += mcgen('''
    298         visit_type_%(name)sKind(m, &(*obj)->kind, "%(type)s", &err);
    299         if (!err) {
    300             switch ((*obj)->kind) {
    301 ''',
    302                  name=name, type=desc_type)
    303 
    304     for key in members:
    305         if not discriminator:
    306             fmt = 'visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);'
    307         else:
    308             fmt = '''visit_start_implicit_struct(m, (void**) &(*obj)->%(c_name)s, sizeof(%(c_type)s), &err);
    309                 if (!err) {
    310                     visit_type_%(c_type)s_fields(m, &(*obj)->%(c_name)s, &err);
    311                     error_propagate(errp, err);
    312                     err = NULL;
    313                     visit_end_implicit_struct(m, &err);
    314                 }'''
    315 
    316         ret += mcgen('''
    317             case %(abbrev)s_KIND_%(enum)s:
    318                 ''' + fmt + '''
    319                 break;
    320 ''',
    321                 abbrev = de_camel_case(name).upper(),
    322                 enum = c_fun(de_camel_case(key),False).upper(),
    323                 c_type=type_name(members[key]),
    324                 c_name=c_fun(key))
    325 
    326     ret += mcgen('''
    327             default:
    328                 abort();
    329             }
    330         }
    331         error_propagate(errp, err);
    332         err = NULL;
    333     }
    334 ''')
    335     pop_indent()
    336     ret += mcgen('''
    337         /* Always call end_struct if start_struct succeeded.  */
    338         visit_end_struct(m, &err);
    339     }
    340     error_propagate(errp, err);
    341 }
    342 ''')
    343 
    344     pop_indent();
    345     ret += mcgen('''
    346 }
    347 ''')
    348 
    349     return ret
    350 
    351 def generate_declaration(name, members, genlist=True, builtin_type=False):
    352     ret = ""
    353     if not builtin_type:
    354         ret += mcgen('''
    355 
    356 void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, Error **errp);
    357 ''',
    358                     name=name)
    359 
    360     if genlist:
    361         ret += mcgen('''
    362 void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
    363 ''',
    364                  name=name)
    365 
    366     return ret
    367 
    368 def generate_enum_declaration(name, members, genlist=True):
    369     ret = ""
    370     if genlist:
    371         ret += mcgen('''
    372 void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char *name, Error **errp);
    373 ''',
    374                      name=name)
    375 
    376     return ret
    377 
    378 def generate_decl_enum(name, members, genlist=True):
    379     return mcgen('''
    380 
    381 void visit_type_%(name)s(Visitor *m, %(name)s * obj, const char *name, Error **errp);
    382 ''',
    383                 name=name)
    384 
    385 try:
    386     opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
    387                                    ["source", "header", "builtins", "prefix=",
    388                                     "output-dir="])
    389 except getopt.GetoptError, err:
    390     print str(err)
    391     sys.exit(1)
    392 
    393 output_dir = ""
    394 prefix = ""
    395 c_file = 'qapi-visit.c'
    396 h_file = 'qapi-visit.h'
    397 
    398 do_c = False
    399 do_h = False
    400 do_builtins = False
    401 
    402 for o, a in opts:
    403     if o in ("-p", "--prefix"):
    404         prefix = a
    405     elif o in ("-o", "--output-dir"):
    406         output_dir = a + "/"
    407     elif o in ("-c", "--source"):
    408         do_c = True
    409     elif o in ("-h", "--header"):
    410         do_h = True
    411     elif o in ("-b", "--builtins"):
    412         do_builtins = True
    413 
    414 if not do_c and not do_h:
    415     do_c = True
    416     do_h = True
    417 
    418 c_file = output_dir + prefix + c_file
    419 h_file = output_dir + prefix + h_file
    420 
    421 try:
    422     os.makedirs(output_dir)
    423 except os.error, e:
    424     if e.errno != errno.EEXIST:
    425         raise
    426 
    427 def maybe_open(really, name, opt):
    428     if really:
    429         return open(name, opt)
    430     else:
    431         import StringIO
    432         return StringIO.StringIO()
    433 
    434 fdef = maybe_open(do_c, c_file, 'w')
    435 fdecl = maybe_open(do_h, h_file, 'w')
    436 
    437 fdef.write(mcgen('''
    438 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
    439 
    440 /*
    441  * schema-defined QAPI visitor functions
    442  *
    443  * Copyright IBM, Corp. 2011
    444  *
    445  * Authors:
    446  *  Anthony Liguori   <aliguori (at] us.ibm.com>
    447  *
    448  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
    449  * See the COPYING.LIB file in the top-level directory.
    450  *
    451  */
    452 
    453 #include "qemu-common.h"
    454 #include "%(header)s"
    455 ''',
    456                  header=basename(h_file)))
    457 
    458 fdecl.write(mcgen('''
    459 /* THIS FILE IS AUTOMATICALLY GENERATED, DO NOT MODIFY */
    460 
    461 /*
    462  * schema-defined QAPI visitor function
    463  *
    464  * Copyright IBM, Corp. 2011
    465  *
    466  * Authors:
    467  *  Anthony Liguori   <aliguori (at] us.ibm.com>
    468  *
    469  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
    470  * See the COPYING.LIB file in the top-level directory.
    471  *
    472  */
    473 
    474 #ifndef %(guard)s
    475 #define %(guard)s
    476 
    477 #include "qapi/visitor.h"
    478 #include "%(prefix)sqapi-types.h"
    479 
    480 ''',
    481                   prefix=prefix, guard=guardname(h_file)))
    482 
    483 exprs = parse_schema(sys.stdin)
    484 
    485 # to avoid header dependency hell, we always generate declarations
    486 # for built-in types in our header files and simply guard them
    487 fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
    488 for typename in builtin_types:
    489     fdecl.write(generate_declaration(typename, None, genlist=True,
    490                                      builtin_type=True))
    491 fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
    492 
    493 # ...this doesn't work for cases where we link in multiple objects that
    494 # have the functions defined, so we use -b option to provide control
    495 # over these cases
    496 if do_builtins:
    497     fdef.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DEF"))
    498     for typename in builtin_types:
    499         fdef.write(generate_visit_list(typename, None))
    500     fdef.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DEF"))
    501 
    502 for expr in exprs:
    503     if expr.has_key('type'):
    504         ret = generate_visit_struct(expr)
    505         ret += generate_visit_list(expr['type'], expr['data'])
    506         fdef.write(ret)
    507 
    508         ret = generate_declaration(expr['type'], expr['data'])
    509         fdecl.write(ret)
    510     elif expr.has_key('union'):
    511         ret = generate_visit_union(expr)
    512         ret += generate_visit_list(expr['union'], expr['data'])
    513         fdef.write(ret)
    514 
    515         ret = generate_decl_enum('%sKind' % expr['union'], expr['data'].keys())
    516         ret += generate_declaration(expr['union'], expr['data'])
    517         fdecl.write(ret)
    518     elif expr.has_key('enum'):
    519         ret = generate_visit_list(expr['enum'], expr['data'])
    520         ret += generate_visit_enum(expr['enum'], expr['data'])
    521         fdef.write(ret)
    522 
    523         ret = generate_decl_enum(expr['enum'], expr['data'])
    524         ret += generate_enum_declaration(expr['enum'], expr['data'])
    525         fdecl.write(ret)
    526 
    527 fdecl.write('''
    528 #endif
    529 ''')
    530 
    531 fdecl.flush()
    532 fdecl.close()
    533 
    534 fdef.flush()
    535 fdef.close()
    536