jobfile.py revision 6654
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
31class Data(object):
32    def __init__(self, name, desc, **kwargs):
33        self.name = name
34        self.desc = desc
35        self.__dict__.update(kwargs)
36
37    def update(self, obj):
38        if not isinstance(obj, Data):
39            raise AttributeError, "can only update from Data object"
40
41        for key,val in obj.__dict__.iteritems():
42            if key.startswith('_') or key in ('name', 'desc'):
43                continue
44
45            if key not in self.__dict__:
46                self.__dict__[key] = val
47                continue
48
49            if not isinstance(val, dict):
50                if self.__dict__[key] == val:
51                    continue
52
53                raise AttributeError, \
54                      "%s specified more than once old: %s new: %s" % \
55                      (key, self.__dict__[key], val)
56
57            d = self.__dict__[key]
58            for k,v in val.iteritems():
59                if k in d:
60                    raise AttributeError, \
61                          "%s specified more than once in %s" % (k, key)
62                d[k] = v
63
64        if hasattr(self, 'system') and hasattr(obj, 'system'):
65            if self.system != obj.system:
66                raise AttributeError, \
67                      "conflicting values for system: '%s'/'%s'" % \
68                      (self.system, obj.system)
69
70    def printinfo(self):
71        if self.name:
72            print 'name: %s' % self.name
73        if self.desc:
74            print 'desc: %s' % self.desc
75        try:
76            if self.system:
77                print 'system: %s' % self.system
78        except AttributeError:
79            pass
80
81    def printverbose(self):
82        for key in self:
83            val = self[key]
84            if isinstance(val, dict):
85                import pprint
86                val = pprint.pformat(val)
87            print '%-20s = %s' % (key, val)
88        print
89
90    def __contains__(self, attr):
91        if attr.startswith('_'):
92            return False
93        return attr in self.__dict__
94
95    def __getitem__(self, key):
96        if key.startswith('_'):
97            raise KeyError, "Key '%s' not found" % attr
98        return self.__dict__[key]
99
100    def __iter__(self):
101        keys = self.__dict__.keys()
102        keys.sort()
103        for key in keys:
104            if not key.startswith('_'):
105                yield key
106
107    def optiondict(self):
108        import m5.util
109        result = m5.util.optiondict()
110        for key in self:
111            result[key] = self[key]
112        return result
113
114    def __repr__(self):
115        d = {}
116        for key,value in self.__dict__.iteritems():
117            if not key.startswith('_'):
118                d[key] = value
119
120        return "<%s: %s>" % (type(self).__name__, d)
121
122    def __str__(self):
123        return self.name
124
125class Job(Data):
126    def __init__(self, options):
127        super(Job, self).__init__('', '')
128
129        config = options[0]._config
130        for opt in options:
131            if opt._config != config:
132                raise AttributeError, \
133                      "All options are not from the same Configuration"
134
135        self._config = config
136        self._groups = [ opt._group for opt in options ]
137        self._options = options
138
139        self.update(self._config)
140        for group in self._groups:
141            self.update(group)
142
143        self._is_checkpoint = True
144
145        for option in self._options:
146            self.update(option)
147            if not option._group._checkpoint:
148                self._is_checkpoint = False
149
150            if option._suboption:
151                self.update(option._suboption)
152                self._is_checkpoint = False
153
154        names = [ ]
155        for opt in self._options:
156            if opt.name:
157                names.append(opt.name)
158        self.name = ':'.join(names)
159
160        descs = [ ]
161        for opt in self._options:
162            if opt.desc:
163                descs.append(opt.desc)
164        self.desc = ', '.join(descs)
165
166        self._checkpoint = None
167        if not self._is_checkpoint:
168            opts = []
169            for opt in options:
170                cpt = opt._group._checkpoint
171                if not cpt:
172                    continue
173                if isinstance(cpt, Option):
174                    opt = cpt.clone(suboptions=False)
175                else:
176                    opt = opt.clone(suboptions=False)
177
178                opts.append(opt)
179
180            if opts:
181                self._checkpoint = Job(opts)
182
183    def clone(self):
184        return Job(self._options)
185
186    def printinfo(self):
187        super(Job, self).printinfo()
188        if self._checkpoint:
189            print 'checkpoint: %s' % self._checkpoint.name
190        print 'config: %s' % self._config.name
191        print 'groups: %s' % [ g.name for g in self._groups ]
192        print 'options: %s' % [ o.name for o in self._options ]
193        super(Job, self).printverbose()
194
195class SubOption(Data):
196    def __init__(self, name, desc, **kwargs):
197        super(SubOption, self).__init__(name, desc, **kwargs)
198        self._number = None
199
200class Option(Data):
201    def __init__(self, name, desc, **kwargs):
202        super(Option, self).__init__(name, desc, **kwargs)
203        self._suboptions = []
204        self._suboption = None
205        self._number = None
206
207    def __getattribute__(self, attr):
208        if attr == 'name':
209            name = self.__dict__[attr]
210            if self._suboption is not None:
211                name = '%s:%s' % (name, self._suboption.name)
212            return name
213
214        if attr == 'desc':
215            desc = [ self.__dict__[attr] ]
216            if self._suboption is not None and self._suboption.desc:
217                desc.append(self._suboption.desc)
218            return ', '.join(desc)
219
220        return super(Option, self).__getattribute__(attr)
221
222    def suboption(self, name, desc, **kwargs):
223        subo = SubOption(name, desc, **kwargs)
224        subo._config = self._config
225        subo._group = self._group
226        subo._option = self
227        subo._number = len(self._suboptions)
228        self._suboptions.append(subo)
229        return subo
230
231    def clone(self, suboptions=True):
232        option = Option(self.__dict__['name'], self.__dict__['desc'])
233        option.update(self)
234        option._group = self._group
235        option._config = self._config
236        option._number = self._number
237        if suboptions:
238            option._suboptions.extend(self._suboptions)
239            option._suboption = self._suboption
240        return option
241
242    def subopts(self):
243        if not self._suboptions:
244            return [ self ]
245
246        subopts = []
247        for subo in self._suboptions:
248            option = self.clone()
249            option._suboption = subo
250            subopts.append(option)
251
252        return subopts
253
254    def printinfo(self):
255        super(Option, self).printinfo()
256        print 'config: %s' % self._config.name
257        super(Option, self).printverbose()
258
259class Group(Data):
260    def __init__(self, name, desc, **kwargs):
261        super(Group, self).__init__(name, desc, **kwargs)
262        self._options = []
263        self._number = None
264        self._checkpoint = False
265
266    def option(self, name, desc, **kwargs):
267        opt = Option(name, desc, **kwargs)
268        opt._config = self._config
269        opt._group = self
270        opt._number = len(self._options)
271        self._options.append(opt)
272        return opt
273
274    def options(self):
275        return self._options
276
277    def subopts(self):
278        subopts = []
279        for opt in self._options:
280            for subo in opt.subopts():
281                subopts.append(subo)
282        return subopts
283
284    def printinfo(self):
285        super(Group, self).printinfo()
286        print 'config: %s' % self._config.name
287        print 'options: %s' % [ o.name for o in self._options ]
288        super(Group, self).printverbose()
289
290class Configuration(Data):
291    def __init__(self, name, desc, **kwargs):
292        super(Configuration, self).__init__(name, desc, **kwargs)
293        self._groups = []
294        self._posfilters = []
295        self._negfilters = []
296
297    def group(self, name, desc, **kwargs):
298        grp = Group(name, desc, **kwargs)
299        grp._config = self
300        grp._number = len(self._groups)
301        self._groups.append(grp)
302        return grp
303
304    def groups(self):
305        return self._groups
306
307    def checkchildren(self, kids):
308        for kid in kids:
309            if kid._config != self:
310                raise AttributeError, "child from the wrong configuration"
311
312    def sortgroups(self, groups):
313        groups = [ (grp._number, grp) for grp in groups ]
314        groups.sort()
315        return [ grp[1] for grp in groups ]
316
317    def options(self, groups=None, checkpoint=False):
318        if groups is None:
319            groups = self._groups
320        self.checkchildren(groups)
321        groups = self.sortgroups(groups)
322        if checkpoint:
323            groups = [ grp for grp in groups if grp._checkpoint ]
324            optgroups = [ g.options() for g in groups ]
325        else:
326            optgroups = [ g.subopts() for g in groups ]
327        if not optgroups:
328            return
329
330        import m5.util
331        for options in m5.util.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
422    return data['conf']
423
424def main(conf=None):
425    usage = 'Usage: %s [-b] [-c] [-v]' % sys.argv[0]
426    if conf is None:
427        usage += ' <jobfile>'
428
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()
473