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