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