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