| #!/usr/bin/env python |
| # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ Generator for C style prototypes and definitions """ |
| |
| import glob |
| import os |
| import sys |
| |
| from idl_log import ErrOut, InfoOut, WarnOut |
| from idl_node import IDLNode |
| from idl_ast import IDLAst |
| from idl_option import GetOption, Option, ParseOptions |
| from idl_parser import ParseFiles |
| |
| Option('cgen_debug', 'Debug generate.') |
| |
| class CGenError(Exception): |
| def __init__(self, msg): |
| self.value = msg |
| |
| def __str__(self): |
| return repr(self.value) |
| |
| |
| def CommentLines(lines, tabs=0): |
| # Generate a C style comment block by prepending the block with '<tab>/*' |
| # and adding a '<tab> *' per line. |
| tab = ' ' * tabs |
| |
| out = '%s/*' % tab + ('\n%s *' % tab).join(lines) |
| |
| # Add a terminating ' */' unless the last line is blank which would mean it |
| # already has ' *' |
| if not lines[-1]: |
| out += '/\n' |
| else: |
| out += ' */\n' |
| return out |
| |
| def Comment(node, prefix=None, tabs=0): |
| # Generate a comment block from the provided Comment node. |
| comment = node.GetName() |
| lines = comment.split('\n') |
| |
| # If an option prefix is provided, then prepend that to the comment |
| # for this node. |
| if prefix: |
| prefix_lines = prefix.split('\n') |
| # If both the prefix and comment start with a blank line ('*') remove |
| # the extra one. |
| if prefix_lines[0] == '*' and lines[0] == '*': |
| lines = prefix_lines + lines[1:] |
| else: |
| lines = prefix_lines + lines; |
| return CommentLines(lines, tabs) |
| |
| def GetNodeComments(node, tabs=0): |
| # Generate a comment block joining all comment nodes which are children of |
| # the provided node. |
| comment_txt = '' |
| for doc in node.GetListOf('Comment'): |
| comment_txt += Comment(doc, tabs=tabs) |
| return comment_txt |
| |
| |
| class CGen(object): |
| # TypeMap |
| # |
| # TypeMap modifies how an object is stored or passed, for example pointers |
| # are passed as 'const' if they are 'in' parameters, and structures are |
| # preceeded by the keyword 'struct' as well as using a pointer. |
| # |
| TypeMap = { |
| 'Array': { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s*', |
| 'store': '%s', |
| 'return': '%s', |
| 'ref': '%s*' |
| }, |
| 'Callspec': { |
| 'in': '%s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'store': '%s', |
| 'return': '%s' |
| }, |
| 'Enum': { |
| 'in': '%s', |
| 'inout': '%s*', |
| 'out': '%s*', |
| 'store': '%s', |
| 'return': '%s' |
| }, |
| 'Interface': { |
| 'in': 'const %s*', |
| 'inout': '%s*', |
| 'out': '%s**', |
| 'return': '%s*', |
| 'store': '%s*' |
| }, |
| 'Struct': { |
| 'in': 'const %s*', |
| 'inout': '%s*', |
| 'out': '%s*', |
| 'return': ' %s*', |
| 'store': '%s', |
| 'ref': '%s*' |
| }, |
| 'blob_t': { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'return': '%s', |
| 'store': '%s' |
| }, |
| 'mem_t': { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'return': '%s', |
| 'store': '%s' |
| }, |
| 'mem_ptr_t': { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'return': '%s', |
| 'store': '%s' |
| }, |
| 'str_t': { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'return': 'const %s', |
| 'store': '%s' |
| }, |
| 'cstr_t': { |
| 'in': '%s', |
| 'inout': '%s*', |
| 'out': '%s*', |
| 'return': '%s', |
| 'store': '%s' |
| }, |
| 'TypeValue': { |
| 'in': '%s', |
| 'constptr_in': 'const %s*', # So we can use const* for PP_Var sometimes. |
| 'inout': '%s*', |
| 'out': '%s*', |
| 'return': '%s', |
| 'store': '%s' |
| }, |
| } |
| |
| |
| # |
| # RemapName |
| # |
| # A diction array of PPAPI types that are converted to language specific |
| # types before being returned by by the C generator |
| # |
| RemapName = { |
| 'blob_t': 'void**', |
| 'float_t': 'float', |
| 'double_t': 'double', |
| 'handle_t': 'int', |
| 'mem_t': 'void*', |
| 'mem_ptr_t': 'void**', |
| 'str_t': 'char*', |
| 'cstr_t': 'const char*', |
| 'interface_t' : 'const void*' |
| } |
| |
| # Tell how to handle pointers to GL types. |
| for gltype in ['GLbitfield', 'GLboolean', 'GLbyte', 'GLclampf', |
| 'GLclampx', 'GLenum', 'GLfixed', 'GLfloat', 'GLint', |
| 'GLintptr', 'GLshort', 'GLsizei', 'GLsizeiptr', |
| 'GLubyte', 'GLuint', 'GLushort']: |
| ptrtype = gltype + '_ptr_t' |
| TypeMap[ptrtype] = { |
| 'in': 'const %s', |
| 'inout': '%s', |
| 'out': '%s', |
| 'return': 'const %s', |
| 'store': '%s' |
| } |
| RemapName[ptrtype] = '%s*' % gltype |
| |
| def __init__(self): |
| self.dbg_depth = 0 |
| |
| # |
| # Debug Logging functions |
| # |
| def Log(self, txt): |
| if not GetOption('cgen_debug'): return |
| tabs = ' ' * self.dbg_depth |
| print '%s%s' % (tabs, txt) |
| |
| def LogEnter(self, txt): |
| if txt: self.Log(txt) |
| self.dbg_depth += 1 |
| |
| def LogExit(self, txt): |
| self.dbg_depth -= 1 |
| if txt: self.Log(txt) |
| |
| |
| def GetDefine(self, name, value): |
| out = '#define %s %s' % (name, value) |
| if len(out) > 80: |
| out = '#define %s \\\n %s' % (name, value) |
| return '%s\n' % out |
| |
| # |
| # Interface strings |
| # |
| def GetMacroHelper(self, node): |
| macro = node.GetProperty('macro') |
| if macro: return macro |
| name = node.GetName() |
| name = name.upper() |
| return "%s_INTERFACE" % name |
| |
| def GetInterfaceMacro(self, node, version = None): |
| name = self.GetMacroHelper(node) |
| if version is None: |
| return name |
| return '%s_%s' % (name, str(version).replace('.', '_')) |
| |
| def GetInterfaceString(self, node, version = None): |
| # If an interface name is specified, use that |
| name = node.GetProperty('iname') |
| if not name: |
| # Otherwise, the interface name is the object's name |
| # With '_Dev' replaced by '(Dev)' if it's a Dev interface. |
| name = node.GetName() |
| if name.endswith('_Dev'): |
| name = '%s(Dev)' % name[:-4] |
| if version is None: |
| return name |
| return "%s;%s" % (name, version) |
| |
| |
| # |
| # Return the array specification of the object. |
| # |
| def GetArraySpec(self, node): |
| assert(node.cls == 'Array') |
| fixed = node.GetProperty('FIXED') |
| if fixed: |
| return '[%s]' % fixed |
| else: |
| return '[]' |
| |
| # |
| # GetTypeName |
| # |
| # For any valid 'typed' object such as Member or Typedef |
| # the typenode object contains the typename |
| # |
| # For a given node return the type name by passing mode. |
| # |
| def GetTypeName(self, node, release, prefix=''): |
| self.LogEnter('GetTypeName of %s rel=%s' % (node, release)) |
| |
| # For Members, Params, and Typedefs get the type it refers to otherwise |
| # the node in question is it's own type (struct, union etc...) |
| if node.IsA('Member', 'Param', 'Typedef'): |
| typeref = node.GetType(release) |
| else: |
| typeref = node |
| |
| if typeref is None: |
| node.Error('No type at release %s.' % release) |
| raise CGenError('No type for %s' % node) |
| |
| # If the type is a (BuiltIn) Type then return it's name |
| # remapping as needed |
| if typeref.IsA('Type'): |
| name = CGen.RemapName.get(typeref.GetName(), None) |
| if name is None: name = typeref.GetName() |
| name = '%s%s' % (prefix, name) |
| |
| # For Interfaces, use the name + version |
| elif typeref.IsA('Interface'): |
| rel = typeref.first_release[release] |
| name = 'struct %s%s' % (prefix, self.GetStructName(typeref, rel, True)) |
| |
| # For structures, preceed with 'struct' or 'union' as appropriate |
| elif typeref.IsA('Struct'): |
| if typeref.GetProperty('union'): |
| name = 'union %s%s' % (prefix, typeref.GetName()) |
| else: |
| name = 'struct %s%s' % (prefix, typeref.GetName()) |
| |
| # If it's an enum, or typedef then return the Enum's name |
| elif typeref.IsA('Enum', 'Typedef'): |
| if not typeref.LastRelease(release): |
| first = node.first_release[release] |
| ver = '_' + node.GetVersion(first).replace('.','_') |
| else: |
| ver = '' |
| # The enum may have skipped having a typedef, we need prefix with 'enum'. |
| if typeref.GetProperty('notypedef'): |
| name = 'enum %s%s%s' % (prefix, typeref.GetName(), ver) |
| else: |
| name = '%s%s%s' % (prefix, typeref.GetName(), ver) |
| |
| else: |
| raise RuntimeError('Getting name of non-type %s.' % node) |
| self.LogExit('GetTypeName %s is %s' % (node, name)) |
| return name |
| |
| |
| # |
| # GetRootType |
| # |
| # For a given node return basic type of that object. This is |
| # either a 'Type', 'Callspec', or 'Array' |
| # |
| def GetRootTypeMode(self, node, release, mode): |
| self.LogEnter('GetRootType of %s' % node) |
| # If it has an array spec, then treat it as an array regardless of type |
| if node.GetOneOf('Array'): |
| rootType = 'Array' |
| # Or if it has a callspec, treat it as a function |
| elif node.GetOneOf('Callspec'): |
| rootType, mode = self.GetRootTypeMode(node.GetType(release), release, |
| 'return') |
| |
| # If it's a plain typedef, try that object's root type |
| elif node.IsA('Member', 'Param', 'Typedef'): |
| rootType, mode = self.GetRootTypeMode(node.GetType(release), |
| release, mode) |
| |
| # If it's an Enum, then it's normal passing rules |
| elif node.IsA('Enum'): |
| rootType = node.cls |
| |
| # If it's an Interface or Struct, we may be passing by value |
| elif node.IsA('Interface', 'Struct'): |
| if mode == 'return': |
| if node.GetProperty('returnByValue'): |
| rootType = 'TypeValue' |
| else: |
| rootType = node.cls |
| else: |
| if node.GetProperty('passByValue'): |
| rootType = 'TypeValue' |
| else: |
| rootType = node.cls |
| |
| # If it's an Basic Type, check if it's a special type |
| elif node.IsA('Type'): |
| if node.GetName() in CGen.TypeMap: |
| rootType = node.GetName() |
| else: |
| rootType = 'TypeValue' |
| else: |
| raise RuntimeError('Getting root type of non-type %s.' % node) |
| self.LogExit('RootType is "%s"' % rootType) |
| return rootType, mode |
| |
| |
| def GetTypeByMode(self, node, release, mode): |
| self.LogEnter('GetTypeByMode of %s mode=%s release=%s' % |
| (node, mode, release)) |
| name = self.GetTypeName(node, release) |
| ntype, mode = self.GetRootTypeMode(node, release, mode) |
| out = CGen.TypeMap[ntype][mode] % name |
| self.LogExit('GetTypeByMode %s = %s' % (node, out)) |
| return out |
| |
| |
| # Get the passing mode of the object (in, out, inout). |
| def GetParamMode(self, node): |
| self.Log('GetParamMode for %s' % node) |
| if node.GetProperty('in'): return 'in' |
| if node.GetProperty('out'): return 'out' |
| if node.GetProperty('inout'): return 'inout' |
| if node.GetProperty('constptr_in'): return 'constptr_in' |
| return 'return' |
| |
| # |
| # GetComponents |
| # |
| # Returns the signature components of an object as a tuple of |
| # (rtype, name, arrays, callspec) where: |
| # rtype - The store or return type of the object. |
| # name - The name of the object. |
| # arrays - A list of array dimensions as [] or [<fixed_num>]. |
| # args - None if not a function, otherwise a list of parameters. |
| # |
| def GetComponents(self, node, release, mode): |
| self.LogEnter('GetComponents mode %s for %s %s' % (mode, node, release)) |
| |
| # Generate passing type by modifying root type |
| rtype = self.GetTypeByMode(node, release, mode) |
| # If this is an array output, change it from type* foo[] to type** foo. |
| # type* foo[] means an array of pointers to type, which is confusing. |
| arrayspec = [self.GetArraySpec(array) for array in node.GetListOf('Array')] |
| if mode == 'out' and len(arrayspec) == 1 and arrayspec[0] == '[]': |
| rtype += '*' |
| del arrayspec[0] |
| |
| if node.IsA('Enum', 'Interface', 'Struct'): |
| rname = node.GetName() |
| else: |
| rname = node.GetType(release).GetName() |
| |
| if rname in CGen.RemapName: |
| rname = CGen.RemapName[rname] |
| if '%' in rtype: |
| rtype = rtype % rname |
| name = node.GetName() |
| callnode = node.GetOneOf('Callspec') |
| if callnode: |
| callspec = [] |
| for param in callnode.GetListOf('Param'): |
| if not param.IsRelease(release): |
| continue |
| mode = self.GetParamMode(param) |
| ptype, pname, parray, pspec = self.GetComponents(param, release, mode) |
| callspec.append((ptype, pname, parray, pspec)) |
| else: |
| callspec = None |
| |
| self.LogExit('GetComponents: %s, %s, %s, %s' % |
| (rtype, name, arrayspec, callspec)) |
| return (rtype, name, arrayspec, callspec) |
| |
| |
| def Compose(self, rtype, name, arrayspec, callspec, prefix, func_as_ptr, |
| include_name, unsized_as_ptr): |
| self.LogEnter('Compose: %s %s' % (rtype, name)) |
| arrayspec = ''.join(arrayspec) |
| |
| # Switch unsized array to a ptr. NOTE: Only last element can be unsized. |
| if unsized_as_ptr and arrayspec[-2:] == '[]': |
| prefix += '*' |
| arrayspec=arrayspec[:-2] |
| |
| if not include_name: |
| name = prefix + arrayspec |
| else: |
| name = prefix + name + arrayspec |
| if callspec is None: |
| out = '%s %s' % (rtype, name) |
| else: |
| params = [] |
| for ptype, pname, parray, pspec in callspec: |
| params.append(self.Compose(ptype, pname, parray, pspec, '', True, |
| include_name=True, |
| unsized_as_ptr=unsized_as_ptr)) |
| if func_as_ptr: |
| name = '(*%s)' % name |
| if not params: |
| params = ['void'] |
| out = '%s %s(%s)' % (rtype, name, ', '.join(params)) |
| self.LogExit('Exit Compose: %s' % out) |
| return out |
| |
| # |
| # GetSignature |
| # |
| # Returns the 'C' style signature of the object |
| # prefix - A prefix for the object's name |
| # func_as_ptr - Formats a function as a function pointer |
| # include_name - If true, include member name in the signature. |
| # If false, leave it out. In any case, prefix is always |
| # included. |
| # include_version - if True, include version in the member name |
| # |
| def GetSignature(self, node, release, mode, prefix='', func_as_ptr=True, |
| include_name=True, include_version=False): |
| self.LogEnter('GetSignature %s %s as func=%s' % |
| (node, mode, func_as_ptr)) |
| rtype, name, arrayspec, callspec = self.GetComponents(node, release, mode) |
| if include_version: |
| name = self.GetStructName(node, release, True) |
| |
| # If not a callspec (such as a struct) use a ptr instead of [] |
| unsized_as_ptr = not callspec |
| |
| out = self.Compose(rtype, name, arrayspec, callspec, prefix, |
| func_as_ptr, include_name, unsized_as_ptr) |
| |
| self.LogExit('Exit GetSignature: %s' % out) |
| return out |
| |
| # Define a Typedef. |
| def DefineTypedef(self, node, releases, prefix='', comment=False): |
| __pychecker__ = 'unusednames=comment' |
| build_list = node.GetUniqueReleases(releases) |
| |
| out = 'typedef %s;\n' % self.GetSignature(node, build_list[-1], 'return', |
| prefix, True, |
| include_version=False) |
| # Version mangle any other versions |
| for index, rel in enumerate(build_list[:-1]): |
| out += '\n' |
| out += 'typedef %s;\n' % self.GetSignature(node, rel, 'return', |
| prefix, True, |
| include_version=True) |
| self.Log('DefineTypedef: %s' % out) |
| return out |
| |
| # Define an Enum. |
| def DefineEnum(self, node, releases, prefix='', comment=False): |
| __pychecker__ = 'unusednames=comment,releases' |
| self.LogEnter('DefineEnum %s' % node) |
| name = '%s%s' % (prefix, node.GetName()) |
| notypedef = node.GetProperty('notypedef') |
| unnamed = node.GetProperty('unnamed') |
| |
| if unnamed: |
| out = 'enum {' |
| elif notypedef: |
| out = 'enum %s {' % name |
| else: |
| out = 'typedef enum {' |
| enumlist = [] |
| for child in node.GetListOf('EnumItem'): |
| value = child.GetProperty('VALUE') |
| comment_txt = GetNodeComments(child, tabs=1) |
| if value: |
| item_txt = '%s%s = %s' % (prefix, child.GetName(), value) |
| else: |
| item_txt = '%s%s' % (prefix, child.GetName()) |
| enumlist.append('%s %s' % (comment_txt, item_txt)) |
| self.LogExit('Exit DefineEnum') |
| |
| if unnamed or notypedef: |
| out = '%s\n%s\n};\n' % (out, ',\n'.join(enumlist)) |
| else: |
| out = '%s\n%s\n} %s;\n' % (out, ',\n'.join(enumlist), name) |
| return out |
| |
| def DefineMember(self, node, releases, prefix='', comment=False): |
| __pychecker__ = 'unusednames=prefix,comment' |
| release = releases[0] |
| self.LogEnter('DefineMember %s' % node) |
| if node.GetProperty('ref'): |
| out = '%s;' % self.GetSignature(node, release, 'ref', '', True) |
| else: |
| out = '%s;' % self.GetSignature(node, release, 'store', '', True) |
| self.LogExit('Exit DefineMember') |
| return out |
| |
| def GetStructName(self, node, release, include_version=False): |
| suffix = '' |
| if include_version: |
| ver_num = node.GetVersion(release) |
| suffix = ('_%s' % ver_num).replace('.', '_') |
| return node.GetName() + suffix |
| |
| def DefineStructInternals(self, node, release, |
| include_version=False, comment=True): |
| channel = node.GetProperty('FILE').release_map.GetChannel(release) |
| if channel == 'dev': |
| channel_comment = ' /* dev */' |
| else: |
| channel_comment = '' |
| out = '' |
| if node.GetProperty('union'): |
| out += 'union %s {%s\n' % ( |
| self.GetStructName(node, release, include_version), channel_comment) |
| else: |
| out += 'struct %s {%s\n' % ( |
| self.GetStructName(node, release, include_version), channel_comment) |
| |
| channel = node.GetProperty('FILE').release_map.GetChannel(release) |
| # Generate Member Functions |
| members = [] |
| for child in node.GetListOf('Member'): |
| if channel == 'stable' and child.NodeIsDevOnly(): |
| continue |
| member = self.Define(child, [release], tabs=1, comment=comment) |
| if not member: |
| continue |
| members.append(member) |
| out += '%s\n};\n' % '\n'.join(members) |
| return out |
| |
| |
| def DefineUnversionedInterface(self, node, rel): |
| out = '\n' |
| if node.GetProperty('force_struct_namespace'): |
| # Duplicate the definition to put it in struct namespace. This |
| # attribute is only for legacy APIs like OpenGLES2 and new APIs |
| # must not use this. See http://crbug.com/411799 |
| out += self.DefineStructInternals(node, rel, |
| include_version=False, comment=True) |
| else: |
| # Define an unversioned typedef for the most recent version |
| out += 'typedef struct %s %s;\n' % ( |
| self.GetStructName(node, rel, include_version=True), |
| self.GetStructName(node, rel, include_version=False)) |
| return out |
| |
| |
| def DefineStruct(self, node, releases, prefix='', comment=False): |
| __pychecker__ = 'unusednames=comment,prefix' |
| self.LogEnter('DefineStruct %s' % node) |
| out = '' |
| build_list = node.GetUniqueReleases(releases) |
| |
| newest_stable = None |
| newest_dev = None |
| for rel in build_list: |
| channel = node.GetProperty('FILE').release_map.GetChannel(rel) |
| if channel == 'stable': |
| newest_stable = rel |
| if channel == 'dev': |
| newest_dev = rel |
| last_rel = build_list[-1] |
| |
| # TODO(bradnelson) : Bug 157017 finish multiversion support |
| if node.IsA('Struct'): |
| if len(build_list) != 1: |
| node.Error('Can not support multiple versions of node.') |
| assert len(build_list) == 1 |
| # Build the most recent one versioned, with comments |
| out = self.DefineStructInternals(node, last_rel, |
| include_version=False, comment=True) |
| |
| if node.IsA('Interface'): |
| # Build the most recent one versioned, with comments |
| out = self.DefineStructInternals(node, last_rel, |
| include_version=True, comment=True) |
| if last_rel == newest_stable: |
| out += self.DefineUnversionedInterface(node, last_rel) |
| |
| # Build the rest without comments and with the version number appended |
| for rel in build_list[0:-1]: |
| channel = node.GetProperty('FILE').release_map.GetChannel(rel) |
| # Skip dev channel interface versions that are |
| # Not the newest version, and |
| # Don't have an equivalent stable version. |
| if channel == 'dev' and rel != newest_dev: |
| if not node.DevInterfaceMatchesStable(rel): |
| continue |
| out += '\n' + self.DefineStructInternals(node, rel, |
| include_version=True, |
| comment=False) |
| if rel == newest_stable: |
| out += self.DefineUnversionedInterface(node, rel) |
| |
| self.LogExit('Exit DefineStruct') |
| return out |
| |
| |
| # |
| # Copyright and Comment |
| # |
| # Generate a comment or copyright block |
| # |
| def Copyright(self, node, cpp_style=False): |
| lines = node.GetName().split('\n') |
| if cpp_style: |
| return '//' + '\n//'.join(filter(lambda f: f != '', lines)) + '\n' |
| return CommentLines(lines) |
| |
| |
| def Indent(self, data, tabs=0): |
| """Handles indentation and 80-column line wrapping.""" |
| tab = ' ' * tabs |
| lines = [] |
| for line in data.split('\n'): |
| # Add indentation |
| line = tab + line |
| space_break = line.rfind(' ', 0, 80) |
| if len(line) <= 80 or 'http://' in line: |
| # Ignore normal line and URLs permitted by the style guide. |
| lines.append(line.rstrip()) |
| elif not '(' in line and space_break >= 0: |
| # Break long typedefs on nearest space. |
| lines.append(line[0:space_break]) |
| lines.append(' ' + line[space_break + 1:]) |
| else: |
| left = line.rfind('(') + 1 |
| args = line[left:].split(',') |
| orig_args = args |
| orig_left = left |
| # Try to split on '(arg1)' or '(arg1, arg2)', not '()' |
| while args[0][0] == ')': |
| left = line.rfind('(', 0, left - 1) + 1 |
| if left == 0: # No more parens, take the original option |
| args = orig_args |
| left = orig_left |
| break |
| args = line[left:].split(',') |
| |
| line_max = 0 |
| for arg in args: |
| if len(arg) > line_max: line_max = len(arg) |
| |
| if left + line_max >= 80: |
| indent = '%s ' % tab |
| args = (',\n%s' % indent).join([arg.strip() for arg in args]) |
| lines.append('%s\n%s%s' % (line[:left], indent, args)) |
| else: |
| indent = ' ' * (left - 1) |
| args = (',\n%s' % indent).join(args) |
| lines.append('%s%s' % (line[:left], args)) |
| return '\n'.join(lines) |
| |
| |
| # Define a top level object. |
| def Define(self, node, releases, tabs=0, prefix='', comment=False): |
| # If this request does not match unique release, or if the release is not |
| # available (possibly deprecated) then skip. |
| unique = node.GetUniqueReleases(releases) |
| if not unique or not node.InReleases(releases): |
| return '' |
| |
| self.LogEnter('Define %s tab=%d prefix="%s"' % (node,tabs,prefix)) |
| declmap = dict({ |
| 'Enum': CGen.DefineEnum, |
| 'Function': CGen.DefineMember, |
| 'Interface': CGen.DefineStruct, |
| 'Member': CGen.DefineMember, |
| 'Struct': CGen.DefineStruct, |
| 'Typedef': CGen.DefineTypedef |
| }) |
| |
| out = '' |
| func = declmap.get(node.cls, None) |
| if not func: |
| ErrOut.Log('Failed to define %s named %s' % (node.cls, node.GetName())) |
| define_txt = func(self, node, releases, prefix=prefix, comment=comment) |
| |
| comment_txt = GetNodeComments(node, tabs=0) |
| if comment_txt and comment: |
| out += comment_txt |
| out += define_txt |
| |
| indented_out = self.Indent(out, tabs) |
| self.LogExit('Exit Define') |
| return indented_out |
| |
| |
| # Clean a string representing an object definition and return then string |
| # as a single space delimited set of tokens. |
| def CleanString(instr): |
| instr = instr.strip() |
| instr = instr.split() |
| return ' '.join(instr) |
| |
| |
| # Test a file, by comparing all it's objects, with their comments. |
| def TestFile(filenode): |
| cgen = CGen() |
| |
| errors = 0 |
| for node in filenode.GetChildren()[2:]: |
| instr = node.GetOneOf('Comment') |
| if not instr: continue |
| instr.Dump() |
| instr = CleanString(instr.GetName()) |
| |
| outstr = cgen.Define(node, releases=['M14']) |
| if GetOption('verbose'): |
| print outstr + '\n' |
| outstr = CleanString(outstr) |
| |
| if instr != outstr: |
| ErrOut.Log('Failed match of\n>>%s<<\nto:\n>>%s<<\nFor:\n' % |
| (instr, outstr)) |
| node.Dump(1, comments=True) |
| errors += 1 |
| return errors |
| |
| |
| # Build and resolve the AST and compare each file individual. |
| def TestFiles(filenames): |
| if not filenames: |
| idldir = os.path.split(sys.argv[0])[0] |
| idldir = os.path.join(idldir, 'test_cgen', '*.idl') |
| filenames = glob.glob(idldir) |
| |
| filenames = sorted(filenames) |
| ast = ParseFiles(filenames) |
| |
| total_errs = 0 |
| for filenode in ast.GetListOf('File'): |
| errs = TestFile(filenode) |
| if errs: |
| ErrOut.Log('%s test failed with %d error(s).' % |
| (filenode.GetName(), errs)) |
| total_errs += errs |
| |
| if total_errs: |
| ErrOut.Log('Failed generator test.') |
| else: |
| InfoOut.Log('Passed generator test.') |
| return total_errs |
| |
| def main(args): |
| filenames = ParseOptions(args) |
| if GetOption('test'): |
| return TestFiles(filenames) |
| ast = ParseFiles(filenames) |
| cgen = CGen() |
| for f in ast.GetListOf('File'): |
| if f.GetProperty('ERRORS') > 0: |
| print 'Skipping %s' % f.GetName() |
| continue |
| for node in f.GetChildren()[2:]: |
| print cgen.Define(node, ast.releases, comment=True, prefix='tst_') |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main(sys.argv[1:])) |