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