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