| OLD | NEW |
| (Empty) | |
| 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. |
| 5 |
| 6 """ Generator for C style prototypes and definitions """ |
| 7 |
| 8 import glob |
| 9 import os |
| 10 import re |
| 11 import sys |
| 12 |
| 13 from idl_log import ErrOut, InfoOut, WarnOut |
| 14 from idl_node import IDLAttribute, IDLNode |
| 15 from idl_ast import IDLAst |
| 16 from idl_option import GetOption, Option, ParseOptions |
| 17 from idl_outfile import IDLOutFile |
| 18 from idl_parser import ParseFiles |
| 19 from idl_c_proto import CGen, GetNodeComments, CommentLines, Comment |
| 20 from idl_generator import Generator, GeneratorByFile |
| 21 from idl_visitor import IDLVisitor |
| 22 |
| 23 Option('dstroot', 'Base directory of output', default=os.path.join('..', 'c')) |
| 24 Option('guard', 'Include guard prefix', default=os.path.join('ppapi', 'c')) |
| 25 |
| 26 |
| 27 # |
| 28 # PrototypeResolver |
| 29 # |
| 30 # A specialized visitor which traverses the AST, building a mapping of |
| 31 # Release names to Versions numbers and calculating a min version. |
| 32 # The mapping is applied to the File nodes within the AST. |
| 33 # |
| 34 class ProtoResolver(IDLVisitor): |
| 35 def __init__(self): |
| 36 IDLVisitor.__init__(self) |
| 37 self.struct_map = {} |
| 38 self.interface_map = {} |
| 39 |
| 40 def Arrive(self, node, ignore): |
| 41 if node.IsA('Member') and node.GetProperty('ref'): |
| 42 typeref = node.typelist.GetReleases()[0] |
| 43 if typeref.IsA('Struct'): |
| 44 nodelist = self.struct_map.get(typeref.GetName(), []) |
| 45 nodelist.append(node) |
| 46 self.struct_map[typeref.GetName()] = nodelist |
| 47 |
| 48 if node.IsA('Param'): |
| 49 typeref = node.typelist.GetReleases()[0] |
| 50 if typeref.IsA('Interface'): |
| 51 nodelist = self.struct_map.get(typeref.GetName(), []) |
| 52 nodelist.append(node) |
| 53 self.interface_map[typeref.GetName()] = nodelist |
| 54 |
| 55 return None |
| 56 |
| 57 |
| 58 def GetPathFromNode(filenode, relpath=None, ext=None): |
| 59 path, name = os.path.split(filenode.GetProperty('NAME')) |
| 60 if ext: name = os.path.splitext(name)[0] + ext |
| 61 if path: name = os.path.join(path, name) |
| 62 if relpath: name = os.path.join(relpath, name) |
| 63 name = os.path.normpath(name) |
| 64 return name |
| 65 |
| 66 |
| 67 def GetHeaderFromNode(filenode, relpath=None): |
| 68 return GetPathFromNode(filenode, relpath, ext='.h') |
| 69 |
| 70 |
| 71 def WriteGroupMarker(out, node, last_group): |
| 72 # If we are part of a group comment marker... |
| 73 if last_group and last_group != node.cls: |
| 74 pre = CommentLines(['*',' @}', '']) + '\n' |
| 75 else: |
| 76 pre = '\n' |
| 77 |
| 78 if node.cls in ['Typedef', 'Interface', 'Struct', 'Enum']: |
| 79 if last_group != node.cls: |
| 80 pre += CommentLines(['*',' @addtogroup %ss' % node.cls, ' @{', '']) |
| 81 last_group = node.cls |
| 82 else: |
| 83 last_group = None |
| 84 out.Write(pre) |
| 85 return last_group |
| 86 |
| 87 |
| 88 def GenerateHeader(out, filenode, releases): |
| 89 cgen = CGen() |
| 90 pref = '' |
| 91 do_comments = True |
| 92 |
| 93 # Generate definitions. |
| 94 last_group = None |
| 95 top_types = ['Typedef', 'Interface', 'Struct', 'Enum', 'Inline'] |
| 96 for node in filenode.GetListOf(*top_types): |
| 97 # Skip if this node is not in this release |
| 98 if not node.InReleases(releases): |
| 99 print "Skiping %s" % node |
| 100 continue |
| 101 |
| 102 # End/Start group marker |
| 103 if do_comments: |
| 104 last_group = WriteGroupMarker(out, node, last_group) |
| 105 |
| 106 if node.IsA('Inline'): |
| 107 item = node.GetProperty('VALUE') |
| 108 # If 'C++' use __cplusplus wrapper |
| 109 if node.GetName() == 'cc': |
| 110 item = '#ifdef __cplusplus\n%s\n#endif /* __cplusplus */\n\n' % item |
| 111 # If not C++ or C, then skip it |
| 112 elif not node.GetName() == 'c': |
| 113 continue |
| 114 if item: out.Write(item) |
| 115 continue |
| 116 |
| 117 # |
| 118 # Otherwise we are defining a file level object, so generate the |
| 119 # correct document notation. |
| 120 # |
| 121 item = cgen.Define(node, releases, prefix=pref, comment=True) |
| 122 if not item: continue |
| 123 asize = node.GetProperty('assert_size()') |
| 124 if asize: |
| 125 name = '%s%s' % (pref, node.GetName()) |
| 126 if node.IsA('Struct'): |
| 127 form = 'PP_COMPILE_ASSERT_STRUCT_SIZE_IN_BYTES(%s, %s);\n' |
| 128 elif node.IsA('Enum'): |
| 129 if node.GetProperty('notypedef'): |
| 130 form = 'PP_COMPILE_ASSERT_ENUM_SIZE_IN_BYTES(%s, %s);\n' |
| 131 else: |
| 132 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n' |
| 133 else: |
| 134 form = 'PP_COMPILE_ASSERT_SIZE_IN_BYTES(%s, %s);\n' |
| 135 item += form % (name, asize[0]) |
| 136 |
| 137 if item: out.Write(item) |
| 138 if last_group: |
| 139 out.Write(CommentLines(['*',' @}', '']) + '\n') |
| 140 |
| 141 |
| 142 class HGen(GeneratorByFile): |
| 143 def __init__(self): |
| 144 Generator.__init__(self, 'C Header', 'cgen', 'Generate the C headers.') |
| 145 |
| 146 def GenerateFile(self, filenode, releases, options): |
| 147 savename = GetHeaderFromNode(filenode, GetOption('dstroot')) |
| 148 my_min, my_max = filenode.GetMinMax(releases) |
| 149 if my_min > releases[-1] or my_max < releases[0]: |
| 150 if os.path.isfile(savename): |
| 151 print "Removing stale %s for this range." % filenode.GetName() |
| 152 os.remove(os.path.realpath(savename)) |
| 153 return False |
| 154 |
| 155 out = IDLOutFile(savename) |
| 156 self.GenerateHead(out, filenode, releases, options) |
| 157 self.GenerateBody(out, filenode, releases, options) |
| 158 self.GenerateTail(out, filenode, releases, options) |
| 159 return out.Close() |
| 160 |
| 161 def GenerateHead(self, out, filenode, releases, options): |
| 162 __pychecker__ = 'unusednames=options' |
| 163 |
| 164 proto = ProtoResolver() |
| 165 proto.Visit(filenode, None) |
| 166 |
| 167 cgen = CGen() |
| 168 gpath = GetOption('guard') |
| 169 def_guard = GetHeaderFromNode(filenode, relpath=gpath) |
| 170 def_guard = def_guard.replace(os.sep,'_').replace('.','_').upper() + '_' |
| 171 |
| 172 cright_node = filenode.GetChildren()[0] |
| 173 assert(cright_node.IsA('Copyright')) |
| 174 fileinfo = filenode.GetChildren()[1] |
| 175 assert(fileinfo.IsA('Comment')) |
| 176 |
| 177 out.Write('%s\n' % cgen.Copyright(cright_node)) |
| 178 |
| 179 # Wrap the From ... modified ... comment if it would be >80 characters. |
| 180 from_text = 'From %s' % GetPathFromNode(filenode).replace(os.sep, '/') |
| 181 modified_text = 'modified %s.' % ( |
| 182 filenode.GetProperty('DATETIME')) |
| 183 if len(from_text) + len(modified_text) < 74: |
| 184 out.Write('/* %s %s */\n\n' % (from_text, modified_text)) |
| 185 else: |
| 186 out.Write('/* %s,\n * %s\n */\n\n' % (from_text, modified_text)) |
| 187 |
| 188 out.Write('#ifndef %s\n#define %s\n\n' % (def_guard, def_guard)) |
| 189 # Generate set of includes |
| 190 |
| 191 deps = set() |
| 192 for release in releases: |
| 193 deps |= filenode.GetDeps(release) |
| 194 |
| 195 includes = set([]) |
| 196 for dep in deps: |
| 197 depfile = dep.GetProperty('FILE') |
| 198 if depfile: |
| 199 includes.add(depfile) |
| 200 includes = [GetHeaderFromNode( |
| 201 include, relpath=gpath).replace(os.sep, '/') for include in includes] |
| 202 includes.append('ppapi/c/pp_macros.h') |
| 203 |
| 204 # Assume we need stdint if we "include" C or C++ code |
| 205 if filenode.GetListOf('Include'): |
| 206 includes.append('ppapi/c/pp_stdint.h') |
| 207 |
| 208 includes = sorted(set(includes)) |
| 209 cur_include = GetHeaderFromNode(filenode, |
| 210 relpath=gpath).replace(os.sep, '/') |
| 211 for include in includes: |
| 212 if include == cur_include: continue |
| 213 out.Write('#include "%s"\n' % include) |
| 214 |
| 215 # Generate Prototypes |
| 216 if proto.struct_map: |
| 217 out.Write('\n/* Struct prototypes */\n') |
| 218 for struct in proto.struct_map: |
| 219 out.Write('struct %s;\n' % struct) |
| 220 |
| 221 # Create a macro for the highest available release number. |
| 222 if filenode.GetProperty('NAME').endswith('pp_macros.idl'): |
| 223 releasestr = ' '.join(releases) |
| 224 if releasestr: |
| 225 release_numbers = re.findall('[\d\_]+', releasestr) |
| 226 release = re.findall('\d+', release_numbers[-1])[0] |
| 227 if release: |
| 228 out.Write('\n#define PPAPI_RELEASE %s\n' % release) |
| 229 |
| 230 # Generate all interface defines |
| 231 out.Write('\n') |
| 232 for node in filenode.GetListOf('Interface'): |
| 233 idefs = '' |
| 234 macro = cgen.GetInterfaceMacro(node) |
| 235 unique = node.GetUniqueReleases(releases) |
| 236 |
| 237 # Skip this interface if there are no matching versions |
| 238 if not unique: continue |
| 239 |
| 240 for rel in unique: |
| 241 version = node.GetVersion(rel) |
| 242 name = cgen.GetInterfaceString(node, version) |
| 243 strver = str(version).replace('.', '_') |
| 244 idefs += cgen.GetDefine('%s_%s' % (macro, strver), '"%s"' % name) |
| 245 idefs += cgen.GetDefine(macro, '%s_%s' % (macro, strver)) + '\n' |
| 246 out.Write(idefs) |
| 247 |
| 248 # Generate the @file comment |
| 249 out.Write('%s\n' % Comment(fileinfo, prefix='*\n @file')) |
| 250 |
| 251 def GenerateBody(self, out, filenode, releases, options): |
| 252 __pychecker__ = 'unusednames=options' |
| 253 GenerateHeader(out, filenode, releases) |
| 254 |
| 255 def GenerateTail(self, out, filenode, releases, options): |
| 256 __pychecker__ = 'unusednames=options,releases' |
| 257 gpath = GetOption('guard') |
| 258 def_guard = GetPathFromNode(filenode, relpath=gpath, ext='.h') |
| 259 def_guard = def_guard.replace(os.sep,'_').replace('.','_').upper() + '_' |
| 260 out.Write('#endif /* %s */\n\n' % def_guard) |
| 261 |
| 262 |
| 263 hgen = HGen() |
| 264 |
| 265 def main(args): |
| 266 # Default invocation will verify the golden files are unchanged. |
| 267 failed = 0 |
| 268 if not args: |
| 269 args = ['--wnone', '--diff', '--test', '--dstroot=.'] |
| 270 |
| 271 ParseOptions(args) |
| 272 |
| 273 idldir = os.path.split(sys.argv[0])[0] |
| 274 idldir = os.path.join(idldir, 'test_cgen', '*.idl') |
| 275 filenames = glob.glob(idldir) |
| 276 ast = ParseFiles(filenames) |
| 277 if hgen.GenerateRelease(ast, 'M14', {}): |
| 278 print "Golden file for M14 failed." |
| 279 failed = 1 |
| 280 else: |
| 281 print "Golden file for M14 passed." |
| 282 |
| 283 |
| 284 idldir = os.path.split(sys.argv[0])[0] |
| 285 idldir = os.path.join(idldir, 'test_cgen_range', '*.idl') |
| 286 filenames = glob.glob(idldir) |
| 287 |
| 288 ast = ParseFiles(filenames) |
| 289 if hgen.GenerateRange(ast, ['M13', 'M14', 'M15'], {}): |
| 290 print "Golden file for M13-M15 failed." |
| 291 failed =1 |
| 292 else: |
| 293 print "Golden file for M13-M15 passed." |
| 294 |
| 295 return failed |
| 296 |
| 297 if __name__ == '__main__': |
| 298 sys.exit(main(sys.argv[1:])) |
| 299 |
| OLD | NEW |