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