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 loopVars[key] = keystr[2:-2].split(',') 96 varNames = testDict.copy() 97 for key in loopVars: 98 varNames[key] = '${' + str(key) + '}' 99 return (loopVars,varNames) 100 101 102 def getArgLabel(self,testDict): 103 """ 104 In all of the arguments in the test dictionary, create a simple 105 string for searching within the makefile system. For simplicity in 106 search, remove "-", for strings, etc. 107 Also, concatenate the arg commands 108 For now, ignore nsize -- seems hard to search for anyway 109 """ 110 # Collect all of the args associated with a test 111 argStr=("" if not testDict.has_key('args') else testDict['args']) 112 if testDict.has_key('subtests'): 113 for stest in testDict["subtests"]: 114 sd=testDict[stest] 115 argStr=argStr+("" if not sd.has_key('args') else sd['args']) 116 117 # Now go through and cleanup 118 argStr=re.sub('{{(.*?)}}',"",argStr) 119 argStr=re.sub('-'," ",argStr) 120 for digit in string.digits: argStr=re.sub(digit," ",argStr) 121 argStr=re.sub("\.","",argStr) 122 argStr=re.sub(",","",argStr) 123 argStr=re.sub('\+',' ',argStr) 124 argStr=re.sub(' +',' ',argStr) # Remove repeated white space 125 return argStr.strip() 126 127 def addToSources(self,exfile,root,srcDict): 128 """ 129 Put into data structure that allows easy generation of makefile 130 """ 131 pkg=self.relpath(self.petsc_dir,root).split("/")[1] 132 fullfile=os.path.join(root,exfile) 133 relpfile=self.relpath(self.petsc_dir,fullfile) 134 lang=self.getLanguage(exfile) 135 if not lang: return 136 self.sources[pkg][lang]['srcs'].append(relpfile) 137 if srcDict.has_key('depends'): 138 depSrc=srcDict['depends'] 139 depObj=os.path.splitext(depSrc)[0]+".o" 140 self.sources[pkg][lang][exfile]=depObj 141 142 # In gmakefile, ${TESTDIR} var specifies the object compilation 143 testsdir=self.relpath(self.petsc_dir,root)+"/" 144 objfile="${TESTDIR}/"+testsdir+os.path.splitext(exfile)[0]+".o" 145 self.objects[pkg].append(objfile) 146 return 147 148 def addToTests(self,test,root,exfile,execname,testDict): 149 """ 150 Put into data structure that allows easy generation of makefile 151 Organized by languages to allow testing of languages 152 """ 153 pkg=self.relpath(self.petsc_dir,root).split("/")[1] 154 #nmtest=self.nameSpace(test,root) 155 rpath=self.relpath(self.petsc_dir,root) 156 nmtest=os.path.join(rpath,test) 157 lang=self.getLanguage(exfile) 158 if not lang: return 159 self.tests[pkg][lang][nmtest]={} 160 self.tests[pkg][lang][nmtest]['exfile']=os.path.join(rpath,exfile) 161 self.tests[pkg][lang][nmtest]['exec']=execname 162 self.tests[pkg][lang][nmtest]['argLabel']=self.getArgLabel(testDict) 163 return 164 165 def getFor(self,subst,i,j): 166 """ 167 Get the for and done lines 168 """ 169 forlines="" 170 donlines="" 171 indent=" " 172 nsizeStr=subst['nsize'] 173 for loop in re.findall('{{(.*?)}}',subst['nsize']): 174 lindex=string.ascii_lowercase[i] 175 forline=indent*j+"for "+lindex+" in '"+loop+"'; do" 176 nsizeStr=re.sub("{{"+loop+"}}","${"+lindex+"}",nsizeStr) 177 donline=indent*j+"done" 178 forlines=forlines+forline+"\n" 179 donlines=donlines+donline+"\n" 180 i=i+1 181 j=j+1 182 subst['nsize']=nsizeStr 183 argStr=subst['args'] 184 for loop in re.findall('{{(.*?)}}',subst['args']): 185 lindex=string.ascii_lowercase[i] 186 forline=indent*j+"for "+lindex+" in '"+loop+"'; do" 187 argStr=re.sub("{{"+loop+"}}","${"+lindex+"}",argStr) 188 donline=indent*j+"done" 189 forlines=forlines+forline+"\n" 190 donlines=donlines+donline+"\n" 191 i=i+1 192 j=j+1 193 subst['args']=argStr 194 195 # The do lines have reverse order with respect to indentation 196 dl=donlines.rstrip("\n").split("\n") 197 dl.reverse() 198 donlines="\n".join(dl)+"\n" 199 200 return forlines,donlines,i,j 201 202 203 def getExecname(self,exfile,root): 204 """ 205 Generate bash script using template found next to this file. 206 This file is read in at constructor time to avoid file I/O 207 """ 208 rpath=self.relpath(self.petsc_dir,root) 209 if self.single_ex: 210 execname=rpath.split("/")[1]+"-ex" 211 else: 212 execname=os.path.splitext(exfile)[0] 213 return execname 214 215 def getSubstVars(self,testDict,rpath,testname): 216 """ 217 Create a dictionary with all of the variables that get substituted 218 into the template commands found in example_template.py 219 TODO: Cleanup 220 """ 221 subst={} 222 # Handle defaults 223 if not testDict.has_key('nsize'): testDict['nsize']=1 224 if not testDict.has_key('filter'): testDict['filter']="" 225 if not testDict.has_key('filter_output'): testDict['filter_output']="" 226 if not testDict.has_key('localrunfiles'): testDict['localrunfiles']="" 227 if not testDict.has_key('args'): testDict['args']="" 228 defroot=(re.sub("run","",testname) if testname.startswith("run") else testname) 229 if not "_" in defroot: defroot=defroot+"_1" 230 if not testDict.has_key('redirect_file'): testDict['redirect_file']=defroot+".tmp" 231 if not testDict.has_key('output_file'): testDict['output_file']="output/"+defroot+".out" 232 233 # Setup the variables in template_string that need to be substituted 234 subst['srcdir']=os.path.join(self.petsc_dir,rpath) 235 subst['label']=self.nameSpace(defroot,subst['srcdir']) 236 subst['redirect_file']=testDict['redirect_file'] 237 subst['output_file']=os.path.join(subst['srcdir'],testDict['output_file']) 238 subst['exec']="../"+testDict['execname'] 239 subst['filter']="'"+testDict['filter']+"'" # Quotes are tricky 240 subst['filter_output']=testDict['filter_output'] 241 subst['localrunfiles']=testDict['localrunfiles'] 242 subst['testroot']=self.testroot_dir 243 subst['testname']=testname 244 245 # Be careful with this 246 if testDict.has_key('command'): subst['command']=testDict['command'] 247 248 # These can have for loops and are treated separately later 249 if testDict.has_key('nsize'): subst['nsize']=str(testDict['nsize']) 250 if testDict.has_key('args'): subst['args']=testDict['args'] 251 252 #Conf vars 253 if self.petsc_arch.find('valgrind')>=0: 254 subst['mpiexec']='petsc_mpiexec_valgrind ' + self.conf['MPIEXEC'] 255 else: 256 subst['mpiexec']=self.conf['MPIEXEC'] 257 subst['petsc_dir']=self.petsc_dir # not self.conf['PETSC_DIR'] as this could be windows path 258 subst['diff']=self.conf['DIFF'] 259 subst['rm']=self.conf['RM'] 260 subst['grep']=self.conf['GREP'] 261 subst['petsc_lib_dir']=self.conf['PETSC_LIB_DIR'] 262 subst['wpetsc_dir']=self.conf['wPETSC_DIR'] 263 264 for key in subst: 265 if type(subst[key])!=types.StringType: continue 266 subst[key] = self._substVars(testDict,subst[key]) 267 268 return subst 269 270 def getCmds(self,subst,i,subtest=False): 271 """ 272 Generate bash script using template found next to this file. 273 This file is read in at constructor time to avoid file I/O 274 """ 275 indent=" " 276 nindent=i # the start and has to be consistent with below 277 cmdLines="" 278 # MPI is the default -- but we have a few odd commands 279 if not subst.has_key('command'): 280 if not subtest: 281 cmd=indent*nindent+self._substVars(subst,example_template.mpitest) 282 else: 283 cmd=indent*nindent+self._substVars(subst,example_template.mpisubtest) 284 else: 285 cmd=indent*nindent+self._substVars(subst,example_template.commandtest) 286 cmdLines=cmdLines+cmd+"\n\n" 287 288 if not subst['filter_output']: 289 cmd=indent*nindent+self._substVars(subst,example_template.difftest) 290 else: 291 cmd=indent*nindent+self._substVars(subst,example_template.filterdifftest) 292 cmdLines=cmdLines+cmd+"\n" 293 return cmdLines 294 295 def _substVars(self,subst,origStr): 296 """ 297 Substitute varial 298 """ 299 Str=origStr 300 for subkey in subst: 301 if type(subst[subkey])!=types.StringType: continue 302 patt="@"+subkey.upper()+"@" 303 Str=re.sub(patt,subst[subkey],Str) 304 return Str 305 306 def getLoopVarsHead(self,loopVars,i): 307 outstr='' 308 indent=" " 309 for key in loopVars: 310 newstr = indent * i + "for ${{{0}}} in '{1}'; do\n".format(key,','.join(loopVars[key])) 311 outstr = outstr + newstr 312 i = i + 1 313 return (outstr,i) 314 315 def getLoopVarsFoot(self,loopVars,i): 316 outstr='' 317 indent=" " 318 for key in loopVars: 319 i = i - 1 320 newstr = indent * i + "done\n" 321 outstr = outstr + newstr 322 return (outstr,i) 323 324 325 def genRunScript(self,testname,root,isRun,srcDict): 326 """ 327 Generate bash script using template found next to this file. 328 This file is read in at constructor time to avoid file I/O 329 """ 330 # runscript_dir directory has to be consistent with gmakefile 331 testDict=srcDict[testname] 332 rpath=self.relpath(self.petsc_dir,root) 333 runscript_dir=os.path.join(self.testroot_dir,rpath) 334 if not os.path.isdir(runscript_dir): os.makedirs(runscript_dir) 335 fh=open(os.path.join(runscript_dir,testname+".sh"),"w") 336 petscvarfile=os.path.join(self.arch_dir,'lib','petsc','conf','petscvariables') 337 338 (loopVars,varNames) = self._getLoopVars(testDict) 339 340 subst=self.getSubstVars(varNames,rpath,testname) 341 if (loopVars): print subst 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