jobfile.py revision 5618
12141SN/A# Copyright (c) 2005-2006 The Regents of The University of Michigan
21376SN/A# All rights reserved.
31376SN/A#
41376SN/A# Redistribution and use in source and binary forms, with or without
51376SN/A# modification, are permitted provided that the following conditions are
61376SN/A# met: redistributions of source code must retain the above copyright
71376SN/A# notice, this list of conditions and the following disclaimer;
81376SN/A# redistributions in binary form must reproduce the above copyright
91376SN/A# notice, this list of conditions and the following disclaimer in the
101376SN/A# documentation and/or other materials provided with the distribution;
111376SN/A# neither the name of the copyright holders nor the names of its
121376SN/A# contributors may be used to endorse or promote products derived from
131376SN/A# this software without specific prior written permission.
141376SN/A#
151376SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
161376SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
171376SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
181376SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
191376SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
201376SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
211376SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
221376SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
231376SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
241376SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
251376SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
261376SN/A#
271376SN/A# Authors: Nathan Binkert
281376SN/A
291428SN/Aimport sys
301428SN/A
315618Snate@binkert.orgfrom attrdict import optiondict
325618Snate@binkert.orgfrom misc import crossproduct
331881SN/A
341881SN/Aclass Data(object):
351881SN/A    def __init__(self, name, desc, **kwargs):
361881SN/A        self.name = name
371881SN/A        self.desc = desc
385467Snate@binkert.org        self.__dict__.update(kwargs)
391881SN/A
401881SN/A    def update(self, obj):
411881SN/A        if not isinstance(obj, Data):
421881SN/A            raise AttributeError, "can only update from Data object"
431881SN/A
445618Snate@binkert.org        for key,val in obj.__dict__.iteritems():
455618Snate@binkert.org            if key.startswith('_') or key in ('name', 'desc'):
465618Snate@binkert.org                continue
475618Snate@binkert.org
485618Snate@binkert.org            if key not in self.__dict__:
495618Snate@binkert.org                self.__dict__[key] = val
505618Snate@binkert.org                continue
515618Snate@binkert.org
525618Snate@binkert.org            if not isinstance(val, dict):
535618Snate@binkert.org                if self.__dict__[key] == val:
545618Snate@binkert.org                    continue
555618Snate@binkert.org
565618Snate@binkert.org                raise AttributeError, \
575618Snate@binkert.org                      "%s specified more than once old: %s new: %s" % \
585618Snate@binkert.org                      (key, self.__dict__[key], val)
595618Snate@binkert.org
605618Snate@binkert.org            d = self.__dict__[key]
615618Snate@binkert.org            for k,v in val.iteritems():
625618Snate@binkert.org                if k in d:
635618Snate@binkert.org                    raise AttributeError, \
645618Snate@binkert.org                          "%s specified more than once in %s" % (k, key)
655618Snate@binkert.org                d[k] = v
665618Snate@binkert.org
675467Snate@binkert.org        if hasattr(self, 'system') and hasattr(obj, 'system'):
685467Snate@binkert.org            if self.system != obj.system:
691881SN/A                raise AttributeError, \
701881SN/A                      "conflicting values for system: '%s'/'%s'" % \
711881SN/A                      (self.system, obj.system)
721881SN/A
731881SN/A    def printinfo(self):
741881SN/A        if self.name:
751881SN/A            print 'name: %s' % self.name
761881SN/A        if self.desc:
771881SN/A            print 'desc: %s' % self.desc
785467Snate@binkert.org        try:
795467Snate@binkert.org            if self.system:
805467Snate@binkert.org                print 'system: %s' % self.system
815467Snate@binkert.org        except AttributeError:
825467Snate@binkert.org            pass
831881SN/A
841881SN/A    def printverbose(self):
855467Snate@binkert.org        for key in self:
865467Snate@binkert.org            val = self[key]
875467Snate@binkert.org            if isinstance(val, dict):
885467Snate@binkert.org                import pprint
895467Snate@binkert.org                val = pprint.pformat(val)
905467Snate@binkert.org            print '%-20s = %s' % (key, val)
915467Snate@binkert.org        print
925467Snate@binkert.org
935467Snate@binkert.org    def __contains__(self, attr):
945467Snate@binkert.org        if attr.startswith('_'):
955467Snate@binkert.org            return False
965467Snate@binkert.org        return attr in self.__dict__
975467Snate@binkert.org
985467Snate@binkert.org    def __getitem__(self, key):
995467Snate@binkert.org        if key.startswith('_'):
1005467Snate@binkert.org            raise KeyError, "Key '%s' not found" % attr
1015467Snate@binkert.org        return self.__dict__[key]
1025467Snate@binkert.org
1035467Snate@binkert.org    def __iter__(self):
1045467Snate@binkert.org        keys = self.__dict__.keys()
1051881SN/A        keys.sort()
1061881SN/A        for key in keys:
1075467Snate@binkert.org            if not key.startswith('_'):
1085467Snate@binkert.org                yield key
1095467Snate@binkert.org
1105467Snate@binkert.org    def optiondict(self):
1115467Snate@binkert.org        result = optiondict()
1125467Snate@binkert.org        for key in self:
1135467Snate@binkert.org            result[key] = self[key]
1145467Snate@binkert.org        return result
1151881SN/A
1165618Snate@binkert.org    def __repr__(self):
1175618Snate@binkert.org        d = {}
1185618Snate@binkert.org        for key,value in self.__dict__.iteritems():
1195618Snate@binkert.org            if not key.startswith('_'):
1205618Snate@binkert.org                d[key] = value
1215618Snate@binkert.org
1225618Snate@binkert.org        return "<%s: %s>" % (type(self).__name__, d)
1235618Snate@binkert.org
1241881SN/A    def __str__(self):
1251881SN/A        return self.name
1261881SN/A
1271881SN/Aclass Job(Data):
1281881SN/A    def __init__(self, options):
1291881SN/A        super(Job, self).__init__('', '')
1301881SN/A
1315467Snate@binkert.org        config = options[0]._config
1321881SN/A        for opt in options:
1335467Snate@binkert.org            if opt._config != config:
1341881SN/A                raise AttributeError, \
1351881SN/A                      "All options are not from the same Configuration"
1361881SN/A
1375467Snate@binkert.org        self._config = config
1385467Snate@binkert.org        self._groups = [ opt._group for opt in options ]
1395467Snate@binkert.org        self._options = options
1401881SN/A
1415467Snate@binkert.org        self.update(self._config)
1425467Snate@binkert.org        for group in self._groups:
1431881SN/A            self.update(group)
1441881SN/A
1455467Snate@binkert.org        self._is_checkpoint = True
1465467Snate@binkert.org
1475467Snate@binkert.org        for option in self._options:
1481881SN/A            self.update(option)
1495467Snate@binkert.org            if not option._group._checkpoint:
1505467Snate@binkert.org                self._is_checkpoint = False
1515467Snate@binkert.org
1521881SN/A            if option._suboption:
1531881SN/A                self.update(option._suboption)
1545467Snate@binkert.org                self._is_checkpoint = False
1555467Snate@binkert.org
1565467Snate@binkert.org        names = [ ]
1575467Snate@binkert.org        for opt in self._options:
1585467Snate@binkert.org            if opt.name:
1595467Snate@binkert.org                names.append(opt.name)
1605467Snate@binkert.org        self.name = ':'.join(names)
1615467Snate@binkert.org
1625467Snate@binkert.org        descs = [ ]
1635467Snate@binkert.org        for opt in self._options:
1645467Snate@binkert.org            if opt.desc:
1655467Snate@binkert.org                descs.append(opt.desc)
1665467Snate@binkert.org        self.desc = ', '.join(descs)
1675467Snate@binkert.org
1685467Snate@binkert.org        self._checkpoint = None
1695467Snate@binkert.org        if not self._is_checkpoint:
1705467Snate@binkert.org            opts = []
1715467Snate@binkert.org            for opt in options:
1725467Snate@binkert.org                cpt = opt._group._checkpoint
1735467Snate@binkert.org                if not cpt:
1745467Snate@binkert.org                    continue
1755467Snate@binkert.org                if isinstance(cpt, Option):
1765467Snate@binkert.org                    opt = cpt.clone(suboptions=False)
1775467Snate@binkert.org                else:
1785467Snate@binkert.org                    opt = opt.clone(suboptions=False)
1795467Snate@binkert.org
1805467Snate@binkert.org                opts.append(opt)
1815467Snate@binkert.org
1825467Snate@binkert.org            if opts:
1835467Snate@binkert.org                self._checkpoint = Job(opts)
1845467Snate@binkert.org
1855467Snate@binkert.org    def clone(self):
1865467Snate@binkert.org        return Job(self._options)
1871881SN/A
1881881SN/A    def printinfo(self):
1891881SN/A        super(Job, self).printinfo()
1905467Snate@binkert.org        if self._checkpoint:
1915467Snate@binkert.org            print 'checkpoint: %s' % self._checkpoint.name
1925467Snate@binkert.org        print 'config: %s' % self._config.name
1935467Snate@binkert.org        print 'groups: %s' % [ g.name for g in self._groups ]
1945467Snate@binkert.org        print 'options: %s' % [ o.name for o in self._options ]
1951881SN/A        super(Job, self).printverbose()
1961881SN/A
1971881SN/Aclass SubOption(Data):
1981881SN/A    def __init__(self, name, desc, **kwargs):
1991881SN/A        super(SubOption, self).__init__(name, desc, **kwargs)
2005467Snate@binkert.org        self._number = None
2011881SN/A
2021881SN/Aclass Option(Data):
2031881SN/A    def __init__(self, name, desc, **kwargs):
2041881SN/A        super(Option, self).__init__(name, desc, **kwargs)
2051881SN/A        self._suboptions = []
2061881SN/A        self._suboption = None
2075467Snate@binkert.org        self._number = None
2081881SN/A
2091881SN/A    def __getattribute__(self, attr):
2101881SN/A        if attr == 'name':
2111881SN/A            name = self.__dict__[attr]
2121881SN/A            if self._suboption is not None:
2131881SN/A                name = '%s:%s' % (name, self._suboption.name)
2141881SN/A            return name
2151881SN/A
2161881SN/A        if attr == 'desc':
2172142SN/A            desc = [ self.__dict__[attr] ]
2182142SN/A            if self._suboption is not None and self._suboption.desc:
2192142SN/A                desc.append(self._suboption.desc)
2202142SN/A            return ', '.join(desc)
2212142SN/A
2221881SN/A        return super(Option, self).__getattribute__(attr)
2231881SN/A
2241881SN/A    def suboption(self, name, desc, **kwargs):
2251881SN/A        subo = SubOption(name, desc, **kwargs)
2265467Snate@binkert.org        subo._config = self._config
2275467Snate@binkert.org        subo._group = self._group
2285467Snate@binkert.org        subo._option = self
2295467Snate@binkert.org        subo._number = len(self._suboptions)
2301881SN/A        self._suboptions.append(subo)
2311881SN/A        return subo
2321881SN/A
2331881SN/A    def clone(self, suboptions=True):
2341881SN/A        option = Option(self.__dict__['name'], self.__dict__['desc'])
2351881SN/A        option.update(self)
2365467Snate@binkert.org        option._group = self._group
2375467Snate@binkert.org        option._config = self._config
2385467Snate@binkert.org        option._number = self._number
2391881SN/A        if suboptions:
2401881SN/A            option._suboptions.extend(self._suboptions)
2411881SN/A            option._suboption = self._suboption
2421881SN/A        return option
2431881SN/A
2441881SN/A    def subopts(self):
2451881SN/A        if not self._suboptions:
2461881SN/A            return [ self ]
2471881SN/A
2481881SN/A        subopts = []
2491881SN/A        for subo in self._suboptions:
2501881SN/A            option = self.clone()
2511881SN/A            option._suboption = subo
2521881SN/A            subopts.append(option)
2531881SN/A
2541881SN/A        return subopts
2551881SN/A
2561881SN/A    def printinfo(self):
2571881SN/A        super(Option, self).printinfo()
2585467Snate@binkert.org        print 'config: %s' % self._config.name
2591881SN/A        super(Option, self).printverbose()
2601881SN/A
2611881SN/Aclass Group(Data):
2621881SN/A    def __init__(self, name, desc, **kwargs):
2631881SN/A        super(Group, self).__init__(name, desc, **kwargs)
2641881SN/A        self._options = []
2655467Snate@binkert.org        self._number = None
2665467Snate@binkert.org        self._checkpoint = False
2671881SN/A
2681881SN/A    def option(self, name, desc, **kwargs):
2691881SN/A        opt = Option(name, desc, **kwargs)
2705467Snate@binkert.org        opt._config = self._config
2715467Snate@binkert.org        opt._group = self
2725467Snate@binkert.org        opt._number = len(self._options)
2731881SN/A        self._options.append(opt)
2741881SN/A        return opt
2751881SN/A
2761881SN/A    def options(self):
2771881SN/A        return self._options
2781881SN/A
2791881SN/A    def subopts(self):
2801881SN/A        subopts = []
2811881SN/A        for opt in self._options:
2821881SN/A            for subo in opt.subopts():
2831881SN/A                subopts.append(subo)
2841881SN/A        return subopts
2851881SN/A
2861881SN/A    def printinfo(self):
2871881SN/A        super(Group, self).printinfo()
2885467Snate@binkert.org        print 'config: %s' % self._config.name
2891881SN/A        print 'options: %s' % [ o.name for o in self._options ]
2901881SN/A        super(Group, self).printverbose()
2911881SN/A
2921881SN/Aclass Configuration(Data):
2931881SN/A    def __init__(self, name, desc, **kwargs):
2941881SN/A        super(Configuration, self).__init__(name, desc, **kwargs)
2951881SN/A        self._groups = []
2962141SN/A        self._posfilters = []
2972141SN/A        self._negfilters = []
2981881SN/A
2991881SN/A    def group(self, name, desc, **kwargs):
3001881SN/A        grp = Group(name, desc, **kwargs)
3015467Snate@binkert.org        grp._config = self
3025467Snate@binkert.org        grp._number = len(self._groups)
3031881SN/A        self._groups.append(grp)
3041881SN/A        return grp
3051881SN/A
3065467Snate@binkert.org    def groups(self):
3075467Snate@binkert.org        return self._groups
3081881SN/A
3091881SN/A    def checkchildren(self, kids):
3101881SN/A        for kid in kids:
3115467Snate@binkert.org            if kid._config != self:
3121881SN/A                raise AttributeError, "child from the wrong configuration"
3131881SN/A
3141881SN/A    def sortgroups(self, groups):
3155467Snate@binkert.org        groups = [ (grp._number, grp) for grp in groups ]
3161881SN/A        groups.sort()
3171881SN/A        return [ grp[1] for grp in groups ]
3181881SN/A
3195467Snate@binkert.org    def options(self, groups=None, checkpoint=False):
3201881SN/A        if groups is None:
3211881SN/A            groups = self._groups
3221881SN/A        self.checkchildren(groups)
3231881SN/A        groups = self.sortgroups(groups)
3241881SN/A        if checkpoint:
3255467Snate@binkert.org            groups = [ grp for grp in groups if grp._checkpoint ]
3261881SN/A            optgroups = [ g.options() for g in groups ]
3271881SN/A        else:
3281881SN/A            optgroups = [ g.subopts() for g in groups ]
3295467Snate@binkert.org        if not optgroups:
3305467Snate@binkert.org            return
3311881SN/A        for options in crossproduct(optgroups):
3321881SN/A            for opt in options:
3335467Snate@binkert.org                cpt = opt._group._checkpoint
3341881SN/A                if not isinstance(cpt, bool) and cpt != opt:
3351881SN/A                    if checkpoint:
3361881SN/A                        break
3371881SN/A                    else:
3381881SN/A                        yield options
3391881SN/A            else:
3401881SN/A                if checkpoint:
3411881SN/A                    yield options
3421881SN/A
3432141SN/A    def addfilter(self, filt, pos=True):
3442141SN/A        import re
3452141SN/A        filt = re.compile(filt)
3462141SN/A        if pos:
3472141SN/A            self._posfilters.append(filt)
3482141SN/A        else:
3492141SN/A            self._negfilters.append(filt)
3502141SN/A
3512141SN/A    def jobfilter(self, job):
3522141SN/A        for filt in self._negfilters:
3532141SN/A            if filt.match(job.name):
3542141SN/A                return False
3552141SN/A
3562141SN/A        if not self._posfilters:
3572141SN/A            return True
3582141SN/A
3592141SN/A        for filt in self._posfilters:
3602141SN/A            if filt.match(job.name):
3612141SN/A                return True
3622141SN/A
3632141SN/A        return False
3642141SN/A
3655467Snate@binkert.org    def checkpoints(self, groups=None):
3661881SN/A        for options in self.options(groups, True):
3672141SN/A            job = Job(options)
3682141SN/A            if self.jobfilter(job):
3692141SN/A                yield job
3701881SN/A
3715467Snate@binkert.org    def jobs(self, groups=None):
3721881SN/A        for options in self.options(groups, False):
3732141SN/A            job = Job(options)
3742141SN/A            if self.jobfilter(job):
3752141SN/A                yield job
3761881SN/A
3775467Snate@binkert.org    def alljobs(self, groups=None):
3781881SN/A        for options in self.options(groups, True):
3791881SN/A            yield Job(options)
3801881SN/A        for options in self.options(groups, False):
3811881SN/A            yield Job(options)
3821881SN/A
3831881SN/A    def find(self, jobname):
3841881SN/A        for job in self.alljobs():
3851881SN/A            if job.name == jobname:
3861881SN/A                return job
3871881SN/A        else:
3881881SN/A            raise AttributeError, "job '%s' not found" % jobname
3891881SN/A
3901881SN/A    def job(self, options):
3911881SN/A        self.checkchildren(options)
3925467Snate@binkert.org        options = [ (opt._group._number, opt) for opt in options ]
3931881SN/A        options.sort()
3941881SN/A        options = [ opt[1] for opt in options ]
3951881SN/A        job = Job(options)
3961881SN/A        return job
3971881SN/A
3981881SN/A    def printinfo(self):
3991881SN/A        super(Configuration, self).printinfo()
4005467Snate@binkert.org        print 'groups: %s' % [ g.name for g in self._groups ]
4011881SN/A        super(Configuration, self).printverbose()
4021881SN/A
4031881SN/Adef JobFile(jobfile):
4041881SN/A    from os.path import expanduser, isfile, join as joinpath
4051881SN/A    filename = expanduser(jobfile)
4061881SN/A
4071881SN/A    # Can't find filename in the current path, search sys.path
4081881SN/A    if not isfile(filename):
4091881SN/A        for path in sys.path:
4101881SN/A            testname = joinpath(path, filename)
4111881SN/A            if isfile(testname):
4121881SN/A                filename = testname
4131881SN/A                break
4141881SN/A        else:
4151881SN/A            raise AttributeError, \
4161881SN/A                  "Could not find file '%s'" % jobfile
4171881SN/A
4181881SN/A    data = {}
4191881SN/A    execfile(filename, data)
4201881SN/A    if 'conf' not in data:
4211881SN/A        raise ImportError, 'cannot import name conf from %s' % jobfile
4225618Snate@binkert.org    return data['conf']
4231881SN/A
4245467Snate@binkert.orgdef main(conf=None):
4255618Snate@binkert.org    usage = 'Usage: %s [-b] [-c] [-v]' % sys.argv[0]
4265618Snate@binkert.org    if conf is None:
4275618Snate@binkert.org        usage += ' <jobfile>'
4281881SN/A
4291881SN/A    try:
4301881SN/A        import getopt
4311881SN/A        opts, args = getopt.getopt(sys.argv[1:], '-bcv')
4321881SN/A    except getopt.GetoptError:
4331881SN/A        sys.exit(usage)
4341881SN/A
4351881SN/A    both = False
4361881SN/A    checkpoint = False
4371881SN/A    verbose = False
4381881SN/A    for opt,arg in opts:
4391881SN/A        if opt == '-b':
4401881SN/A            both = True
4411881SN/A            checkpoint = True
4421881SN/A        if opt == '-c':
4431881SN/A            checkpoint = True
4441881SN/A        if opt == '-v':
4451881SN/A            verbose = True
4461881SN/A
4475467Snate@binkert.org    if conf is None:
4485467Snate@binkert.org        if len(args) != 1:
4495467Snate@binkert.org            raise AttributeError, usage
4505467Snate@binkert.org        conf = JobFile(args[0])
4515467Snate@binkert.org    else:
4525467Snate@binkert.org        if len(args) != 0:
4535467Snate@binkert.org            raise AttributeError, usage
4541881SN/A
4551881SN/A    if both:
4565467Snate@binkert.org        jobs = conf.alljobs()
4571881SN/A    elif checkpoint:
4585467Snate@binkert.org        jobs = conf.checkpoints()
4591881SN/A    else:
4605467Snate@binkert.org        jobs = conf.jobs()
4611881SN/A
4625467Snate@binkert.org    for job in jobs:
4635467Snate@binkert.org        if verbose:
4645467Snate@binkert.org            job.printinfo()
4655467Snate@binkert.org        else:
4661881SN/A            cpt = ''
4675467Snate@binkert.org            if job._checkpoint:
4685467Snate@binkert.org                cpt = job._checkpoint.name
4691881SN/A            print job.name, cpt
4705467Snate@binkert.org
4715467Snate@binkert.orgif __name__ == '__main__':
4725467Snate@binkert.org    main()
473