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