1#!/usr/bin/env python 2 3import os,shutil, string, re 4from distutils.sysconfig import parse_makefile 5import sys 6import logging, time 7import types 8sys.path.insert(0, os.path.abspath(os.path.dirname(__file__))) 9from cmakegen import Mistakes, stripsplit, AUTODIRS, SKIPDIRS 10from cmakegen import defaultdict # collections.defaultdict, with fallback for python-2.4 11from gmakegen import * 12 13import inspect 14thisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) 15sys.path.insert(0,thisscriptdir) 16import testparse 17import example_template 18 19class generateExamples(Petsc): 20 """ 21 gmakegen.py has basic structure for finding the files, writing out 22 the dependencies, etc. 23 """ 24 def __init__(self,petsc_dir=None, petsc_arch=None, verbose=False, single_ex=False): 25 super(generateExamples, self).__init__(petsc_dir=None, petsc_arch=None, verbose=False) 26 27 self.single_ex=single_ex 28 self.arch_dir=os.path.join(self.petsc_dir,self.petsc_arch) 29 self.ptNaming=True 30 # Whether to write out a useful debugging 31 #if verbose: self.summarize=True 32 self.summarize=True 33 34 # For help in setting the requirements 35 self.precision_types="single double __float128 int32".split() 36 self.integer_types="int32 int64".split() 37 self.languages="fortran cuda cxx".split() # Always requires C so do not list 38 39 # Things that are not test 40 self.buildkeys=testparse.buildkeys 41 42 # Adding a dictionary for storing sources, objects, and tests 43 # to make building the dependency tree easier 44 self.sources={} 45 self.objects={} 46 self.tests={} 47 for pkg in PKGS: 48 self.sources[pkg]={} 49 self.objects[pkg]=[] 50 self.tests[pkg]={} 51 for lang in LANGS: 52 self.sources[pkg][lang]={} 53 self.sources[pkg][lang]['srcs']=[] 54 self.tests[pkg][lang]={} 55 56 # Do some initialization 57 self.testroot_dir=os.path.join(self.arch_dir,"tests") 58 if not os.path.isdir(self.testroot_dir): os.makedirs(self.testroot_dir) 59 60 self.indent=" " 61 return 62 63 def nameSpace(self,srcfile,srcdir): 64 """ 65 Because the scripts have a non-unique naming, the pretty-printing 66 needs to convey the srcdir and srcfile. There are two ways of doing this. 67 """ 68 if self.ptNaming: 69 cdir=srcdir.split('src')[1].lstrip("/").rstrip("/") 70 prefix=cdir.replace('/examples/','_').replace("/","_")+"-" 71 nameString=prefix+srcfile 72 else: 73 #nameString=srcdir+": "+srcfile 74 nameString=srcfile 75 return nameString 76 77 def getLanguage(self,srcfile): 78 """ 79 Based on the source, determine associated language as found in gmakegen.LANGS 80 Can we just return srcext[1:\] now? 81 """ 82 langReq=None 83 srcext=os.path.splitext(srcfile)[-1] 84 if srcext in ".F90".split(): langReq="F90" 85 if srcext in ".F".split(): langReq="F" 86 if srcext in ".cxx".split(): langReq="cxx" 87 if srcext == ".cu": langReq="cu" 88 if srcext == ".c": langReq="c" 89 #if not langReq: print "ERROR: ", srcext, srcfile 90 return langReq 91 92 def _getLoopVars(self,inDict,testname, isSubtest=False): 93 """ 94 Given: 'args: -bs {{1 2 3 4 5}} -pc_type {{cholesky sor}} -ksp_monitor' 95 Return: 96 inDict['args']: -ksp_monitor 97 inDict['subargs']: -bs ${bs} -pc_type ${pc_type} 98 loopVars['subargs']['varlist']=['bs' 'pc_type'] # Don't worry about OrderedDict 99 loopVars['subargs']['bs']=[["bs"],["1 2 3 4 5"]] 100 loopVars['subargs']['pc_type']=[["pc_type"],["cholesky sor"]] 101 subst should be passed in instead of inDict 102 """ 103 loopVars={}; newargs="" 104 lkeys=inDict.keys() 105 lsuffix='_' 106 from testparse import parseLoopArgs 107 for key in lkeys: 108 if type(inDict[key])!=types.StringType: continue 109 keystr = str(inDict[key]) 110 akey=('subargs' if key=='args' else key) # what to assign 111 if akey not in inDict: inDict[akey]='' 112 varlist=[] 113 for varset in re.split('-(?=[a-zA-Z])',keystr): 114 if not varset.strip(): continue 115 if '{{' in varset: 116 keyvar,lvars,ftype=parseLoopArgs(varset) 117 if akey not in loopVars: loopVars[akey]={} 118 varlist.append(keyvar) 119 loopVars[akey][keyvar]=[keyvar,lvars] 120 if akey=='nsize': 121 inDict[akey] = '${' + keyvar + '}' 122 lsuffix+=akey+'-'+inDict[akey]+'_' 123 else: 124 inDict[akey] += ' -'+keyvar+' ${' + keyvar + '}' 125 lsuffix+=keyvar+'-${' + keyvar + '}_' 126 else: 127 if key=='args': newargs+=" -"+varset.strip() 128 if len(varlist)>0: loopVars[akey]['varlist']=varlist 129 130 131 # For subtests, args are always substituted in (not top level) 132 if isSubtest: 133 inDict['subargs']+=" "+newargs.strip() 134 inDict['args']='' 135 if 'label_suffix' in inDict: 136 inDict['label_suffix']+=lsuffix.rstrip('_') 137 else: 138 inDict['label_suffix']=lsuffix.rstrip('_') 139 else: 140 if len(loopVars.keys())>0: 141 inDict['args']=newargs.strip() 142 inDict['label_suffix']=lsuffix.rstrip('_') 143 if len(loopVars.keys())>0: 144 return loopVars 145 else: 146 return None 147 148 def getArgLabel(self,testDict): 149 """ 150 In all of the arguments in the test dictionary, create a simple 151 string for searching within the makefile system. For simplicity in 152 search, remove "-", for strings, etc. 153 Also, concatenate the arg commands 154 For now, ignore nsize -- seems hard to search for anyway 155 """ 156 # Collect all of the args associated with a test 157 argStr=("" if 'args' not in testDict else testDict['args']) 158 if 'subtests' in testDict: 159 for stest in testDict["subtests"]: 160 sd=testDict[stest] 161 argStr=argStr+("" if 'args' not in sd else sd['args']) 162 163 # Now go through and cleanup 164 argStr=re.sub('{{(.*?)}}',"",argStr) 165 argStr=re.sub('-'," ",argStr) 166 for digit in string.digits: argStr=re.sub(digit," ",argStr) 167 argStr=re.sub("\.","",argStr) 168 argStr=re.sub(",","",argStr) 169 argStr=re.sub('\+',' ',argStr) 170 argStr=re.sub(' +',' ',argStr) # Remove repeated white space 171 return argStr.strip() 172 173 def addToSources(self,exfile,root,srcDict): 174 """ 175 Put into data structure that allows easy generation of makefile 176 """ 177 pkg=self.relpath(self.petsc_dir,root).split("/")[1] 178 fullfile=os.path.join(root,exfile) 179 relpfile=self.relpath(self.petsc_dir,fullfile) 180 lang=self.getLanguage(exfile) 181 if not lang: return 182 self.sources[pkg][lang]['srcs'].append(relpfile) 183 if 'depends' in srcDict: 184 depSrc=srcDict['depends'] 185 depObj=os.path.splitext(depSrc)[0]+".o" 186 self.sources[pkg][lang][exfile]=depObj 187 188 # In gmakefile, ${TESTDIR} var specifies the object compilation 189 testsdir=self.relpath(self.petsc_dir,root)+"/" 190 objfile="${TESTDIR}/"+testsdir+os.path.splitext(exfile)[0]+".o" 191 self.objects[pkg].append(objfile) 192 return 193 194 def addToTests(self,test,root,exfile,execname,testDict): 195 """ 196 Put into data structure that allows easy generation of makefile 197 Organized by languages to allow testing of languages 198 """ 199 pkg=self.relpath(self.petsc_dir,root).split("/")[1] 200 #nmtest=self.nameSpace(test,root) 201 rpath=self.relpath(self.petsc_dir,root) 202 nmtest=os.path.join(rpath,test) 203 lang=self.getLanguage(exfile) 204 if not lang: return 205 self.tests[pkg][lang][nmtest]={} 206 self.tests[pkg][lang][nmtest]['exfile']=os.path.join(rpath,exfile) 207 self.tests[pkg][lang][nmtest]['exec']=execname 208 self.tests[pkg][lang][nmtest]['argLabel']=self.getArgLabel(testDict) 209 return 210 211 def getExecname(self,exfile,root): 212 """ 213 Generate bash script using template found next to this file. 214 This file is read in at constructor time to avoid file I/O 215 """ 216 rpath=self.relpath(self.petsc_dir,root) 217 if self.single_ex: 218 execname=rpath.split("/")[1]+"-ex" 219 else: 220 execname=os.path.splitext(exfile)[0] 221 return execname 222 223 def getSubstVars(self,testDict,rpath,testname): 224 """ 225 Create a dictionary with all of the variables that get substituted 226 into the template commands found in example_template.py 227 """ 228 subst={} 229 230 # Handle defaults of testparse.acceptedkeys (e.g., ignores subtests) 231 if 'nsize' not in testDict: testDict['nsize']=1 232 for ak in testparse.acceptedkeys: 233 if ak=='test': continue 234 subst[ak]=(testDict[ak] if ak in testDict else '') 235 236 # Now do other variables 237 subst['execname']=testDict['execname'] 238 if 'filter' in testDict: 239 subst['filter']="'"+testDict['filter']+"'" # Quotes are tricky - overwrite 240 241 # Others 242 subst['subargs']='' # Default. For variables override 243 subst['srcdir']=os.path.join(self.petsc_dir,rpath) 244 subst['label_suffix']='' 245 subst['comments']="\n#".join(subst['comments'].split("\n")) 246 if subst['comments']: subst['comments']="#"+subst['comments'] 247 subst['exec']="../"+subst['execname'] 248 subst['testroot']=self.testroot_dir 249 subst['testname']=testname 250 dp = self.conf.get('DATAFILESPATH','') 251 subst['datafilespath_line'] = 'DATAFILESPATH=${DATAFILESPATH:-"'+dp+'"}' 252 253 # This is used to label some matrices 254 subst['petsc_index_size']=str(self.conf['PETSC_INDEX_SIZE']) 255 subst['petsc_scalar_size']=str(self.conf['PETSC_SCALAR_SIZE']) 256 257 # These can have for loops and are treated separately later 258 subst['nsize']=str(subst['nsize']) 259 260 #Conf vars 261 if self.petsc_arch.find('valgrind')>=0: 262 subst['mpiexec']='petsc_mpiexec_valgrind ' + self.conf['MPIEXEC'] 263 else: 264 subst['mpiexec']=self.conf['MPIEXEC'] 265 subst['petsc_dir']=self.petsc_dir # not self.conf['PETSC_DIR'] as this could be windows path 266 subst['diff']=self.conf['DIFF'] 267 subst['rm']=self.conf['RM'] 268 subst['grep']=self.conf['GREP'] 269 subst['petsc_lib_dir']=self.conf['PETSC_LIB_DIR'] 270 subst['wpetsc_dir']=self.conf['wPETSC_DIR'] 271 272 # Output file is special because of subtests override 273 defroot=(re.sub("run","",testname) if testname.startswith("run") else testname) 274 if not "_" in defroot: defroot=defroot+"_1" 275 subst['defroot']=defroot 276 subst['label']=self.nameSpace(defroot,subst['srcdir']) 277 subst['redirect_file']=defroot+".tmp" 278 if 'output_file' not in testDict: 279 subst['output_file']="output/"+defroot+".out" 280 # Add in the full path here. 281 subst['output_file']=os.path.join(subst['srcdir'],subst['output_file']) 282 if not os.path.isfile(os.path.join(self.petsc_dir,subst['output_file'])): 283 if not subst['TODO']: 284 print "Warning: "+subst['output_file']+" not found." 285 # Worry about alt files here -- see 286 # src/snes/examples/tutorials/output/ex22*.out 287 altlist=[subst['output_file']] 288 for i in range(1,3): 289 altroot=defroot+"_alt" 290 if i==2: altroot=altroot+"_2" 291 af="output/"+altroot+".out" 292 srcaf=os.path.join(subst['srcdir'],af) 293 fullaf=os.path.join(self.petsc_dir,srcaf) 294 if os.path.isfile(fullaf): altlist.append(srcaf) 295 if len(altlist)>1: subst['altfiles']=altlist 296 #if len(altlist)>1: print "Found alt files: ",altlist 297 298 return subst 299 300 def getCmds(self,subst,i): 301 """ 302 Generate bash script using template found next to this file. 303 This file is read in at constructor time to avoid file I/O 304 """ 305 indnt=self.indent 306 nindnt=i # the start and has to be consistent with below 307 cmdLines="" 308 309 # MPI is the default -- but we have a few odd commands 310 if not subst['command']: 311 cmd=indnt*nindnt+self._substVars(subst,example_template.mpitest) 312 else: 313 cmd=indnt*nindnt+self._substVars(subst,example_template.commandtest) 314 cmdLines+=cmd+"\n\n" 315 316 if not subst['filter_output']: 317 if 'altfiles' not in subst: 318 cmd=indnt*nindnt+self._substVars(subst,example_template.difftest) 319 else: 320 # Have to do it by hand a bit because of variable number of alt files 321 rf=subst['redirect_file'] 322 cmd=indnt*nindnt+example_template.difftest.split('@')[0] 323 for i in range(len(subst['altfiles'])): 324 af=subst['altfiles'][i] 325 cmd+=af+' '+rf+' > diff-${testname}-'+str(i)+'.out 2> diff-${testname}-'+str(i)+'.out' 326 if i!=len(subst['altfiles'])-1: 327 cmd+=' || ${diff_exe} ' 328 else: 329 cmd+='" diff-${testname}.out diff-${testname}.out diff-${label}' 330 cmd+=subst['label_suffix']+' ""' # Quotes are painful 331 else: 332 cmd=indnt*nindnt+self._substVars(subst,example_template.filterdifftest) 333 cmdLines+=cmd+"\n" 334 return cmdLines 335 336 def _substVars(self,subst,origStr): 337 """ 338 Substitute variables 339 """ 340 Str=origStr 341 for subkey in subst: 342 if type(subst[subkey])!=types.StringType: continue 343 patt="@"+subkey.upper()+"@" 344 Str=re.sub(patt,subst[subkey],Str) 345 return Str 346 347 def _writeTodoSkip(self,fh,tors,reasons,footer): 348 """ 349 Write out the TODO and SKIP lines in the file 350 The TODO or SKIP variable, tors, should be lower case 351 """ 352 TORS=tors.upper() 353 template=eval("example_template."+tors+"line") 354 tsStr=re.sub("@"+TORS+"COMMENT@",', '.join(reasons),template) 355 tab = '' 356 if reasons: 357 fh.write('if ! $force; then\n') 358 tab = tab + ' ' 359 if reasons == ["Requires DATAFILESPATH"]: 360 # The only reason not to run is DATAFILESPATH, which we check at run-time 361 fh.write(tab + 'if test -z "${DATAFILESPATH}"; then\n') 362 tab = tab + ' ' 363 if reasons: 364 fh.write(tab+tsStr+"\n" + tab + "total=1; "+tors+"=1\n") 365 fh.write(tab+footer+"\n") 366 fh.write(tab+"exit\n") 367 if reasons == ["Requires DATAFILESPATH"]: 368 fh.write(' fi\n') 369 if reasons: 370 fh.write('fi\n') 371 fh.write('\n\n') 372 return 373 374 def getLoopVarsHead(self,loopVars,i): 375 """ 376 Generate a nicely indented string with the format loops 377 Here is what the data structure looks like 378 loopVars['subargs']['varlist']=['bs' 'pc_type'] # Don't worry about OrderedDict 379 loopVars['subargs']['bs']=["i","1 2 3 4 5"] 380 loopVars['subargs']['pc_type']=["j","cholesky sor"] 381 """ 382 outstr=''; indnt=self.indent 383 for key in loopVars: 384 for var in loopVars[key]['varlist']: 385 varval=loopVars[key][var] 386 outstr += indnt * i + "for "+varval[0]+" in "+varval[1]+"; do\n" 387 i = i + 1 388 return (outstr,i) 389 390 def getLoopVarsFoot(self,loopVars,i): 391 outstr=''; indnt=self.indent 392 for key in loopVars: 393 for var in loopVars[key]['varlist']: 394 i = i - 1 395 outstr += indnt * i + "done\n" 396 return (outstr,i) 397 398 def genRunScript(self,testname,root,isRun,srcDict): 399 """ 400 Generate bash script using template found next to this file. 401 This file is read in at constructor time to avoid file I/O 402 """ 403 # runscript_dir directory has to be consistent with gmakefile 404 testDict=srcDict[testname] 405 rpath=self.relpath(self.petsc_dir,root) 406 runscript_dir=os.path.join(self.testroot_dir,rpath) 407 if not os.path.isdir(runscript_dir): os.makedirs(runscript_dir) 408 fh=open(os.path.join(runscript_dir,testname+".sh"),"w") 409 petscvarfile=os.path.join(self.arch_dir,'lib','petsc','conf','petscvariables') 410 411 # Get variables to go into shell scripts. last time testDict used 412 subst=self.getSubstVars(testDict,rpath,testname) 413 loopVars = self._getLoopVars(subst,testname) # Alters subst as well 414 #if '33_' in testname: print subst['subargs'] 415 416 #Handle runfiles 417 for lfile in subst.get('localrunfiles','').split(): 418 fullfile=os.path.join(self.petsc_dir,rpath,lfile) 419 shutil.copy(fullfile,runscript_dir) 420 # Check subtests for local runfiles 421 for stest in subst.get("subtests",[]): 422 for lfile in testDict[stest].get('localrunfiles','').split(): 423 fullfile=os.path.join(self.petsc_dir,rpath,lfile) 424 shutil.copy(fullfile,self.runscript_dir) 425 426 # Now substitute the key variables into the header and footer 427 header=self._substVars(subst,example_template.header) 428 # The header is done twice to enable @...@ in header 429 header=self._substVars(subst,header) 430 footer=re.sub('@TESTROOT@',subst['testroot'],example_template.footer) 431 432 # Start writing the file 433 fh.write(header+"\n") 434 435 # If there is a TODO or a SKIP then we do it before writing out the 436 # rest of the command (which is useful for working on the test) 437 # SKIP and TODO can be for the source file or for the runs 438 self._writeTodoSkip(fh,'todo',[s for s in [srcDict.get('TODO',''), testDict.get('TODO','')] if s],footer) 439 self._writeTodoSkip(fh,'skip',srcDict.get('SKIP',[]) + testDict.get('SKIP',[]),footer) 440 441 j=0 # for indentation 442 443 if loopVars: 444 (loopHead,j) = self.getLoopVarsHead(loopVars,j) 445 if (loopHead): fh.write(loopHead+"\n") 446 447 # Subtests are special 448 if 'subtests' in testDict: 449 substP=subst # Subtests can inherit args but be careful 450 for stest in testDict["subtests"]: 451 subst=substP.copy() 452 subst.update(testDict[stest]) 453 subst['nsize']=str(subst['nsize']) 454 sLoopVars = self._getLoopVars(subst,testname,isSubtest=True) 455 #if '10_9' in testname: print sLoopVars 456 if sLoopVars: 457 (sLoopHead,j) = self.getLoopVarsHead(sLoopVars,j) 458 fh.write(sLoopHead+"\n") 459 fh.write(self.getCmds(subst,j)+"\n") 460 if sLoopVars: 461 (sLoopFoot,j) = self.getLoopVarsFoot(sLoopVars,j) 462 fh.write(sLoopFoot+"\n") 463 else: 464 fh.write(self.getCmds(subst,j)+"\n") 465 466 if loopVars: 467 (loopFoot,j) = self.getLoopVarsFoot(loopVars,j) 468 fh.write(loopFoot+"\n") 469 470 fh.write(footer+"\n") 471 os.chmod(os.path.join(runscript_dir,testname+".sh"),0755) 472 #if '10_9' in testname: sys.exit() 473 return 474 475 def genScriptsAndInfo(self,exfile,root,srcDict): 476 """ 477 Generate scripts from the source file, determine if built, etc. 478 For every test in the exfile with info in the srcDict: 479 1. Determine if it needs to be run for this arch 480 2. Generate the script 481 3. Generate the data needed to write out the makefile in a 482 convenient way 483 All tests are *always* run, but some may be SKIP'd per the TAP standard 484 """ 485 debug=False 486 fileIsTested=False 487 execname=self.getExecname(exfile,root) 488 isBuilt=self._isBuilt(exfile,srcDict) 489 for test in srcDict: 490 if test in self.buildkeys: continue 491 if debug: print self.nameSpace(exfile,root), test 492 srcDict[test]['execname']=execname # Convenience in generating scripts 493 isRun=self._isRun(srcDict[test]) 494 self.genRunScript(test,root,isRun,srcDict) 495 srcDict[test]['isrun']=isRun 496 if isRun: fileIsTested=True 497 self.addToTests(test,root,exfile,execname,srcDict[test]) 498 499 # This adds to datastructure for building deps 500 if fileIsTested and isBuilt: self.addToSources(exfile,root,srcDict) 501 #print self.nameSpace(exfile,root), fileIsTested 502 return 503 504 def _isBuilt(self,exfile,srcDict): 505 """ 506 Determine if this file should be built. 507 """ 508 # Get the language based on file extension 509 srcDict['SKIP'] = [] 510 lang=self.getLanguage(exfile) 511 if (lang=="F" or lang=="F90") and not self.have_fortran: 512 srcDict["SKIP"].append("Fortran required for this test") 513 if lang=="cu" and 'PETSC_HAVE_CUDA' not in self.conf: 514 srcDict["SKIP"].append("CUDA required for this test") 515 if lang=="cxx" and 'PETSC_HAVE_CXX' not in self.conf: 516 srcDict["SKIP"].append("C++ required for this test") 517 518 # Deprecated source files 519 if srcDict.get("TODO"): 520 return False 521 522 # isRun can work with srcDict to handle the requires 523 if "requires" in srcDict: 524 if len(srcDict["requires"])>0: 525 return self._isRun(srcDict) 526 527 return srcDict['SKIP'] == [] 528 529 530 def _isRun(self,testDict): 531 """ 532 Based on the requirements listed in the src file and the petscconf.h 533 info, determine whether this test should be run or not. 534 """ 535 indent=" " 536 debug=False 537 538 if 'SKIP' not in testDict: 539 testDict['SKIP'] = [] 540 # MPI requirements 541 if testDict.get('nsize',1)>1 and 'MPI_IS_MPIUNI' in self.conf: 542 if debug: print indent+"Cannot run parallel tests" 543 testDict['SKIP'].append("Parallel test with serial build") 544 545 # The requirements for the test are the sum of all the run subtests 546 if 'subtests' in testDict: 547 if 'requires' not in testDict: testDict['requires']="" 548 for stest in testDict['subtests']: 549 if 'requires' in testDict[stest]: 550 testDict['requires']+=" "+testDict[stest]['requires'] 551 552 553 # Now go through all requirements 554 if 'requires' in testDict: 555 for requirement in testDict['requires'].split(): 556 requirement=requirement.strip() 557 if not requirement: continue 558 if debug: print indent+"Requirement: ", requirement 559 isNull=False 560 if requirement.startswith("!"): 561 requirement=requirement[1:]; isNull=True 562 # Precision requirement for reals 563 if requirement in self.precision_types: 564 if self.conf['PETSC_PRECISION']==requirement: 565 if isNull: 566 testDict['SKIP'].append("not "+requirement+" required") 567 continue 568 continue # Success 569 elif not isNull: 570 testDict['SKIP'].append(requirement+" required") 571 continue 572 # Precision requirement for ints 573 if requirement in self.integer_types: 574 if requirement=="int32": 575 if self.conf['PETSC_SIZEOF_INT']==4: 576 if isNull: 577 testDict['SKIP'].append("not int32 required") 578 continue 579 continue # Success 580 elif not isNull: 581 testDict['SKIP'].append("int32 required") 582 continue 583 if requirement=="int64": 584 if self.conf['PETSC_SIZEOF_INT']==8: 585 if isNull: 586 testDict['SKIP'].append("NOT int64 required") 587 continue 588 continue # Success 589 elif not isNull: 590 testDict['SKIP'].append("int64 required") 591 continue 592 # Datafilespath 593 if requirement=="datafilespath" and not isNull: 594 testDict['SKIP'].append("Requires DATAFILESPATH") 595 continue 596 # Defines -- not sure I have comments matching 597 if "define(" in requirement.lower(): 598 reqdef=requirement.split("(")[1].split(")")[0] 599 if reqdef in self.conf: 600 if isNull: 601 testDict['SKIP'].append("Null requirement not met: "+requirement) 602 continue 603 continue # Success 604 elif not isNull: 605 testDict['SKIP'].append("Required: "+requirement) 606 continue 607 608 # Rest should be packages that we can just get from conf 609 if requirement == "complex": 610 petscconfvar="PETSC_USE_COMPLEX" 611 else: 612 petscconfvar="PETSC_HAVE_"+requirement.upper() 613 if self.conf.get(petscconfvar): 614 if isNull: 615 testDict['SKIP'].append("Not "+petscconfvar+" requirement not met") 616 continue 617 continue # Success 618 elif not isNull: 619 if debug: print "requirement not found: ", requirement 620 testDict['SKIP'].append(petscconfvar+" requirement not met") 621 continue 622 623 return testDict['SKIP'] == [] 624 625 def genPetscTests_summarize(self,dataDict): 626 """ 627 Required method to state what happened 628 """ 629 if not self.summarize: return 630 indent=" " 631 fhname=os.path.join(self.testroot_dir,'GenPetscTests_summarize.txt') 632 fh=open(fhname,"w") 633 #print "See ", fhname 634 for root in dataDict: 635 relroot=self.relpath(self.petsc_dir,root) 636 pkg=relroot.split("/")[1] 637 fh.write(relroot+"\n") 638 allSrcs=[] 639 for lang in LANGS: allSrcs=allSrcs+self.sources[pkg][lang]['srcs'] 640 for exfile in dataDict[root]: 641 # Basic information 642 fullfile=os.path.join(root,exfile) 643 rfile=self.relpath(self.petsc_dir,fullfile) 644 builtStatus=(" Is built" if rfile in allSrcs else " Is NOT built") 645 fh.write(indent+exfile+indent*4+builtStatus+"\n") 646 647 for test in dataDict[root][exfile]: 648 if test in self.buildkeys: continue 649 line=indent*2+test 650 fh.write(line+"\n") 651 # Looks nice to have the keys in order 652 #for key in dataDict[root][exfile][test]: 653 for key in "isrun abstracted nsize args requires script".split(): 654 if key not in dataDict[root][exfile][test]: continue 655 line=indent*3+key+": "+str(dataDict[root][exfile][test][key]) 656 fh.write(line+"\n") 657 fh.write("\n") 658 fh.write("\n") 659 fh.write("\n") 660 #fh.write("\nClass Sources\n"+str(self.sources)+"\n") 661 #fh.write("\nClass Tests\n"+str(self.tests)+"\n") 662 fh.close() 663 return 664 665 def genPetscTests(self,root,dirs,files,dataDict): 666 """ 667 Go through and parse the source files in the directory to generate 668 the examples based on the metadata contained in the source files 669 """ 670 debug=False 671 # Use examplesAnalyze to get what the makefles think are sources 672 #self.examplesAnalyze(root,dirs,files,anlzDict) 673 674 dataDict[root]={} 675 676 for exfile in files: 677 #TST: Until we replace files, still leaving the orginals as is 678 #if not exfile.startswith("new_"+"ex"): continue 679 if not exfile.startswith("ex"): continue 680 681 # Convenience 682 fullex=os.path.join(root,exfile) 683 relpfile=self.relpath(self.petsc_dir,fullex) 684 if debug: print relpfile 685 dataDict[root].update(testparse.parseTestFile(fullex,0)) 686 # Need to check and make sure tests are in the file 687 # if verbosity>=1: print relpfile 688 if exfile in dataDict[root]: 689 self.genScriptsAndInfo(exfile,root,dataDict[root][exfile]) 690 691 return 692 693 def walktree(self,top,action="printFiles"): 694 """ 695 Walk a directory tree, starting from 'top' 696 """ 697 #print "action", action 698 # Goal of action is to fill this dictionary 699 dataDict={} 700 for root, dirs, files in os.walk(top, topdown=False): 701 if not "examples" in root: continue 702 if not os.path.isfile(os.path.join(root,"makefile")): continue 703 bname=os.path.basename(root.rstrip("/")) 704 if bname=="tests" or bname=="tutorials": 705 eval("self."+action+"(root,dirs,files,dataDict)") 706 if type(top) != types.StringType: 707 raise TypeError("top must be a string") 708 # Now summarize this dictionary 709 eval("self."+action+"_summarize(dataDict)") 710 return dataDict 711 712 def gen_gnumake(self, fd): 713 """ 714 Overwrite of the method in the base PETSc class 715 """ 716 def write(stem, srcs): 717 for lang in LANGS: 718 fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs']))) 719 for pkg in PKGS: 720 srcs = self.gen_pkg(pkg) 721 write('testsrcs-' + pkg, srcs) 722 return self.gendeps 723 724 def gen_pkg(self, pkg): 725 """ 726 Overwrite of the method in the base PETSc class 727 """ 728 return self.sources[pkg] 729 730 def write_gnumake(self,dataDict): 731 """ 732 Write out something similar to files from gmakegen.py 733 734 Test depends on script which also depends on source 735 file, but since I don't have a good way generating 736 acting on a single file (oops) just depend on 737 executable which in turn will depend on src file 738 """ 739 # Different options for how to set up the targets 740 compileExecsFirst=False 741 742 # Open file 743 arch_files = self.arch_path('lib','petsc','conf', 'testfiles') 744 fd = open(arch_files, 'w') 745 746 # Write out the sources 747 gendeps = self.gen_gnumake(fd) 748 749 # Write out the tests and execname targets 750 fd.write("\n#Tests and executables\n") # Delimiter 751 752 for pkg in PKGS: 753 # These grab the ones that are built 754 for lang in LANGS: 755 testdeps=[] 756 for ftest in self.tests[pkg][lang]: 757 test=os.path.basename(ftest) 758 basedir=os.path.dirname(ftest) 759 testdeps.append(self.nameSpace(test,basedir)) 760 fd.write("test-"+pkg+"."+lang+" := "+' '.join(testdeps)+"\n") 761 fd.write('test-%s.%s : $(test-%s.%s)\n' % (pkg, lang, pkg, lang)) 762 763 # test targets 764 for ftest in self.tests[pkg][lang]: 765 test=os.path.basename(ftest) 766 basedir=os.path.dirname(ftest) 767 testdir="${TESTDIR}/"+basedir+"/" 768 nmtest=self.nameSpace(test,basedir) 769 rundir=os.path.join(testdir,test) 770 #print test, nmtest 771 script=test+".sh" 772 773 # Deps 774 exfile=self.tests[pkg][lang][ftest]['exfile'] 775 fullex=os.path.join(self.petsc_dir,exfile) 776 localexec=self.tests[pkg][lang][ftest]['exec'] 777 execname=os.path.join(testdir,localexec) 778 fullscript=os.path.join(testdir,script) 779 tmpfile=os.path.join(testdir,test,test+".tmp") 780 781 # *.counts depends on the script and either executable (will 782 # be run) or the example source file (SKIP or TODO) 783 fd.write('%s.counts : %s %s\n' 784 % (os.path.join('$(TESTDIR)/counts', nmtest), 785 fullscript, 786 execname if exfile in self.sources[pkg][lang]['srcs'] else fullex)) 787 # Now write the args: 788 fd.write(nmtest+"_ARGS := '"+self.tests[pkg][lang][ftest]['argLabel']+"'\n") 789 790 fd.close() 791 return 792 793 def writeHarness(self,output,dataDict): 794 """ 795 This is set up to write out multiple harness even if only gnumake 796 is supported now 797 """ 798 eval("self.write_"+output+"(dataDict)") 799 return 800 801def main(petsc_dir=None, petsc_arch=None, output=None, verbose=False, single_ex=False): 802 if output is None: 803 output = 'gnumake' 804 805 806 pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose, single_ex=single_ex) 807 dataDict=pEx.walktree(os.path.join(pEx.petsc_dir,'src'),action="genPetscTests") 808 pEx.writeHarness(output,dataDict) 809 810if __name__ == '__main__': 811 import optparse 812 parser = optparse.OptionParser() 813 parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False) 814 parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH')) 815 parser.add_option('--output', help='Location to write output file', default=None) 816 parser.add_option('-s', '--single_executable', dest='single_executable', action="store_false", help='Whether there should be single executable per src subdir. Default is false') 817 opts, extra_args = parser.parse_args() 818 if extra_args: 819 import sys 820 sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args)) 821 exit(1) 822 main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose, single_ex=opts.single_executable) 823