jobfile.py revision 5618
12SN/A# Copyright (c) 2005-2006 The Regents of The University of Michigan 21762SN/A# All rights reserved. 32SN/A# 42SN/A# Redistribution and use in source and binary forms, with or without 52SN/A# modification, are permitted provided that the following conditions are 62SN/A# met: redistributions of source code must retain the above copyright 72SN/A# notice, this list of conditions and the following disclaimer; 82SN/A# redistributions in binary form must reproduce the above copyright 92SN/A# notice, this list of conditions and the following disclaimer in the 102SN/A# documentation and/or other materials provided with the distribution; 112SN/A# neither the name of the copyright holders nor the names of its 122SN/A# contributors may be used to endorse or promote products derived from 132SN/A# this software without specific prior written permission. 142SN/A# 152SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 162SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 172SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 182SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 192SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 202SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 212SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 222SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 232SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 242SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 252SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 262SN/A# 272665Ssaidi@eecs.umich.edu# Authors: Nathan Binkert 282665Ssaidi@eecs.umich.edu 292SN/Aimport sys 302SN/A 312SN/Afrom attrdict import optiondict 322SN/Afrom misc import crossproduct 338229Snate@binkert.org 348229Snate@binkert.orgclass Data(object): 352SN/A def __init__(self, name, desc, **kwargs): 362SN/A self.name = name 378229Snate@binkert.org self.desc = desc 388229Snate@binkert.org self.__dict__.update(kwargs) 3956SN/A 4056SN/A def update(self, obj): 418229Snate@binkert.org if not isinstance(obj, Data): 422SN/A raise AttributeError, "can only update from Data object" 432SN/A 442SN/A for key,val in obj.__dict__.iteritems(): 455523Snate@binkert.org if key.startswith('_') or key in ('name', 'desc'): 465523Snate@binkert.org continue 475523Snate@binkert.org 485523Snate@binkert.org if key not in self.__dict__: 495523Snate@binkert.org self.__dict__[key] = val 505523Snate@binkert.org continue 515523Snate@binkert.org 525523Snate@binkert.org if not isinstance(val, dict): 535523Snate@binkert.org if self.__dict__[key] == val: 545523Snate@binkert.org continue 555523Snate@binkert.org 565523Snate@binkert.org raise AttributeError, \ 575523Snate@binkert.org "%s specified more than once old: %s new: %s" % \ 585523Snate@binkert.org (key, self.__dict__[key], val) 595523Snate@binkert.org 605523Snate@binkert.org d = self.__dict__[key] 615523Snate@binkert.org for k,v in val.iteritems(): 622SN/A if k in d: 632SN/A raise AttributeError, \ 642SN/A "%s specified more than once in %s" % (k, key) 652SN/A d[k] = v 662SN/A 672SN/A if hasattr(self, 'system') and hasattr(obj, 'system'): 682SN/A if self.system != obj.system: 692SN/A raise AttributeError, \ 702SN/A "conflicting values for system: '%s'/'%s'" % \ 712SN/A (self.system, obj.system) 722SN/A 732SN/A def printinfo(self): 742SN/A if self.name: 752SN/A print 'name: %s' % self.name 762SN/A if self.desc: 772SN/A print 'desc: %s' % self.desc 782SN/A try: 792SN/A if self.system: 802SN/A print 'system: %s' % self.system 812SN/A except AttributeError: 822SN/A pass 832SN/A 842SN/A def printverbose(self): 851290SN/A for key in self: 862SN/A val = self[key] 872SN/A if isinstance(val, dict): 882SN/A import pprint 892SN/A val = pprint.pformat(val) 902SN/A print '%-20s = %s' % (key, val) 912SN/A print 922SN/A 932SN/A def __contains__(self, attr): 942SN/A if attr.startswith('_'): 952SN/A return False 962SN/A return attr in self.__dict__ 972SN/A 982SN/A def __getitem__(self, key): 992SN/A if key.startswith('_'): 1002SN/A raise KeyError, "Key '%s' not found" % attr 1012SN/A return self.__dict__[key] 1022SN/A 1032SN/A def __iter__(self): 1042SN/A keys = self.__dict__.keys() 1052SN/A keys.sort() 1062SN/A for key in keys: 1072SN/A if not key.startswith('_'): 1082SN/A yield key 1092SN/A 1102SN/A def optiondict(self): 1115523Snate@binkert.org result = optiondict() 1122SN/A for key in self: 1132SN/A result[key] = self[key] 1142SN/A return result 1152SN/A 1162SN/A def __repr__(self): 1172SN/A d = {} 1182SN/A for key,value in self.__dict__.iteritems(): 1192SN/A if not key.startswith('_'): 1202SN/A d[key] = value 1212SN/A 1222SN/A return "<%s: %s>" % (type(self).__name__, d) 1232SN/A 1242SN/A def __str__(self): 1252SN/A return self.name 1262SN/A 1272SN/Aclass Job(Data): 1282SN/A def __init__(self, options): 1292SN/A super(Job, self).__init__('', '') 1302SN/A 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 for options in 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