jobfile.py revision 13671
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
29from __future__ import print_function
30
31import sys
32
33class Data(object):
34    def __init__(self, name, desc, **kwargs):
35        self.name = name
36        self.desc = desc
37        self.__dict__.update(kwargs)
38
39    def update(self, obj):
40        if not isinstance(obj, Data):
41            raise AttributeError("can only update from Data object")
42
43        for key,val in obj.__dict__.iteritems():
44            if key.startswith('_') or key in ('name', 'desc'):
45                continue
46
47            if key not in self.__dict__:
48                self.__dict__[key] = val
49                continue
50
51            if not isinstance(val, dict):
52                if self.__dict__[key] == val:
53                    continue
54
55                raise AttributeError(
56                    "%s specified more than once old: %s new: %s" % \
57                    (key, self.__dict__[key], val))
58
59            d = self.__dict__[key]
60            for k,v in val.iteritems():
61                if k in d:
62                    raise AttributeError(
63                        "%s specified more than once in %s" % (k, key))
64                d[k] = v
65
66        if hasattr(self, 'system') and hasattr(obj, 'system'):
67            if self.system != obj.system:
68                raise AttributeError(
69                    "conflicting values for system: '%s'/'%s'" % \
70                    (self.system, obj.system))
71
72    def printinfo(self):
73        if self.name:
74            print('name: %s' % self.name)
75        if self.desc:
76            print('desc: %s' % self.desc)
77        try:
78            if self.system:
79                print('system: %s' % self.system)
80        except AttributeError:
81            pass
82
83    def printverbose(self):
84        for key in self:
85            val = self[key]
86            if isinstance(val, dict):
87                import pprint
88                val = pprint.pformat(val)
89            print('%-20s = %s' % (key, val))
90        print()
91
92    def __contains__(self, attr):
93        if attr.startswith('_'):
94            return False
95        return attr in self.__dict__
96
97    def __getitem__(self, key):
98        if key.startswith('_'):
99            raise KeyError("Key '%s' not found" % attr)
100        return self.__dict__[key]
101
102    def __iter__(self):
103        keys = self.__dict__.keys()
104        keys.sort()
105        for key in keys:
106            if not key.startswith('_'):
107                yield key
108
109    def optiondict(self):
110        import m5.util
111        result = m5.util.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
332        import m5.util
333        for options in m5.util.crossproduct(optgroups):
334            for opt in options:
335                cpt = opt._group._checkpoint
336                if not isinstance(cpt, bool) and cpt != opt:
337                    if checkpoint:
338                        break
339                    else:
340                        yield options
341            else:
342                if checkpoint:
343                    yield options
344
345    def addfilter(self, filt, pos=True):
346        import re
347        filt = re.compile(filt)
348        if pos:
349            self._posfilters.append(filt)
350        else:
351            self._negfilters.append(filt)
352
353    def jobfilter(self, job):
354        for filt in self._negfilters:
355            if filt.match(job.name):
356                return False
357
358        if not self._posfilters:
359            return True
360
361        for filt in self._posfilters:
362            if filt.match(job.name):
363                return True
364
365        return False
366
367    def checkpoints(self, groups=None):
368        for options in self.options(groups, True):
369            job = Job(options)
370            if self.jobfilter(job):
371                yield job
372
373    def jobs(self, groups=None):
374        for options in self.options(groups, False):
375            job = Job(options)
376            if self.jobfilter(job):
377                yield job
378
379    def alljobs(self, groups=None):
380        for options in self.options(groups, True):
381            yield Job(options)
382        for options in self.options(groups, False):
383            yield Job(options)
384
385    def find(self, jobname):
386        for job in self.alljobs():
387            if job.name == jobname:
388                return job
389        else:
390            raise AttributeError("job '%s' not found" % jobname)
391
392    def job(self, options):
393        self.checkchildren(options)
394        options = [ (opt._group._number, opt) for opt in options ]
395        options.sort()
396        options = [ opt[1] for opt in options ]
397        job = Job(options)
398        return job
399
400    def printinfo(self):
401        super(Configuration, self).printinfo()
402        print('groups: %s' % [ g.name for g in self._groups ])
403        super(Configuration, self).printverbose()
404
405def JobFile(jobfile):
406    from os.path import expanduser, isfile, join as joinpath
407    filename = expanduser(jobfile)
408
409    # Can't find filename in the current path, search sys.path
410    if not isfile(filename):
411        for path in sys.path:
412            testname = joinpath(path, filename)
413            if isfile(testname):
414                filename = testname
415                break
416        else:
417            raise AttributeError("Could not find file '%s'" % jobfile)
418
419    data = {}
420    exec(compile(open(filename).read(), filename, 'exec'), data)
421    if 'conf' not in data:
422        raise ImportError('cannot import name conf from %s' % jobfile)
423    return data['conf']
424
425def main(conf=None):
426    usage = 'Usage: %s [-b] [-c] [-v]' % sys.argv[0]
427    if conf is None:
428        usage += ' <jobfile>'
429
430    try:
431        import getopt
432        opts, args = getopt.getopt(sys.argv[1:], '-bcv')
433    except getopt.GetoptError:
434        sys.exit(usage)
435
436    both = False
437    checkpoint = False
438    verbose = False
439    for opt,arg in opts:
440        if opt == '-b':
441            both = True
442            checkpoint = True
443        if opt == '-c':
444            checkpoint = True
445        if opt == '-v':
446            verbose = True
447
448    if conf is None:
449        if len(args) != 1:
450            raise AttributeError(usage)
451        conf = JobFile(args[0])
452    else:
453        if len(args) != 0:
454            raise AttributeError(usage)
455
456    if both:
457        jobs = conf.alljobs()
458    elif checkpoint:
459        jobs = conf.checkpoints()
460    else:
461        jobs = conf.jobs()
462
463    for job in jobs:
464        if verbose:
465            job.printinfo()
466        else:
467            cpt = ''
468            if job._checkpoint:
469                cpt = job._checkpoint.name
470            print(job.name, cpt)
471
472if __name__ == '__main__':
473    main()
474