[email protected] | 4a41db5 | 2012-01-13 00:02:56 | [diff] [blame] | 1 | #!/usr/bin/env python |
[email protected] | 2ec654a | 2012-01-10 17:47:00 | [diff] [blame] | 2 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import sys |
| 7 | |
| 8 | from idl_log import ErrOut, InfoOut, WarnOut |
| 9 | from idl_option import GetOption, Option, ParseOptions |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 10 | from idl_parser import ParseFiles |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 11 | |
| 12 | GeneratorList = [] |
| 13 | |
[email protected] | 14fa03b | 2011-08-26 21:10:38 | [diff] [blame] | 14 | Option('release', 'Which release to generate.', default='') |
[email protected] | a3aec32 | 2011-11-04 19:48:48 | [diff] [blame] | 15 | Option('range', 'Which ranges in the form of MIN,MAX.', default='start,end') |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 16 | |
| 17 | |
| 18 | # |
| 19 | # Generator |
| 20 | # |
| 21 | # Base class for generators. This class provides a mechanism for |
| 22 | # adding new generator objects to the IDL driver. To use this class |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 23 | # override the GenerateRelease and GenerateRange members, and |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 24 | # instantiate one copy of the class in the same module which defines it to |
| 25 | # register the generator. After the AST is generated, call the static Run |
| 26 | # member which will check every registered generator to see which ones have |
| 27 | # been enabled through command-line options. To enable a generator use the |
| 28 | # switches: |
| 29 | # --<sname> : To enable with defaults |
| 30 | # --<sname>_opt=<XXX,YYY=y> : To enable with generator specific options. |
| 31 | # |
| 32 | # NOTE: Generators still have access to global options |
| 33 | |
| 34 | class Generator(object): |
| 35 | def __init__(self, name, sname, desc): |
| 36 | self.name = name |
| 37 | self.run_switch = Option(sname, desc) |
| 38 | self.opt_switch = Option(sname + '_opt', 'Options for %s.' % sname, |
| 39 | default='') |
| 40 | GeneratorList.append(self) |
| 41 | self.errors = 0 |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 42 | self.skip_list = [] |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 43 | |
| 44 | def Error(self, msg): |
| 45 | ErrOut.Log('Error %s : %s' % (self.name, msg)) |
| 46 | self.errors += 1 |
| 47 | |
| 48 | def GetRunOptions(self): |
| 49 | options = {} |
| 50 | option_list = self.opt_switch.Get() |
| 51 | if option_list: |
| 52 | option_list = option_list.split(',') |
| 53 | for opt in option_list: |
| 54 | offs = opt.find('=') |
| 55 | if offs > 0: |
| 56 | options[opt[:offs]] = opt[offs+1:] |
| 57 | else: |
| 58 | options[opt] = True |
| 59 | return options |
| 60 | if self.run_switch.Get(): |
| 61 | return options |
| 62 | return None |
| 63 | |
| 64 | def Generate(self, ast, options): |
| 65 | self.errors = 0 |
| 66 | |
| 67 | rangestr = GetOption('range') |
| 68 | releasestr = GetOption('release') |
| 69 | |
[email protected] | a3aec32 | 2011-11-04 19:48:48 | [diff] [blame] | 70 | print "Found releases: %s" % ast.releases |
| 71 | |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 72 | # Generate list of files to ignore due to errors |
| 73 | for filenode in ast.GetListOf('File'): |
| 74 | # If this file has errors, skip it |
| 75 | if filenode.GetProperty('ERRORS') > 0: |
| 76 | self.skip_list.append(filenode) |
| 77 | continue |
| 78 | |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 79 | # Check for a range option which over-rides a release option |
[email protected] | 14fa03b | 2011-08-26 21:10:38 | [diff] [blame] | 80 | if not releasestr and rangestr: |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 81 | range_list = rangestr.split(',') |
| 82 | if len(range_list) != 2: |
| 83 | self.Error('Failed to generate for %s, incorrect range: "%s"' % |
| 84 | (self.name, rangestr)) |
| 85 | else: |
| 86 | vmin = range_list[0] |
| 87 | vmax = range_list[1] |
[email protected] | a3aec32 | 2011-11-04 19:48:48 | [diff] [blame] | 88 | |
| 89 | # Generate 'start' and 'end' represent first and last found. |
| 90 | if vmin == 'start': |
| 91 | vmin = ast.releases[0] |
| 92 | if vmax == 'end': |
| 93 | vmax = ast.releases[-1] |
| 94 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 95 | vmin = ast.releases.index(vmin) |
| 96 | vmax = ast.releases.index(vmax) + 1 |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 97 | releases = ast.releases[vmin:vmax] |
[email protected] | 55e1a70 | 2012-01-10 17:55:17 | [diff] [blame] | 98 | InfoOut.Log('Generate range %s of %s.' % (rangestr, self.name)) |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 99 | ret = self.GenerateRange(ast, releases, options) |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 100 | if ret < 0: |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 101 | self.Error('Failed to generate range %s : %s.' %(vmin, vmax)) |
[email protected] | 14fa03b | 2011-08-26 21:10:38 | [diff] [blame] | 102 | else: |
| 103 | InfoOut.Log('%s wrote %d files.' % (self.name, ret)) |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 104 | # Otherwise this should be a single release generation |
| 105 | else: |
[email protected] | 55e1a70 | 2012-01-10 17:55:17 | [diff] [blame] | 106 | if releasestr == 'start': |
| 107 | releasestr = ast.releases[0] |
| 108 | if releasestr == 'end': |
| 109 | releasestr = ast.releases[-1] |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 110 | if releasestr: |
[email protected] | 14fa03b | 2011-08-26 21:10:38 | [diff] [blame] | 111 | InfoOut.Log('Generate release %s of %s.' % (releasestr, self.name)) |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 112 | ret = self.GenerateRelease(ast, releasestr, options) |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 113 | if ret < 0: |
| 114 | self.Error('Failed to generate release %s.' % releasestr) |
| 115 | else: |
| 116 | InfoOut.Log('%s wrote %d files.' % (self.name, ret)) |
| 117 | |
| 118 | else: |
| 119 | self.Error('No range or release specified for %s.' % releasestr) |
| 120 | return self.errors |
| 121 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 122 | def GenerateRelease(self, ast, release, options): |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 123 | __pychecker__ = 'unusednames=ast,release,options' |
| 124 | self.Error("Undefined release generator.") |
| 125 | return 0 |
| 126 | |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 127 | def GenerateRange(self, ast, releases, options): |
| 128 | __pychecker__ = 'unusednames=ast,releases,options' |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 129 | self.Error("Undefined range generator.") |
| 130 | return 0 |
| 131 | |
| 132 | @staticmethod |
| 133 | def Run(ast): |
| 134 | fail_count = 0 |
| 135 | |
| 136 | # Check all registered generators if they should run. |
| 137 | for gen in GeneratorList: |
| 138 | options = gen.GetRunOptions() |
| 139 | if options is not None: |
| 140 | if gen.Generate(ast, options): |
| 141 | fail_count += 1 |
| 142 | return fail_count |
| 143 | |
| 144 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 145 | # |
| 146 | # GeneratorByFile |
| 147 | # |
| 148 | # A subclass of Generator for use of generators which have a one to one |
| 149 | # mapping between IDL sources and output files. To use, derive a new class |
| 150 | # which defines: |
| 151 | # |
| 152 | # GetOutFile - Returns an IDLOutFile based on filenode (name) and options |
| 153 | # GenerateHead - Writes the first part of the file (includes, etc...) |
| 154 | # GenerateBody - Writes the body of the file (definitions) |
| 155 | # GenerateTail - Writes the end of the file (closing include guard, etc...) |
| 156 | # |
| 157 | class GeneratorByFile(Generator): |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 158 | def GenerateFile(self, filenode, releases, options): |
| 159 | __pychecker__ = 'unusednames=filenode,releases,options' |
| 160 | self.Error("Undefined release generator.") |
| 161 | return 0 |
| 162 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 163 | def GenerateRelease(self, ast, release, options): |
| 164 | return self.GenerateRange(ast, [release], options) |
| 165 | |
| 166 | def GenerateRange(self, ast, releases, options): |
| 167 | # Get list of out files |
| 168 | outlist = GetOption('out') |
| 169 | if outlist: outlist = outlist.split(',') |
| 170 | |
| 171 | skipList = [] |
| 172 | cnt = 0 |
| 173 | for filenode in ast.GetListOf('File'): |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 174 | # Ignore files with errors |
| 175 | if filenode in self.skip_list: |
| 176 | continue |
| 177 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 178 | # Skip this file if not required |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 179 | if outlist and filenode.GetName() not in outlist: |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 180 | continue |
| 181 | |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 182 | # Create the output file and increment out count if there was a delta |
| 183 | if self.GenerateFile(filenode, releases, options): |
| 184 | cnt = cnt + 1 |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 185 | |
| 186 | for filenode in skipList: |
| 187 | errcnt = filenode.GetProperty('ERRORS') |
| 188 | ErrOut.Log('%s : Skipped because of %d errors.' % ( |
| 189 | filenode.GetName(), errcnt)) |
| 190 | |
| 191 | if skipList: |
| 192 | return -len(skipList) |
| 193 | |
| 194 | if GetOption('diff'): |
| 195 | return -cnt |
| 196 | return cnt |
| 197 | |
| 198 | |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 199 | check_release = 0 |
| 200 | check_range = 0 |
| 201 | |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 202 | class GeneratorReleaseTest(Generator): |
| 203 | def GenerateRelease(self, ast, release, options = {}): |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 204 | __pychecker__ = 'unusednames=ast,release,options' |
| 205 | global check_release |
| 206 | check_map = { |
| 207 | 'so_long': True, |
| 208 | 'MyOpt': 'XYZ', |
| 209 | 'goodbye': True |
| 210 | } |
| 211 | check_release = 1 |
| 212 | for item in check_map: |
| 213 | check_item = check_map[item] |
| 214 | option_item = options.get(item, None) |
| 215 | if check_item != option_item: |
| 216 | print 'Option %s is %s, expecting %s' % (item, option_item, check_item) |
| 217 | check_release = 0 |
| 218 | |
| 219 | if release != 'M14': |
| 220 | check_release = 0 |
| 221 | return check_release == 1 |
| 222 | |
[email protected] | 406793d | 2011-11-14 22:26:54 | [diff] [blame] | 223 | def GenerateRange(self, ast, releases, options): |
| 224 | __pychecker__ = 'unusednames=ast,releases,options' |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 225 | global check_range |
| 226 | check_range = 1 |
| 227 | return True |
| 228 | |
| 229 | def Test(): |
| 230 | __pychecker__ = 'unusednames=args' |
| 231 | global check_release |
| 232 | global check_range |
| 233 | |
| 234 | ParseOptions(['--testgen_opt=so_long,MyOpt=XYZ,goodbye']) |
| 235 | if Generator.Run('AST') != 0: |
| 236 | print 'Generate release: Failed.\n' |
| 237 | return -1 |
| 238 | |
| 239 | if check_release != 1 or check_range != 0: |
| 240 | print 'Gererate release: Failed to run.\n' |
| 241 | return -1 |
| 242 | |
| 243 | check_release = 0 |
| 244 | ParseOptions(['--testgen_opt="HELLO"', '--range=M14,M16']) |
| 245 | if Generator.Run('AST') != 0: |
| 246 | print 'Generate range: Failed.\n' |
| 247 | return -1 |
| 248 | |
| 249 | if check_release != 0 or check_range != 1: |
| 250 | print 'Gererate range: Failed to run.\n' |
| 251 | return -1 |
| 252 | |
| 253 | print 'Generator test: Pass' |
| 254 | return 0 |
| 255 | |
| 256 | |
| 257 | def Main(args): |
| 258 | if not args: return Test() |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 259 | filenames = ParseOptions(args) |
| 260 | ast = ParseFiles(filenames) |
| 261 | |
| 262 | return Generator.Run(ast) |
| 263 | |
| 264 | |
| 265 | if __name__ == '__main__': |
[email protected] | f731473 | 2011-08-24 23:03:06 | [diff] [blame] | 266 | GeneratorReleaseTest('Test Gen', 'testgen', 'Generator Class Test.') |
[email protected] | acc3d8e | 2011-08-22 22:17:40 | [diff] [blame] | 267 | sys.exit(Main(sys.argv[1:])) |
[email protected] | 55e1a70 | 2012-01-10 17:55:17 | [diff] [blame] | 268 | |