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