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