GN: Remove duplicate entries from sources lists
[chromium-blink-merge.git] / ppapi / generators / idl_generator.py
blob028a233435b903bf3419865573b7ad37837e59ce
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.
6 import sys
8 from idl_log import ErrOut, InfoOut, WarnOut
9 from idl_option import GetOption, Option, ParseOptions
10 from idl_parser import ParseFiles
12 GeneratorList = []
14 Option('out', 'List of output files', default='')
15 Option('release', 'Which release to generate.', default='')
16 Option('range', 'Which ranges in the form of MIN,MAX.', default='start,end')
18 class Generator(object):
19 """Base class for generators.
21 This class provides a mechanism for adding new generator objects to the IDL
22 driver. To use this class override the GenerateRelease and GenerateRange
23 members, and instantiate one copy of the class in the same module which
24 defines it to register the generator. After the AST is generated, call the
25 static Run member which will check every registered generator to see which
26 ones have been enabled through command-line options. To enable a generator
27 use the switches:
28 --<sname> : To enable with defaults
29 --<sname>_opt=<XXX,YYY=y> : To enable with generator specific options.
31 NOTE: Generators still have access to global options
32 """
34 def __init__(self, name, sname, desc):
35 self.name = name
36 self.run_switch = Option(sname, desc)
37 self.opt_switch = Option(sname + '_opt', 'Options for %s.' % sname,
38 default='')
39 GeneratorList.append(self)
40 self.errors = 0
41 self.skip_list = []
43 def Error(self, msg):
44 ErrOut.Log('Error %s : %s' % (self.name, msg))
45 self.errors += 1
47 def GetRunOptions(self):
48 options = {}
49 option_list = self.opt_switch.Get()
50 if option_list:
51 option_list = option_list.split(',')
52 for opt in option_list:
53 offs = opt.find('=')
54 if offs > 0:
55 options[opt[:offs]] = opt[offs+1:]
56 else:
57 options[opt] = True
58 return options
59 if self.run_switch.Get():
60 return options
61 return None
63 def Generate(self, ast, options):
64 self.errors = 0
66 rangestr = GetOption('range')
67 releasestr = GetOption('release')
69 print "Found releases: %s" % ast.releases
71 # Generate list of files to ignore due to errors
72 for filenode in ast.GetListOf('File'):
73 # If this file has errors, skip it
74 if filenode.GetProperty('ERRORS') > 0:
75 self.skip_list.append(filenode)
76 continue
78 # Check for a range option which over-rides a release option
79 if not releasestr and rangestr:
80 range_list = rangestr.split(',')
81 if len(range_list) != 2:
82 self.Error('Failed to generate for %s, incorrect range: "%s"' %
83 (self.name, rangestr))
84 else:
85 vmin = range_list[0]
86 vmax = range_list[1]
88 # Generate 'start' and 'end' represent first and last found.
89 if vmin == 'start':
90 vmin = ast.releases[0]
91 if vmax == 'end':
92 vmax = ast.releases[-1]
94 vmin = ast.releases.index(vmin)
95 vmax = ast.releases.index(vmax) + 1
96 releases = ast.releases[vmin:vmax]
97 InfoOut.Log('Generate range %s of %s.' % (rangestr, self.name))
98 ret = self.GenerateRange(ast, releases, options)
99 if ret < 0:
100 self.Error('Failed to generate range %s : %s.' %(vmin, vmax))
101 else:
102 InfoOut.Log('%s wrote %d files.' % (self.name, ret))
103 # Otherwise this should be a single release generation
104 else:
105 if releasestr == 'start':
106 releasestr = ast.releases[0]
107 if releasestr == 'end':
108 releasestr = ast.releases[-1]
110 if releasestr > ast.releases[-1]:
111 InfoOut.Log('There is no unique release for %s, using last release.' %
112 releasestr)
113 releasestr = ast.releases[-1]
115 if releasestr not in ast.releases:
116 self.Error('Release %s not in [%s].' %
117 (releasestr, ', '.join(ast.releases)))
119 if releasestr:
120 InfoOut.Log('Generate release %s of %s.' % (releasestr, self.name))
121 ret = self.GenerateRelease(ast, releasestr, options)
122 if ret < 0:
123 self.Error('Failed to generate release %s.' % releasestr)
124 else:
125 InfoOut.Log('%s wrote %d files.' % (self.name, ret))
127 else:
128 self.Error('No range or release specified for %s.' % releasestr)
129 return self.errors
131 def GenerateRelease(self, ast, release, options):
132 __pychecker__ = 'unusednames=ast,release,options'
133 self.Error("Undefined release generator.")
134 return 0
136 def GenerateRange(self, ast, releases, options):
137 __pychecker__ = 'unusednames=ast,releases,options'
138 self.Error("Undefined range generator.")
139 return 0
141 @staticmethod
142 def Run(ast):
143 fail_count = 0
145 # Check all registered generators if they should run.
146 for gen in GeneratorList:
147 options = gen.GetRunOptions()
148 if options is not None:
149 if gen.Generate(ast, options):
150 fail_count += 1
151 return fail_count
154 class GeneratorByFile(Generator):
155 """A simplified generator that generates one output file per IDL source file.
157 A subclass of Generator for use of generators which have a one to one
158 mapping between IDL sources and output files.
160 Derived classes should define GenerateFile.
163 def GenerateFile(self, filenode, releases, options):
164 """Generates an output file from the IDL source.
166 Returns true if the generated file is different than the previously
167 generated file.
169 __pychecker__ = 'unusednames=filenode,releases,options'
170 self.Error("Undefined release generator.")
171 return 0
173 def GenerateRelease(self, ast, release, options):
174 return self.GenerateRange(ast, [release], options)
176 def GenerateRange(self, ast, releases, options):
177 # Get list of out files
178 outlist = GetOption('out')
179 if outlist: outlist = outlist.split(',')
181 skipList = []
182 cnt = 0
183 for filenode in ast.GetListOf('File'):
184 # Ignore files with errors
185 if filenode in self.skip_list:
186 continue
188 # Skip this file if not required
189 if outlist and filenode.GetName() not in outlist:
190 continue
192 # Create the output file and increment out count if there was a delta
193 if self.GenerateFile(filenode, releases, options):
194 cnt = cnt + 1
196 for filenode in skipList:
197 errcnt = filenode.GetProperty('ERRORS')
198 ErrOut.Log('%s : Skipped because of %d errors.' % (
199 filenode.GetName(), errcnt))
201 if skipList:
202 return -len(skipList)
204 if GetOption('diff'):
205 return -cnt
206 return cnt
209 check_release = 0
210 check_range = 0
212 class GeneratorReleaseTest(Generator):
213 def GenerateRelease(self, ast, release, options = {}):
214 __pychecker__ = 'unusednames=ast,release,options'
215 global check_release
216 check_map = {
217 'so_long': True,
218 'MyOpt': 'XYZ',
219 'goodbye': True
221 check_release = 1
222 for item in check_map:
223 check_item = check_map[item]
224 option_item = options.get(item, None)
225 if check_item != option_item:
226 print 'Option %s is %s, expecting %s' % (item, option_item, check_item)
227 check_release = 0
229 if release != 'M14':
230 check_release = 0
231 return check_release == 1
233 def GenerateRange(self, ast, releases, options):
234 __pychecker__ = 'unusednames=ast,releases,options'
235 global check_range
236 check_range = 1
237 return True
239 def Test():
240 __pychecker__ = 'unusednames=args'
241 global check_release
242 global check_range
244 ParseOptions(['--testgen_opt=so_long,MyOpt=XYZ,goodbye'])
245 if Generator.Run('AST') != 0:
246 print 'Generate release: Failed.\n'
247 return -1
249 if check_release != 1 or check_range != 0:
250 print 'Gererate release: Failed to run.\n'
251 return -1
253 check_release = 0
254 ParseOptions(['--testgen_opt="HELLO"', '--range=M14,M16'])
255 if Generator.Run('AST') != 0:
256 print 'Generate range: Failed.\n'
257 return -1
259 if check_release != 0 or check_range != 1:
260 print 'Gererate range: Failed to run.\n'
261 return -1
263 print 'Generator test: Pass'
264 return 0
267 def Main(args):
268 if not args: return Test()
269 filenames = ParseOptions(args)
270 ast = ParseFiles(filenames)
272 return Generator.Run(ast)
275 if __name__ == '__main__':
276 GeneratorReleaseTest('Test Gen', 'testgen', 'Generator Class Test.')
277 sys.exit(Main(sys.argv[1:]))