1# Copyright (c) 2005-2006 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Nathan Binkert
28
29import sys
30
31from attrdict import attrdict, optiondict
32from misc import crossproduct, flatten
31from attrdict import optiondict
32from misc import crossproduct
33
34class Data(object):
35 def __init__(self, name, desc, **kwargs):
36 self.name = name
37 self.desc = desc
38 self.__dict__.update(kwargs)
39
40 def update(self, obj):
41 if not isinstance(obj, Data):
42 raise AttributeError, "can only update from Data object"
43
44 for k,v in obj.__dict__.iteritems():
45 if not k.startswith('_'):
46 self.__dict__[k] = v
44 for key,val in obj.__dict__.iteritems():
45 if key.startswith('_') or key in ('name', 'desc'):
46 continue
47
48 if key not in self.__dict__:
49 self.__dict__[key] = val
50 continue
51
52 if not isinstance(val, dict):
53 if self.__dict__[key] == val:
54 continue
55
56 raise AttributeError, \
57 "%s specified more than once old: %s new: %s" % \
58 (key, self.__dict__[key], val)
59
60 d = self.__dict__[key]
61 for k,v in val.iteritems():
62 if k in d:
63 raise AttributeError, \
64 "%s specified more than once in %s" % (k, key)
65 d[k] = v
66
67 if hasattr(self, 'system') and hasattr(obj, 'system'):
68 if self.system != obj.system:
69 raise AttributeError, \
70 "conflicting values for system: '%s'/'%s'" % \
71 (self.system, obj.system)
72
73 def printinfo(self):
74 if self.name:
75 print 'name: %s' % self.name
76 if self.desc:
77 print 'desc: %s' % self.desc
78 try:
79 if self.system:
80 print 'system: %s' % self.system
81 except AttributeError:
82 pass
83
84 def printverbose(self):
85 for key in self:
86 val = self[key]
87 if isinstance(val, dict):
88 import pprint
89 val = pprint.pformat(val)
90 print '%-20s = %s' % (key, val)
91 print
92
93 def __contains__(self, attr):
94 if attr.startswith('_'):
95 return False
96 return attr in self.__dict__
97
98 def __getitem__(self, key):
99 if key.startswith('_'):
100 raise KeyError, "Key '%s' not found" % attr
101 return self.__dict__[key]
102
103 def __iter__(self):
104 keys = self.__dict__.keys()
105 keys.sort()
106 for key in keys:
107 if not key.startswith('_'):
108 yield key
109
110 def optiondict(self):
111 result = optiondict()
112 for key in self:
113 result[key] = self[key]
114 return result
115
116 def __repr__(self):
117 d = {}
118 for key,value in self.__dict__.iteritems():
119 if not key.startswith('_'):
120 d[key] = value
121
122 return "<%s: %s>" % (type(self).__name__, d)
123
124 def __str__(self):
125 return self.name
126
127class Job(Data):
128 def __init__(self, options):
129 super(Job, self).__init__('', '')
130
131 config = options[0]._config
132 for opt in options:
133 if opt._config != config:
134 raise AttributeError, \
135 "All options are not from the same Configuration"
136
137 self._config = config
138 self._groups = [ opt._group for opt in options ]
139 self._options = options
140
141 self.update(self._config)
142 for group in self._groups:
143 self.update(group)
144
145 self._is_checkpoint = True
146
147 for option in self._options:
148 self.update(option)
149 if not option._group._checkpoint:
150 self._is_checkpoint = False
151
152 if option._suboption:
153 self.update(option._suboption)
154 self._is_checkpoint = False
155
156 names = [ ]
157 for opt in self._options:
158 if opt.name:
159 names.append(opt.name)
160 self.name = ':'.join(names)
161
162 descs = [ ]
163 for opt in self._options:
164 if opt.desc:
165 descs.append(opt.desc)
166 self.desc = ', '.join(descs)
167
168 self._checkpoint = None
169 if not self._is_checkpoint:
170 opts = []
171 for opt in options:
172 cpt = opt._group._checkpoint
173 if not cpt:
174 continue
175 if isinstance(cpt, Option):
176 opt = cpt.clone(suboptions=False)
177 else:
178 opt = opt.clone(suboptions=False)
179
180 opts.append(opt)
181
182 if opts:
183 self._checkpoint = Job(opts)
184
185 def clone(self):
186 return Job(self._options)
187
188 def printinfo(self):
189 super(Job, self).printinfo()
190 if self._checkpoint:
191 print 'checkpoint: %s' % self._checkpoint.name
192 print 'config: %s' % self._config.name
193 print 'groups: %s' % [ g.name for g in self._groups ]
194 print 'options: %s' % [ o.name for o in self._options ]
195 super(Job, self).printverbose()
196
197class SubOption(Data):
198 def __init__(self, name, desc, **kwargs):
199 super(SubOption, self).__init__(name, desc, **kwargs)
200 self._number = None
201
202class Option(Data):
203 def __init__(self, name, desc, **kwargs):
204 super(Option, self).__init__(name, desc, **kwargs)
205 self._suboptions = []
206 self._suboption = None
207 self._number = None
208
209 def __getattribute__(self, attr):
210 if attr == 'name':
211 name = self.__dict__[attr]
212 if self._suboption is not None:
213 name = '%s:%s' % (name, self._suboption.name)
214 return name
215
216 if attr == 'desc':
217 desc = [ self.__dict__[attr] ]
218 if self._suboption is not None and self._suboption.desc:
219 desc.append(self._suboption.desc)
220 return ', '.join(desc)
221
222 return super(Option, self).__getattribute__(attr)
223
224 def suboption(self, name, desc, **kwargs):
225 subo = SubOption(name, desc, **kwargs)
226 subo._config = self._config
227 subo._group = self._group
228 subo._option = self
229 subo._number = len(self._suboptions)
230 self._suboptions.append(subo)
231 return subo
232
233 def clone(self, suboptions=True):
234 option = Option(self.__dict__['name'], self.__dict__['desc'])
235 option.update(self)
236 option._group = self._group
237 option._config = self._config
238 option._number = self._number
239 if suboptions:
240 option._suboptions.extend(self._suboptions)
241 option._suboption = self._suboption
242 return option
243
244 def subopts(self):
245 if not self._suboptions:
246 return [ self ]
247
248 subopts = []
249 for subo in self._suboptions:
250 option = self.clone()
251 option._suboption = subo
252 subopts.append(option)
253
254 return subopts
255
256 def printinfo(self):
257 super(Option, self).printinfo()
258 print 'config: %s' % self._config.name
259 super(Option, self).printverbose()
260
261class Group(Data):
262 def __init__(self, name, desc, **kwargs):
263 super(Group, self).__init__(name, desc, **kwargs)
264 self._options = []
265 self._number = None
266 self._checkpoint = False
267
268 def option(self, name, desc, **kwargs):
269 opt = Option(name, desc, **kwargs)
270 opt._config = self._config
271 opt._group = self
272 opt._number = len(self._options)
273 self._options.append(opt)
274 return opt
275
276 def options(self):
277 return self._options
278
279 def subopts(self):
280 subopts = []
281 for opt in self._options:
282 for subo in opt.subopts():
283 subopts.append(subo)
284 return subopts
285
286 def printinfo(self):
287 super(Group, self).printinfo()
288 print 'config: %s' % self._config.name
289 print 'options: %s' % [ o.name for o in self._options ]
290 super(Group, self).printverbose()
291
292class Configuration(Data):
293 def __init__(self, name, desc, **kwargs):
294 super(Configuration, self).__init__(name, desc, **kwargs)
295 self._groups = []
296 self._posfilters = []
297 self._negfilters = []
298
299 def group(self, name, desc, **kwargs):
300 grp = Group(name, desc, **kwargs)
301 grp._config = self
302 grp._number = len(self._groups)
303 self._groups.append(grp)
304 return grp
305
306 def groups(self):
307 return self._groups
308
309 def checkchildren(self, kids):
310 for kid in kids:
311 if kid._config != self:
312 raise AttributeError, "child from the wrong configuration"
313
314 def sortgroups(self, groups):
315 groups = [ (grp._number, grp) for grp in groups ]
316 groups.sort()
317 return [ grp[1] for grp in groups ]
318
319 def options(self, groups=None, checkpoint=False):
320 if groups is None:
321 groups = self._groups
322 self.checkchildren(groups)
323 groups = self.sortgroups(groups)
324 if checkpoint:
325 groups = [ grp for grp in groups if grp._checkpoint ]
326 optgroups = [ g.options() for g in groups ]
327 else:
328 optgroups = [ g.subopts() for g in groups ]
329 if not optgroups:
330 return
331 for options in crossproduct(optgroups):
332 for opt in options:
333 cpt = opt._group._checkpoint
334 if not isinstance(cpt, bool) and cpt != opt:
335 if checkpoint:
336 break
337 else:
338 yield options
339 else:
340 if checkpoint:
341 yield options
342
343 def addfilter(self, filt, pos=True):
344 import re
345 filt = re.compile(filt)
346 if pos:
347 self._posfilters.append(filt)
348 else:
349 self._negfilters.append(filt)
350
351 def jobfilter(self, job):
352 for filt in self._negfilters:
353 if filt.match(job.name):
354 return False
355
356 if not self._posfilters:
357 return True
358
359 for filt in self._posfilters:
360 if filt.match(job.name):
361 return True
362
363 return False
364
365 def checkpoints(self, groups=None):
366 for options in self.options(groups, True):
367 job = Job(options)
368 if self.jobfilter(job):
369 yield job
370
371 def jobs(self, groups=None):
372 for options in self.options(groups, False):
373 job = Job(options)
374 if self.jobfilter(job):
375 yield job
376
377 def alljobs(self, groups=None):
378 for options in self.options(groups, True):
379 yield Job(options)
380 for options in self.options(groups, False):
381 yield Job(options)
382
383 def find(self, jobname):
384 for job in self.alljobs():
385 if job.name == jobname:
386 return job
387 else:
388 raise AttributeError, "job '%s' not found" % jobname
389
390 def job(self, options):
391 self.checkchildren(options)
392 options = [ (opt._group._number, opt) for opt in options ]
393 options.sort()
394 options = [ opt[1] for opt in options ]
395 job = Job(options)
396 return job
397
398 def printinfo(self):
399 super(Configuration, self).printinfo()
400 print 'groups: %s' % [ g.name for g in self._groups ]
401 super(Configuration, self).printverbose()
402
403def JobFile(jobfile):
404 from os.path import expanduser, isfile, join as joinpath
405 filename = expanduser(jobfile)
406
407 # Can't find filename in the current path, search sys.path
408 if not isfile(filename):
409 for path in sys.path:
410 testname = joinpath(path, filename)
411 if isfile(testname):
412 filename = testname
413 break
414 else:
415 raise AttributeError, \
416 "Could not find file '%s'" % jobfile
417
418 data = {}
419 execfile(filename, data)
420 if 'conf' not in data:
421 raise ImportError, 'cannot import name conf from %s' % jobfile
394 conf = data['conf']
395 import jobfile
396 if not isinstance(conf, Configuration):
397 raise AttributeError, \
398 'conf in jobfile: %s (%s) is not type %s' % \
399 (jobfile, type(conf), Configuration)
400 return conf
422 return data['conf']
423
424def main(conf=None):
403 import sys
425 usage = 'Usage: %s [-b] [-c] [-v]' % sys.argv[0]
426 if conf is None:
427 usage += ' <jobfile>'
428
405 usage = 'Usage: %s [-b] [-c] [-v] <jobfile>' % sys.argv[0]
406
429 try:
430 import getopt
431 opts, args = getopt.getopt(sys.argv[1:], '-bcv')
432 except getopt.GetoptError:
433 sys.exit(usage)
434
435 both = False
436 checkpoint = False
437 verbose = False
438 for opt,arg in opts:
439 if opt == '-b':
440 both = True
441 checkpoint = True
442 if opt == '-c':
443 checkpoint = True
444 if opt == '-v':
445 verbose = True
446
447 if conf is None:
448 if len(args) != 1:
449 raise AttributeError, usage
450 conf = JobFile(args[0])
451 else:
452 if len(args) != 0:
453 raise AttributeError, usage
454
455 if both:
456 jobs = conf.alljobs()
457 elif checkpoint:
458 jobs = conf.checkpoints()
459 else:
460 jobs = conf.jobs()
461
462 for job in jobs:
463 if verbose:
464 job.printinfo()
465 else:
466 cpt = ''
467 if job._checkpoint:
468 cpt = job._checkpoint.name
469 print job.name, cpt
470
471if __name__ == '__main__':
472 main()