Home | History | Annotate | Download | only in scripts
      1 #
      2 # QAPI types generator
      3 #
      4 # Copyright IBM, Corp. 2011
      5 #
      6 # Authors:
      7 #  Anthony Liguori <aliguori (at] us.ibm.com>
      8 #
      9 # This work is licensed under the terms of the GNU GPLv2.
     10 # See the COPYING.LIB file in the top-level directory.
     11 
     12 from ordereddict import OrderedDict
     13 from qapi import *
     14 import sys
     15 import os
     16 import getopt
     17 import errno
     18 
     19 def generate_fwd_struct(name, members, builtin_type=False):
     20     if builtin_type:
     21         return mcgen('''
     22 
     23 typedef struct %(name)sList
     24 {
     25     union {
     26         %(type)s value;
     27         uint64_t padding;
     28     };
     29     struct %(name)sList *next;
     30 } %(name)sList;
     31 ''',
     32                      type=c_type(name),
     33                      name=name)
     34 
     35     return mcgen('''
     36 
     37 typedef struct %(name)s %(name)s;
     38 
     39 typedef struct %(name)sList
     40 {
     41     union {
     42         %(name)s *value;
     43         uint64_t padding;
     44     };
     45     struct %(name)sList *next;
     46 } %(name)sList;
     47 ''',
     48                  name=name)
     49 
     50 def generate_fwd_enum_struct(name, members):
     51     return mcgen('''
     52 typedef struct %(name)sList
     53 {
     54     union {
     55         %(name)s value;
     56         uint64_t padding;
     57     };
     58     struct %(name)sList *next;
     59 } %(name)sList;
     60 ''',
     61                  name=name)
     62 
     63 def generate_struct_fields(members):
     64     ret = ''
     65 
     66     for argname, argentry, optional, structured in parse_args(members):
     67         if optional:
     68             ret += mcgen('''
     69     bool has_%(c_name)s;
     70 ''',
     71                          c_name=c_var(argname))
     72         if structured:
     73             push_indent()
     74             ret += generate_struct({ "field": argname, "data": argentry})
     75             pop_indent()
     76         else:
     77             ret += mcgen('''
     78     %(c_type)s %(c_name)s;
     79 ''',
     80                      c_type=c_type(argentry), c_name=c_var(argname))
     81 
     82     return ret
     83 
     84 def generate_struct(expr):
     85 
     86     structname = expr.get('type', "")
     87     fieldname = expr.get('field', "")
     88     members = expr['data']
     89     base = expr.get('base')
     90 
     91     ret = mcgen('''
     92 struct %(name)s
     93 {
     94 ''',
     95           name=structname)
     96 
     97     if base:
     98         ret += generate_struct_fields({'base': base})
     99 
    100     ret += generate_struct_fields(members)
    101 
    102     if len(fieldname):
    103         fieldname = " " + fieldname
    104     ret += mcgen('''
    105 }%(field)s;
    106 ''',
    107             field=fieldname)
    108 
    109     return ret
    110 
    111 def generate_enum_lookup(name, values):
    112     ret = mcgen('''
    113 const char *%(name)s_lookup[] = {
    114 ''',
    115                          name=name)
    116     i = 0
    117     for value in values:
    118         ret += mcgen('''
    119     "%(value)s",
    120 ''',
    121                      value=value)
    122 
    123     ret += mcgen('''
    124     NULL,
    125 };
    126 
    127 ''')
    128     return ret
    129 
    130 def generate_enum_name(name):
    131     if name.isupper():
    132         return c_fun(name, False)
    133     new_name = ''
    134     for c in c_fun(name, False):
    135         if c.isupper():
    136             new_name += '_'
    137         new_name += c
    138     return new_name.lstrip('_').upper()
    139 
    140 def generate_enum(name, values):
    141     lookup_decl = mcgen('''
    142 extern const char *%(name)s_lookup[];
    143 ''',
    144                 name=name)
    145 
    146     enum_decl = mcgen('''
    147 typedef enum %(name)s
    148 {
    149 ''',
    150                 name=name)
    151 
    152     # append automatically generated _MAX value
    153     enum_values = values + [ 'MAX' ]
    154 
    155     i = 0
    156     for value in enum_values:
    157         enum_decl += mcgen('''
    158     %(abbrev)s_%(value)s = %(i)d,
    159 ''',
    160                      abbrev=de_camel_case(name).upper(),
    161                      value=generate_enum_name(value),
    162                      i=i)
    163         i += 1
    164 
    165     enum_decl += mcgen('''
    166 } %(name)s;
    167 ''',
    168                  name=name)
    169 
    170     return lookup_decl + enum_decl
    171 
    172 def generate_anon_union_qtypes(expr):
    173 
    174     name = expr['union']
    175     members = expr['data']
    176 
    177     ret = mcgen('''
    178 const int %(name)s_qtypes[QTYPE_MAX] = {
    179 ''',
    180     name=name)
    181 
    182     for key in members:
    183         qapi_type = members[key]
    184         if builtin_type_qtypes.has_key(qapi_type):
    185             qtype = builtin_type_qtypes[qapi_type]
    186         elif find_struct(qapi_type):
    187             qtype = "QTYPE_QDICT"
    188         elif find_union(qapi_type):
    189             qtype = "QTYPE_QDICT"
    190         else:
    191             assert False, "Invalid anonymous union member"
    192 
    193         ret += mcgen('''
    194     [ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
    195 ''',
    196         qtype = qtype,
    197         abbrev = de_camel_case(name).upper(),
    198         enum = c_fun(de_camel_case(key),False).upper())
    199 
    200     ret += mcgen('''
    201 };
    202 ''')
    203     return ret
    204 
    205 
    206 def generate_union(expr):
    207 
    208     name = expr['union']
    209     typeinfo = expr['data']
    210 
    211     base = expr.get('base')
    212     discriminator = expr.get('discriminator')
    213 
    214     ret = mcgen('''
    215 struct %(name)s
    216 {
    217     %(name)sKind kind;
    218     union {
    219         void *data;
    220 ''',
    221                 name=name)
    222 
    223     for key in typeinfo:
    224         ret += mcgen('''
    225         %(c_type)s %(c_name)s;
    226 ''',
    227                      c_type=c_type(typeinfo[key]),
    228                      c_name=c_fun(key))
    229 
    230     ret += mcgen('''
    231     };
    232 ''')
    233 
    234     if base:
    235         base_fields = find_struct(base)['data']
    236         if discriminator:
    237             base_fields = base_fields.copy()
    238             del base_fields[discriminator]
    239         ret += generate_struct_fields(base_fields)
    240     else:
    241         assert not discriminator
    242 
    243     ret += mcgen('''
    244 };
    245 ''')
    246     if discriminator == {}:
    247         ret += mcgen('''
    248 extern const int %(name)s_qtypes[];
    249 ''',
    250             name=name)
    251 
    252 
    253     return ret
    254 
    255 def generate_type_cleanup_decl(name):
    256     ret = mcgen('''
    257 void qapi_free_%(type)s(%(c_type)s obj);
    258 ''',
    259                 c_type=c_type(name),type=name)
    260     return ret
    261 
    262 def generate_type_cleanup(name):
    263     ret = mcgen('''
    264 
    265 void qapi_free_%(type)s(%(c_type)s obj)
    266 {
    267     QapiDeallocVisitor *md;
    268     Visitor *v;
    269 
    270     if (!obj) {
    271         return;
    272     }
    273 
    274     md = qapi_dealloc_visitor_new();
    275     v = qapi_dealloc_get_visitor(md);
    276     visit_type_%(type)s(v, &obj, NULL, NULL);
    277     qapi_dealloc_visitor_cleanup(md);
    278 }
    279 ''',
    280                 c_type=c_type(name),type=name)
    281     return ret
    282 
    283 
    284 try:
    285     opts, args = getopt.gnu_getopt(sys.argv[1:], "chbp:o:",
    286                                    ["source", "header", "builtins",
    287                                     "prefix=", "output-dir="])
    288 except getopt.GetoptError, err:
    289     print str(err)
    290     sys.exit(1)
    291 
    292 output_dir = ""
    293 prefix = ""
    294 c_file = 'qapi-types.c'
    295 h_file = 'qapi-types.h'
    296 
    297 do_c = False
    298 do_h = False
    299 do_builtins = False
    300 
    301 for o, a in opts:
    302     if o in ("-p", "--prefix"):
    303         prefix = a
    304     elif o in ("-o", "--output-dir"):
    305         output_dir = a + "/"
    306     elif o in ("-c", "--source"):
    307         do_c = True
    308     elif o in ("-h", "--header"):
    309         do_h = True
    310     elif o in ("-b", "--builtins"):
    311         do_builtins = True
    312 
    313 if not do_c and not do_h:
    314     do_c = True
    315     do_h = True
    316 
    317 c_file = output_dir + prefix + c_file
    318 h_file = output_dir + prefix + h_file
    319 
    320 try:
    321     os.makedirs(output_dir)
    322 except os.error, e:
    323     if e.errno != errno.EEXIST:
    324         raise
    325 
    326 def maybe_open(really, name, opt):
    327     if really:
    328         return open(name, opt)
    329     else:
    330         import StringIO
    331         return StringIO.StringIO()
    332 
    333 fdef = maybe_open(do_c, c_file, 'w')
    334 fdecl = maybe_open(do_h, h_file, 'w')
    335 
    336 fdef.write(mcgen('''
    337 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
    338 
    339 /*
    340  * deallocation functions for schema-defined QAPI types
    341  *
    342  * Copyright IBM, Corp. 2011
    343  *
    344  * Authors:
    345  *  Anthony Liguori   <aliguori (at] us.ibm.com>
    346  *  Michael Roth      <mdroth (at] linux.vnet.ibm.com>
    347  *
    348  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
    349  * See the COPYING.LIB file in the top-level directory.
    350  *
    351  */
    352 
    353 #include "qapi/dealloc-visitor.h"
    354 #include "%(prefix)sqapi-types.h"
    355 #include "%(prefix)sqapi-visit.h"
    356 
    357 ''',             prefix=prefix))
    358 
    359 fdecl.write(mcgen('''
    360 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
    361 
    362 /*
    363  * schema-defined QAPI types
    364  *
    365  * Copyright IBM, Corp. 2011
    366  *
    367  * Authors:
    368  *  Anthony Liguori   <aliguori (at] us.ibm.com>
    369  *
    370  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
    371  * See the COPYING.LIB file in the top-level directory.
    372  *
    373  */
    374 
    375 #ifndef %(guard)s
    376 #define %(guard)s
    377 
    378 #include <stdbool.h>
    379 #include <stdint.h>
    380 
    381 ''',
    382                   guard=guardname(h_file)))
    383 
    384 exprs = parse_schema(sys.stdin)
    385 exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
    386 
    387 fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
    388 for typename in builtin_types:
    389     fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
    390 fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
    391 
    392 for expr in exprs:
    393     ret = "\n"
    394     if expr.has_key('type'):
    395         ret += generate_fwd_struct(expr['type'], expr['data'])
    396     elif expr.has_key('enum'):
    397         ret += generate_enum(expr['enum'], expr['data']) + "\n"
    398         ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
    399         fdef.write(generate_enum_lookup(expr['enum'], expr['data']))
    400     elif expr.has_key('union'):
    401         ret += generate_fwd_struct(expr['union'], expr['data']) + "\n"
    402         ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
    403         fdef.write(generate_enum_lookup('%sKind' % expr['union'], expr['data'].keys()))
    404         if expr.get('discriminator') == {}:
    405             fdef.write(generate_anon_union_qtypes(expr))
    406     else:
    407         continue
    408     fdecl.write(ret)
    409 
    410 # to avoid header dependency hell, we always generate declarations
    411 # for built-in types in our header files and simply guard them
    412 fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
    413 for typename in builtin_types:
    414     fdecl.write(generate_type_cleanup_decl(typename + "List"))
    415 fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
    416 
    417 # ...this doesn't work for cases where we link in multiple objects that
    418 # have the functions defined, so we use -b option to provide control
    419 # over these cases
    420 if do_builtins:
    421     fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
    422     for typename in builtin_types:
    423         fdef.write(generate_type_cleanup(typename + "List"))
    424     fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
    425 
    426 for expr in exprs:
    427     ret = "\n"
    428     if expr.has_key('type'):
    429         ret += generate_struct(expr) + "\n"
    430         ret += generate_type_cleanup_decl(expr['type'] + "List")
    431         fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
    432         ret += generate_type_cleanup_decl(expr['type'])
    433         fdef.write(generate_type_cleanup(expr['type']) + "\n")
    434     elif expr.has_key('union'):
    435         ret += generate_union(expr)
    436         ret += generate_type_cleanup_decl(expr['union'] + "List")
    437         fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n")
    438         ret += generate_type_cleanup_decl(expr['union'])
    439         fdef.write(generate_type_cleanup(expr['union']) + "\n")
    440     elif expr.has_key('enum'):
    441         ret += generate_type_cleanup_decl(expr['enum'] + "List")
    442         fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n")
    443     else:
    444         continue
    445     fdecl.write(ret)
    446 
    447 fdecl.write('''
    448 #endif
    449 ''')
    450 
    451 fdecl.flush()
    452 fdecl.close()
    453 
    454 fdef.flush()
    455 fdef.close()
    456