jobfile.py revision 5467
110688Sandreas.hansson@arm.com# Copyright (c) 2005-2006 The Regents of The University of Michigan
210688Sandreas.hansson@arm.com# All rights reserved.
310688Sandreas.hansson@arm.com#
410688Sandreas.hansson@arm.com# Redistribution and use in source and binary forms, with or without
510688Sandreas.hansson@arm.com# modification, are permitted provided that the following conditions are
610688Sandreas.hansson@arm.com# met: redistributions of source code must retain the above copyright
710688Sandreas.hansson@arm.com# notice, this list of conditions and the following disclaimer;
810688Sandreas.hansson@arm.com# redistributions in binary form must reproduce the above copyright
910688Sandreas.hansson@arm.com# notice, this list of conditions and the following disclaimer in the
1010688Sandreas.hansson@arm.com# documentation and/or other materials provided with the distribution;
1110688Sandreas.hansson@arm.com# neither the name of the copyright holders nor the names of its
1210688Sandreas.hansson@arm.com# contributors may be used to endorse or promote products derived from
134486SN/A# this software without specific prior written permission.
144486SN/A#
154486SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
164486SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
174486SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
184486SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
194486SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
204486SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
214486SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
224486SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
234486SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
244486SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
254486SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
264486SN/A#
274486SN/A# Authors: Nathan Binkert
284486SN/A
294486SN/Aimport sys
304486SN/A
314486SN/Afrom attrdict import attrdict, optiondict
324486SN/Afrom misc import crossproduct, flatten
334486SN/A
344486SN/Aclass Data(object):
354486SN/A    def __init__(self, name, desc, **kwargs):
364486SN/A        self.name = name
374486SN/A        self.desc = desc
384486SN/A        self.__dict__.update(kwargs)
394486SN/A
4010688Sandreas.hansson@arm.com    def update(self, obj):
413102SN/A        if not isinstance(obj, Data):
423187SN/A            raise AttributeError, "can only update from Data object"
433187SN/A
4413892Sgabeblack@google.com        for k,v in obj.__dict__.iteritems():
4513665Sandreas.sandberg@arm.com            if not k.startswith('_'):
4613892Sgabeblack@google.com                self.__dict__[k] = v
471366SN/A        if hasattr(self, 'system') and hasattr(obj, 'system'):
489338SAndreas.Sandberg@arm.com            if self.system != obj.system:
4910688Sandreas.hansson@arm.com                raise AttributeError, \
5010688Sandreas.hansson@arm.com                      "conflicting values for system: '%s'/'%s'" % \
5110688Sandreas.hansson@arm.com                      (self.system, obj.system)
5210688Sandreas.hansson@arm.com
5310688Sandreas.hansson@arm.com    def printinfo(self):
5410688Sandreas.hansson@arm.com        if self.name:
5510688Sandreas.hansson@arm.com            print 'name: %s' % self.name
5610688Sandreas.hansson@arm.com        if self.desc:
5710688Sandreas.hansson@arm.com            print 'desc: %s' % self.desc
5810688Sandreas.hansson@arm.com        try:
5910688Sandreas.hansson@arm.com            if self.system:
6010688Sandreas.hansson@arm.com                print 'system: %s' % self.system
6110688Sandreas.hansson@arm.com        except AttributeError:
6210688Sandreas.hansson@arm.com            pass
6310688Sandreas.hansson@arm.com
641310SN/A    def printverbose(self):
6510688Sandreas.hansson@arm.com        for key in self:
6610688Sandreas.hansson@arm.com            val = self[key]
6710688Sandreas.hansson@arm.com            if isinstance(val, dict):
683187SN/A                import pprint
6910688Sandreas.hansson@arm.com                val = pprint.pformat(val)
7010688Sandreas.hansson@arm.com            print '%-20s = %s' % (key, val)
718832SAli.Saidi@ARM.com        print
7210688Sandreas.hansson@arm.com
7310688Sandreas.hansson@arm.com    def __contains__(self, attr):
7410688Sandreas.hansson@arm.com        if attr.startswith('_'):
7510688Sandreas.hansson@arm.com            return False
76        return attr in self.__dict__
77
78    def __getitem__(self, key):
79        if key.startswith('_'):
80            raise KeyError, "Key '%s' not found" % attr
81        return self.__dict__[key]
82
83    def __iter__(self):
84        keys = self.__dict__.keys()
85        keys.sort()
86        for key in keys:
87            if not key.startswith('_'):
88                yield key
89
90    def optiondict(self):
91        result = optiondict()
92        for key in self:
93            result[key] = self[key]
94        return result
95
96    def __str__(self):
97        return self.name
98
99class Job(Data):
100    def __init__(self, options):
101        super(Job, self).__init__('', '')
102
103        config = options[0]._config
104        for opt in options:
105            if opt._config != config:
106                raise AttributeError, \
107                      "All options are not from the same Configuration"
108
109        self._config = config
110        self._groups = [ opt._group for opt in options ]
111        self._options = options
112
113        self.update(self._config)
114        for group in self._groups:
115            self.update(group)
116
117        self._is_checkpoint = True
118
119        for option in self._options:
120            self.update(option)
121            if not option._group._checkpoint:
122                self._is_checkpoint = False
123
124            if option._suboption:
125                self.update(option._suboption)
126                self._is_checkpoint = False
127
128        names = [ ]
129        for opt in self._options:
130            if opt.name:
131                names.append(opt.name)
132        self.name = ':'.join(names)
133
134        descs = [ ]
135        for opt in self._options:
136            if opt.desc:
137                descs.append(opt.desc)
138        self.desc = ', '.join(descs)
139
140        self._checkpoint = None
141        if not self._is_checkpoint:
142            opts = []
143            for opt in options:
144                cpt = opt._group._checkpoint
145                if not cpt:
146                    continue
147                if isinstance(cpt, Option):
148                    opt = cpt.clone(suboptions=False)
149                else:
150                    opt = opt.clone(suboptions=False)
151
152                opts.append(opt)
153
154            if opts:
155                self._checkpoint = Job(opts)
156
157    def clone(self):
158        return Job(self._options)
159
160    def printinfo(self):
161        super(Job, self).printinfo()
162        if self._checkpoint:
163            print 'checkpoint: %s' % self._checkpoint.name
164        print 'config: %s' % self._config.name
165        print 'groups: %s' % [ g.name for g in self._groups ]
166        print 'options: %s' % [ o.name for o in self._options ]
167        super(Job, self).printverbose()
168
169class SubOption(Data):
170    def __init__(self, name, desc, **kwargs):
171        super(SubOption, self).__init__(name, desc, **kwargs)
172        self._number = None
173
174class Option(Data):
175    def __init__(self, name, desc, **kwargs):
176        super(Option, self).__init__(name, desc, **kwargs)
177        self._suboptions = []
178        self._suboption = None
179        self._number = None
180
181    def __getattribute__(self, attr):
182        if attr == 'name':
183            name = self.__dict__[attr]
184            if self._suboption is not None:
185                name = '%s:%s' % (name, self._suboption.name)
186            return name
187
188        if attr == 'desc':
189            desc = [ self.__dict__[attr] ]
190            if self._suboption is not None and self._suboption.desc:
191                desc.append(self._suboption.desc)
192            return ', '.join(desc)
193
194        return super(Option, self).__getattribute__(attr)
195
196    def suboption(self, name, desc, **kwargs):
197        subo = SubOption(name, desc, **kwargs)
198        subo._config = self._config
199        subo._group = self._group
200        subo._option = self
201        subo._number = len(self._suboptions)
202        self._suboptions.append(subo)
203        return subo
204
205    def clone(self, suboptions=True):
206        option = Option(self.__dict__['name'], self.__dict__['desc'])
207        option.update(self)
208        option._group = self._group
209        option._config = self._config
210        option._number = self._number
211        if suboptions:
212            option._suboptions.extend(self._suboptions)
213            option._suboption = self._suboption
214        return option
215
216    def subopts(self):
217        if not self._suboptions:
218            return [ self ]
219
220        subopts = []
221        for subo in self._suboptions:
222            option = self.clone()
223            option._suboption = subo
224            subopts.append(option)
225
226        return subopts
227
228    def printinfo(self):
229        super(Option, self).printinfo()
230        print 'config: %s' % self._config.name
231        super(Option, self).printverbose()
232
233class Group(Data):
234    def __init__(self, name, desc, **kwargs):
235        super(Group, self).__init__(name, desc, **kwargs)
236        self._options = []
237        self._number = None
238        self._checkpoint = False
239
240    def option(self, name, desc, **kwargs):
241        opt = Option(name, desc, **kwargs)
242        opt._config = self._config
243        opt._group = self
244        opt._number = len(self._options)
245        self._options.append(opt)
246        return opt
247
248    def options(self):
249        return self._options
250
251    def subopts(self):
252        subopts = []
253        for opt in self._options:
254            for subo in opt.subopts():
255                subopts.append(subo)
256        return subopts
257
258    def printinfo(self):
259        super(Group, self).printinfo()
260        print 'config: %s' % self._config.name
261        print 'options: %s' % [ o.name for o in self._options ]
262        super(Group, self).printverbose()
263
264class Configuration(Data):
265    def __init__(self, name, desc, **kwargs):
266        super(Configuration, self).__init__(name, desc, **kwargs)
267        self._groups = []
268        self._posfilters = []
269        self._negfilters = []
270
271    def group(self, name, desc, **kwargs):
272        grp = Group(name, desc, **kwargs)
273        grp._config = self
274        grp._number = len(self._groups)
275        self._groups.append(grp)
276        return grp
277
278    def groups(self):
279        return self._groups
280
281    def checkchildren(self, kids):
282        for kid in kids:
283            if kid._config != self:
284                raise AttributeError, "child from the wrong configuration"
285
286    def sortgroups(self, groups):
287        groups = [ (grp._number, grp) for grp in groups ]
288        groups.sort()
289        return [ grp[1] for grp in groups ]
290
291    def options(self, groups=None, checkpoint=False):
292        if groups is None:
293            groups = self._groups
294        self.checkchildren(groups)
295        groups = self.sortgroups(groups)
296        if checkpoint:
297            groups = [ grp for grp in groups if grp._checkpoint ]
298            optgroups = [ g.options() for g in groups ]
299        else:
300            optgroups = [ g.subopts() for g in groups ]
301        if not optgroups:
302            return
303        for options in crossproduct(optgroups):
304            for opt in options:
305                cpt = opt._group._checkpoint
306                if not isinstance(cpt, bool) and cpt != opt:
307                    if checkpoint:
308                        break
309                    else:
310                        yield options
311            else:
312                if checkpoint:
313                    yield options
314
315    def addfilter(self, filt, pos=True):
316        import re
317        filt = re.compile(filt)
318        if pos:
319            self._posfilters.append(filt)
320        else:
321            self._negfilters.append(filt)
322
323    def jobfilter(self, job):
324        for filt in self._negfilters:
325            if filt.match(job.name):
326                return False
327
328        if not self._posfilters:
329            return True
330
331        for filt in self._posfilters:
332            if filt.match(job.name):
333                return True
334
335        return False
336
337    def checkpoints(self, groups=None):
338        for options in self.options(groups, True):
339            job = Job(options)
340            if self.jobfilter(job):
341                yield job
342
343    def jobs(self, groups=None):
344        for options in self.options(groups, False):
345            job = Job(options)
346            if self.jobfilter(job):
347                yield job
348
349    def alljobs(self, groups=None):
350        for options in self.options(groups, True):
351            yield Job(options)
352        for options in self.options(groups, False):
353            yield Job(options)
354
355    def find(self, jobname):
356        for job in self.alljobs():
357            if job.name == jobname:
358                return job
359        else:
360            raise AttributeError, "job '%s' not found" % jobname
361
362    def job(self, options):
363        self.checkchildren(options)
364        options = [ (opt._group._number, opt) for opt in options ]
365        options.sort()
366        options = [ opt[1] for opt in options ]
367        job = Job(options)
368        return job
369
370    def printinfo(self):
371        super(Configuration, self).printinfo()
372        print 'groups: %s' % [ g.name for g in self._groups ]
373        super(Configuration, self).printverbose()
374
375def JobFile(jobfile):
376    from os.path import expanduser, isfile, join as joinpath
377    filename = expanduser(jobfile)
378
379    # Can't find filename in the current path, search sys.path
380    if not isfile(filename):
381        for path in sys.path:
382            testname = joinpath(path, filename)
383            if isfile(testname):
384                filename = testname
385                break
386        else:
387            raise AttributeError, \
388                  "Could not find file '%s'" % jobfile
389
390    data = {}
391    execfile(filename, data)
392    if 'conf' not in data:
393        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
401
402def main(conf=None):
403    import sys
404
405    usage = 'Usage: %s [-b] [-c] [-v] <jobfile>' % sys.argv[0]
406
407    try:
408        import getopt
409        opts, args = getopt.getopt(sys.argv[1:], '-bcv')
410    except getopt.GetoptError:
411        sys.exit(usage)
412
413    both = False
414    checkpoint = False
415    verbose = False
416    for opt,arg in opts:
417        if opt == '-b':
418            both = True
419            checkpoint = True
420        if opt == '-c':
421            checkpoint = True
422        if opt == '-v':
423            verbose = True
424
425    if conf is None:
426        if len(args) != 1:
427            raise AttributeError, usage
428        conf = JobFile(args[0])
429    else:
430        if len(args) != 0:
431            raise AttributeError, usage
432
433    if both:
434        jobs = conf.alljobs()
435    elif checkpoint:
436        jobs = conf.checkpoints()
437    else:
438        jobs = conf.jobs()
439
440    for job in jobs:
441        if verbose:
442            job.printinfo()
443        else:
444            cpt = ''
445            if job._checkpoint:
446                cpt = job._checkpoint.name
447            print job.name, cpt
448
449if __name__ == '__main__':
450    main()
451