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