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