xref: /petsc/config/gmakegentest.py (revision 862148f4179c86da69cb1c312dcd69cd1fbab188)
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