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.
8 from idl_log
import ErrOut
, InfoOut
, WarnOut
9 from idl_option
import GetOption
, Option
, ParseOptions
10 from idl_parser
import ParseFiles
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
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
34 def __init__(self
, name
, sname
, desc
):
36 self
.run_switch
= Option(sname
, desc
)
37 self
.opt_switch
= Option(sname
+ '_opt', 'Options for %s.' % sname
,
39 GeneratorList
.append(self
)
44 ErrOut
.Log('Error %s : %s' % (self
.name
, msg
))
47 def GetRunOptions(self
):
49 option_list
= self
.opt_switch
.Get()
51 option_list
= option_list
.split(',')
52 for opt
in option_list
:
55 options
[opt
[:offs
]] = opt
[offs
+1:]
59 if self
.run_switch
.Get():
63 def Generate(self
, ast
, options
):
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
)
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
))
88 # Generate 'start' and 'end' represent first and last found.
90 vmin
= ast
.releases
[0]
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
)
100 self
.Error('Failed to generate range %s : %s.' %(vmin
, vmax
))
102 InfoOut
.Log('%s wrote %d files.' % (self
.name
, ret
))
103 # Otherwise this should be a single release generation
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.' %
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
)))
120 InfoOut
.Log('Generate release %s of %s.' % (releasestr
, self
.name
))
121 ret
= self
.GenerateRelease(ast
, releasestr
, options
)
123 self
.Error('Failed to generate release %s.' % releasestr
)
125 InfoOut
.Log('%s wrote %d files.' % (self
.name
, ret
))
128 self
.Error('No range or release specified for %s.' % releasestr
)
131 def GenerateRelease(self
, ast
, release
, options
):
132 __pychecker__
= 'unusednames=ast,release,options'
133 self
.Error("Undefined release generator.")
136 def GenerateRange(self
, ast
, releases
, options
):
137 __pychecker__
= 'unusednames=ast,releases,options'
138 self
.Error("Undefined range generator.")
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
):
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
169 __pychecker__
= 'unusednames=filenode,releases,options'
170 self
.Error("Undefined release generator.")
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(',')
183 for filenode
in ast
.GetListOf('File'):
184 # Ignore files with errors
185 if filenode
in self
.skip_list
:
188 # Skip this file if not required
189 if outlist
and filenode
.GetName() not in outlist
:
192 # Create the output file and increment out count if there was a delta
193 if self
.GenerateFile(filenode
, releases
, options
):
196 for filenode
in skipList
:
197 errcnt
= filenode
.GetProperty('ERRORS')
198 ErrOut
.Log('%s : Skipped because of %d errors.' % (
199 filenode
.GetName(), errcnt
))
202 return -len(skipList
)
204 if GetOption('diff'):
212 class GeneratorReleaseTest(Generator
):
213 def GenerateRelease(self
, ast
, release
, options
= {}):
214 __pychecker__
= 'unusednames=ast,release,options'
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
)
231 return check_release
== 1
233 def GenerateRange(self
, ast
, releases
, options
):
234 __pychecker__
= 'unusednames=ast,releases,options'
240 __pychecker__
= 'unusednames=args'
244 ParseOptions(['--testgen_opt=so_long,MyOpt=XYZ,goodbye'])
245 if Generator
.Run('AST') != 0:
246 print 'Generate release: Failed.\n'
249 if check_release
!= 1 or check_range
!= 0:
250 print 'Gererate release: Failed to run.\n'
254 ParseOptions(['--testgen_opt="HELLO"', '--range=M14,M16'])
255 if Generator
.Run('AST') != 0:
256 print 'Generate range: Failed.\n'
259 if check_release
!= 0 or check_range
!= 1:
260 print 'Gererate range: Failed to run.\n'
263 print 'Generator test: Pass'
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:]))