# Copyright (c) 2005-2006 The Regents of The University of Michigan # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer; # redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution; # neither the name of the copyright holders nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Authors: Nathan Binkert import sys from attrdict import attrdict, optiondict from misc import crossproduct, flatten class Data(object): def __init__(self, name, desc, **kwargs): self.name = name self.desc = desc self.__dict__.update(kwargs) def update(self, obj): if not isinstance(obj, Data): raise AttributeError, "can only update from Data object" for k,v in obj.__dict__.iteritems(): if not k.startswith('_'): self.__dict__[k] = v if hasattr(self, 'system') and hasattr(obj, 'system'): if self.system != obj.system: raise AttributeError, \ "conflicting values for system: '%s'/'%s'" % \ (self.system, obj.system) def printinfo(self): if self.name: print 'name: %s' % self.name if self.desc: print 'desc: %s' % self.desc try: if self.system: print 'system: %s' % self.system except AttributeError: pass def printverbose(self): for key in self: val = self[key] if isinstance(val, dict): import pprint val = pprint.pformat(val) print '%-20s = %s' % (key, val) print def __contains__(self, attr): if attr.startswith('_'): return False return attr in self.__dict__ def __getitem__(self, key): if key.startswith('_'): raise KeyError, "Key '%s' not found" % attr return self.__dict__[key] def __iter__(self): keys = self.__dict__.keys() keys.sort() for key in keys: if not key.startswith('_'): yield key def optiondict(self): result = optiondict() for key in self: result[key] = self[key] return result def __str__(self): return self.name class Job(Data): def __init__(self, options): super(Job, self).__init__('', '') config = options[0]._config for opt in options: if opt._config != config: raise AttributeError, \ "All options are not from the same Configuration" self._config = config self._groups = [ opt._group for opt in options ] self._options = options self.update(self._config) for group in self._groups: self.update(group) self._is_checkpoint = True for option in self._options: self.update(option) if not option._group._checkpoint: self._is_checkpoint = False if option._suboption: self.update(option._suboption) self._is_checkpoint = False names = [ ] for opt in self._options: if opt.name: names.append(opt.name) self.name = ':'.join(names) descs = [ ] for opt in self._options: if opt.desc: descs.append(opt.desc) self.desc = ', '.join(descs) self._checkpoint = None if not self._is_checkpoint: opts = [] for opt in options: cpt = opt._group._checkpoint if not cpt: continue if isinstance(cpt, Option): opt = cpt.clone(suboptions=False) else: opt = opt.clone(suboptions=False) opts.append(opt) if opts: self._checkpoint = Job(opts) def clone(self): return Job(self._options) def printinfo(self): super(Job, self).printinfo() if self._checkpoint: print 'checkpoint: %s' % self._checkpoint.name print 'config: %s' % self._config.name print 'groups: %s' % [ g.name for g in self._groups ] print 'options: %s' % [ o.name for o in self._options ] super(Job, self).printverbose() class SubOption(Data): def __init__(self, name, desc, **kwargs): super(SubOption, self).__init__(name, desc, **kwargs) self._number = None class Option(Data): def __init__(self, name, desc, **kwargs): super(Option, self).__init__(name, desc, **kwargs) self._suboptions = [] self._suboption = None self._number = None def __getattribute__(self, attr): if attr == 'name': name = self.__dict__[attr] if self._suboption is not None: name = '%s:%s' % (name, self._suboption.name) return name if attr == 'desc': desc = [ self.__dict__[attr] ] if self._suboption is not None and self._suboption.desc: desc.append(self._suboption.desc) return ', '.join(desc) return super(Option, self).__getattribute__(attr) def suboption(self, name, desc, **kwargs): subo = SubOption(name, desc, **kwargs) subo._config = self._config subo._group = self._group subo._option = self subo._number = len(self._suboptions) self._suboptions.append(subo) return subo def clone(self, suboptions=True): option = Option(self.__dict__['name'], self.__dict__['desc']) option.update(self) option._group = self._group option._config = self._config option._number = self._number if suboptions: option._suboptions.extend(self._suboptions) option._suboption = self._suboption return option def subopts(self): if not self._suboptions: return [ self ] subopts = [] for subo in self._suboptions: option = self.clone() option._suboption = subo subopts.append(option) return subopts def printinfo(self): super(Option, self).printinfo() print 'config: %s' % self._config.name super(Option, self).printverbose() class Group(Data): def __init__(self, name, desc, **kwargs): super(Group, self).__init__(name, desc, **kwargs) self._options = [] self._number = None self._checkpoint = False def option(self, name, desc, **kwargs): opt = Option(name, desc, **kwargs) opt._config = self._config opt._group = self opt._number = len(self._options) self._options.append(opt) return opt def options(self): return self._options def subopts(self): subopts = [] for opt in self._options: for subo in opt.subopts(): subopts.append(subo) return subopts def printinfo(self): super(Group, self).printinfo() print 'config: %s' % self._config.name print 'options: %s' % [ o.name for o in self._options ] super(Group, self).printverbose() class Configuration(Data): def __init__(self, name, desc, **kwargs): super(Configuration, self).__init__(name, desc, **kwargs) self._groups = [] self._posfilters = [] self._negfilters = [] def group(self, name, desc, **kwargs): grp = Group(name, desc, **kwargs) grp._config = self grp._number = len(self._groups) self._groups.append(grp) return grp def groups(self): return self._groups def checkchildren(self, kids): for kid in kids: if kid._config != self: raise AttributeError, "child from the wrong configuration" def sortgroups(self, groups): groups = [ (grp._number, grp) for grp in groups ] groups.sort() return [ grp[1] for grp in groups ] def options(self, groups=None, checkpoint=False): if groups is None: groups = self._groups self.checkchildren(groups) groups = self.sortgroups(groups) if checkpoint: groups = [ grp for grp in groups if grp._checkpoint ] optgroups = [ g.options() for g in groups ] else: optgroups = [ g.subopts() for g in groups ] if not optgroups: return for options in crossproduct(optgroups): for opt in options: cpt = opt._group._checkpoint if not isinstance(cpt, bool) and cpt != opt: if checkpoint: break else: yield options else: if checkpoint: yield options def addfilter(self, filt, pos=True): import re filt = re.compile(filt) if pos: self._posfilters.append(filt) else: self._negfilters.append(filt) def jobfilter(self, job): for filt in self._negfilters: if filt.match(job.name): return False if not self._posfilters: return True for filt in self._posfilters: if filt.match(job.name): return True return False def checkpoints(self, groups=None): for options in self.options(groups, True): job = Job(options) if self.jobfilter(job): yield job def jobs(self, groups=None): for options in self.options(groups, False): job = Job(options) if self.jobfilter(job): yield job def alljobs(self, groups=None): for options in self.options(groups, True): yield Job(options) for options in self.options(groups, False): yield Job(options) def find(self, jobname): for job in self.alljobs(): if job.name == jobname: return job else: raise AttributeError, "job '%s' not found" % jobname def job(self, options): self.checkchildren(options) options = [ (opt._group._number, opt) for opt in options ] options.sort() options = [ opt[1] for opt in options ] job = Job(options) return job def printinfo(self): super(Configuration, self).printinfo() print 'groups: %s' % [ g.name for g in self._groups ] super(Configuration, self).printverbose() def JobFile(jobfile): from os.path import expanduser, isfile, join as joinpath filename = expanduser(jobfile) # Can't find filename in the current path, search sys.path if not isfile(filename): for path in sys.path: testname = joinpath(path, filename) if isfile(testname): filename = testname break else: raise AttributeError, \ "Could not find file '%s'" % jobfile data = {} execfile(filename, data) if 'conf' not in data: raise ImportError, 'cannot import name conf from %s' % jobfile conf = data['conf'] import jobfile if not isinstance(conf, Configuration): raise AttributeError, \ 'conf in jobfile: %s (%s) is not type %s' % \ (jobfile, type(conf), Configuration) return conf def main(conf=None): import sys usage = 'Usage: %s [-b] [-c] [-v] ' % sys.argv[0] try: import getopt opts, args = getopt.getopt(sys.argv[1:], '-bcv') except getopt.GetoptError: sys.exit(usage) both = False checkpoint = False verbose = False for opt,arg in opts: if opt == '-b': both = True checkpoint = True if opt == '-c': checkpoint = True if opt == '-v': verbose = True if conf is None: if len(args) != 1: raise AttributeError, usage conf = JobFile(args[0]) else: if len(args) != 0: raise AttributeError, usage if both: jobs = conf.alljobs() elif checkpoint: jobs = conf.checkpoints() else: jobs = conf.jobs() for job in jobs: if verbose: job.printinfo() else: cpt = '' if job._checkpoint: cpt = job._checkpoint.name print job.name, cpt if __name__ == '__main__': main()