xref: /petsc/config/gmakegentest.py (revision 94ef8dde638caef1d0cd84a7dc8a2db65fcda8b6)
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):
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      cmd=indent*nindent+self._substVars(subst,example_template.mpitest)
265    else:
266      cmd=indent*nindent+self._substVars(subst,example_template.commandtest)
267    cmdLines=cmdLines+cmd+"\n\n"
268
269    if not subst['filter_output']:
270      cmd=indent*nindent+self._substVars(subst,example_template.difftest)
271    else:
272      cmd=indent*nindent+self._substVars(subst,example_template.filterdifftest)
273    cmdLines=cmdLines+cmd+"\n"
274    return cmdLines
275
276  def _substVars(self,subst,origStr):
277    """
278      Substitute varial
279    """
280    Str=origStr
281    for subkey in subst:
282      if type(subst[subkey])!=types.StringType: continue
283      patt="@"+subkey.upper()+"@"
284      Str=re.sub(patt,subst[subkey],Str)
285    return Str
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    subst=self.getSubstVars(testDict,rpath,testname)
301
302    #Handle runfiles
303    if subst['localrunfiles']:
304      for lfile in subst['localrunfiles'].split():
305        fullfile=os.path.join(self.petsc_dir,rpath,lfile)
306        shutil.copy(fullfile,runscript_dir)
307    # Check subtests for local runfiles
308    if testDict.has_key("subtests"):
309      for stest in testDict["subtests"]:
310        if testDict[stest].has_key('localrunfiles'):
311          for lfile in testDict[stest]['localrunfiles'].split():
312            fullfile=os.path.join(self.petsc_dir,rpath,lfile)
313            shutil.copy(fullfile,self.runscript_dir)
314
315    # Now substitute the key variables into the header and footer
316    header=self._substVars(subst,example_template.header)
317    footer=re.sub('@TESTROOT@',subst['testroot'],example_template.footer)
318
319    # Start writing the file
320    fh.write(header+"\n")
321
322    # If there is a TODO or a SKIP then we do it before writing out the
323    # rest of the command (which is useful for working on the test)
324    # SKIP and TODO can be for the source file or for the runs
325    if srcDict.has_key("SKIP") or srcDict.has_key("TODO"):
326      if srcDict.has_key("TODO"):
327        todo=re.sub("@TODOCOMMENT@",srcDict['TODO'],example_template.todoline)
328        fh.write(todo+"\ntotal=1; todo=1\n")
329        fh.write(footer+"\n")
330        fh.write("exit\n\n\n")
331      elif srcDict.has_key("SKIP") or srcDict.has_key("TODO"):
332        skip=re.sub("@SKIPCOMMENT@",srcDict['SKIP'],example_template.skipline)
333        fh.write(skip+"\ntotal=1; skip=1\n")
334        fh.write(footer+"\n")
335        fh.write("exit\n\n\n")
336    elif not isRun:
337      skip=re.sub("@SKIPCOMMENT@",testDict['SKIP'],example_template.skipline)
338      fh.write(skip+"\ntotal=1; skip=1\n")
339      fh.write(footer+"\n")
340      fh.write("exit\n\n\n")
341    elif testDict.has_key('TODO'):
342      todo=re.sub("@TODOCOMMENT@",testDict['TODO'],example_template.todoline)
343      fh.write(todo+"\ntotal=1; todo=1\n")
344      fh.write(footer+"\n")
345      fh.write("exit\n\n\n")
346
347    # Need to handle loops
348    i=8  # for loop counters
349    j=0  # for indentation
350
351    doForP=False
352    if "{{" in subst['args']+subst['nsize']:
353      doForP=True
354      flinesP,dlinesP,i,j=self.getFor(subst,i,j)
355      fh.write(flinesP+"\n")
356
357    # Subtests are special
358    if testDict.has_key("subtests"):
359      substP=subst   # Subtests can inherit args but be careful
360      if not substP.has_key("arg"): substP["arg"]=""
361      jorig=j
362      for stest in testDict["subtests"]:
363        j=jorig
364        subst=substP
365        subst.update(testDict[stest])
366        subst['nsize']=str(subst['nsize'])
367        if not testDict[stest].has_key('args'): testDict[stest]['args']=""
368        subst['args']=substP['args']+testDict[stest]['args']
369        doFor=False
370        if "{{" in subst['args']+subst['nsize']:
371          doFor=True
372          flines,dlines,i,j=self.getFor(subst,i,j)
373          fh.write(flines+"\n")
374        fh.write(self.getCmds(subst,j)+"\n")
375        if doFor: fh.write(dlines+"\n")
376    else:
377      fh.write(self.getCmds(subst,j)+"\n")
378      if doForP: fh.write(dlinesP+"\n")
379
380    fh.write(footer+"\n")
381    os.chmod(os.path.join(runscript_dir,testname+".sh"),0755)
382    return
383
384  def  genScriptsAndInfo(self,exfile,root,srcDict):
385    """
386    Generate scripts from the source file, determine if built, etc.
387     For every test in the exfile with info in the srcDict:
388      1. Determine if it needs to be run for this arch
389      2. Generate the script
390      3. Generate the data needed to write out the makefile in a
391         convenient way
392     All tests are *always* run, but some may be SKIP'd per the TAP standard
393    """
394    debug=False
395    fileIsTested=False
396    execname=self.getExecname(exfile,root)
397    isBuilt=self._isBuilt(exfile,srcDict)
398    for test in srcDict:
399      if test in self.buildkeys: continue
400      if debug: print self.nameSpace(exfile,root), test
401      srcDict[test]['execname']=execname   # Convenience in generating scripts
402      isRun=self._isRun(srcDict[test])
403      self.genRunScript(test,root,isRun,srcDict)
404      srcDict[test]['isrun']=isRun
405      if isRun: fileIsTested=True
406      self.addToTests(test,root,exfile,execname,srcDict[test])
407
408    # This adds to datastructure for building deps
409    if fileIsTested and isBuilt: self.addToSources(exfile,root,srcDict)
410    #print self.nameSpace(exfile,root), fileIsTested
411    return
412
413  def _isBuilt(self,exfile,srcDict):
414    """
415    Determine if this file should be built.
416    """
417    # Get the language based on file extension
418    lang=self.getLanguage(exfile)
419    if (lang=="F" or lang=="F90") and not self.have_fortran:
420      srcDict["SKIP"]="Fortran required for this test"
421      return False
422    if lang=="cu" and not self.conf.has_key('PETSC_HAVE_CUDA'):
423      srcDict["SKIP"]="CUDA required for this test"
424      return False
425    if lang=="cxx" and not self.conf.has_key('PETSC_HAVE_CXX'):
426      srcDict["SKIP"]="C++ required for this test"
427      return False
428
429    # Deprecated source files
430    if srcDict.has_key("TODO"): return False
431
432    # isRun can work with srcDict to handle the requires
433    if srcDict.has_key("requires"):
434      if len(srcDict["requires"])>0:
435        return self._isRun(srcDict)
436
437    return True
438
439
440  def _isRun(self,testDict):
441    """
442    Based on the requirements listed in the src file and the petscconf.h
443    info, determine whether this test should be run or not.
444    """
445    indent="  "
446    debug=False
447
448    # MPI requirements
449    if testDict.has_key('nsize'):
450      if testDict['nsize']>1 and self.conf.has_key('MPI_IS_MPIUNI'):
451        if debug: print indent+"Cannot run parallel tests"
452        testDict['SKIP']="Parallel test with serial build"
453        return False
454
455    # The requirements for the test are the sum of all the run subtests
456    if testDict.has_key('subtests'):
457      if not testDict.has_key('requires'): testDict['requires']=""
458      for stest in testDict['subtests']:
459        if testDict[stest].has_key('requires'):
460          testDict['requires']=testDict['requires']+" "+testDict[stest]['requires']
461
462
463    # Now go through all requirements
464    if testDict.has_key('requires'):
465      for requirement in testDict['requires'].split():
466        requirement=requirement.strip()
467        if not requirement: continue
468        if debug: print indent+"Requirement: ", requirement
469        isNull=False
470        if requirement.startswith("!"):
471          requirement=requirement[1:]; isNull=True
472        # Precision requirement for reals
473        if requirement in self.precision_types:
474          if self.conf['PETSC_PRECISION']==requirement:
475            testDict['SKIP']="not "+requirement+" required"
476            if isNull: return False
477          else:
478            testDict['SKIP']=requirement+" required"
479            return False
480        # Precision requirement for ints
481        if requirement in self.integer_types:
482          if requirement=="int32":
483            if self.conf['PETSC_SIZEOF_INT']==4:
484              testDict['SKIP']="not int32 required"
485              if isNull: return False
486            else:
487              testDict['SKIP']="int32 required"
488              return False
489          if requirement=="int64":
490            if self.conf['PETSC_SIZEOF_INT']==8:
491              testDict['SKIP']="NOT int64 required"
492              if isNull: return False
493            else:
494              testDict['SKIP']="int64 required"
495              return False
496        # Datafilespath
497        if requirement=="datafilespath":
498          testDict['SKIP']="Requires DATAFILESPATH"
499          return False
500        # Defines -- not sure I have comments matching
501        if "define(" in requirement.lower():
502          reqdef=requirement.split("(")[1].split(")")[0]
503          val=(reqdef.split()[1] if " " in reqdef else "")
504          if self.conf.has_key(reqdef):
505            if val:
506              if self.conf[reqdef]==val:
507                if isNull:
508                  testDict['SKIP']="Null requirement not met: "+requirement
509                  return False
510              else:
511                testDict['SKIP']="Required: "+requirement
512                return False
513            else:
514              if isNull:
515                testDict['SKIP']="Null requirement not met: "+requirement
516                return False
517              else:
518                return True
519          else:
520            testDict['SKIP']="Requirement not met: "+requirement
521            return False
522
523        # Rest should be packages that we can just get from conf
524        if requirement == "complex":  petscconfvar="PETSC_USE_COMPLEX"
525        else:   petscconfvar="PETSC_HAVE_"+requirement.upper()
526        if self.conf.get(petscconfvar):
527          if isNull:
528            testDict['SKIP']="Not "+petscconfvar+" requirement not met"
529            return False
530        elif not isNull:
531          if debug: print "requirement not found: ", requirement
532          testDict['SKIP']=petscconfvar+" requirement not met"
533          return False
534
535    return True
536
537  def genPetscTests_summarize(self,dataDict):
538    """
539    Required method to state what happened
540    """
541    if not self.summarize: return
542    indent="   "
543    fhname=os.path.join(self.testroot_dir,'GenPetscTests_summarize.txt')
544    fh=open(fhname,"w")
545    #print "See ", fhname
546    for root in dataDict:
547      relroot=self.relpath(self.petsc_dir,root)
548      pkg=relroot.split("/")[1]
549      fh.write(relroot+"\n")
550      allSrcs=[]
551      for lang in LANGS: allSrcs=allSrcs+self.sources[pkg][lang]['srcs']
552      for exfile in dataDict[root]:
553        # Basic  information
554        fullfile=os.path.join(root,exfile)
555        rfile=self.relpath(self.petsc_dir,fullfile)
556        builtStatus=(" Is built" if rfile in allSrcs else " Is NOT built")
557        fh.write(indent+exfile+indent*4+builtStatus+"\n")
558
559        for test in dataDict[root][exfile]:
560          if test in self.buildkeys: continue
561          line=indent*2+test
562          fh.write(line+"\n")
563          # Looks nice to have the keys in order
564          #for key in dataDict[root][exfile][test]:
565          for key in "isrun abstracted nsize args requires script".split():
566            if not dataDict[root][exfile][test].has_key(key): continue
567            line=indent*3+key+": "+str(dataDict[root][exfile][test][key])
568            fh.write(line+"\n")
569          fh.write("\n")
570        fh.write("\n")
571      fh.write("\n")
572    #fh.write("\nClass Sources\n"+str(self.sources)+"\n")
573    #fh.write("\nClass Tests\n"+str(self.tests)+"\n")
574    fh.close()
575    return
576
577  def genPetscTests(self,root,dirs,files,dataDict):
578    """
579     Go through and parse the source files in the directory to generate
580     the examples based on the metadata contained in the source files
581    """
582    debug=False
583    # Use examplesAnalyze to get what the makefles think are sources
584    #self.examplesAnalyze(root,dirs,files,anlzDict)
585
586    dataDict[root]={}
587
588    for exfile in files:
589      #TST: Until we replace files, still leaving the orginals as is
590      #if not exfile.startswith("new_"+"ex"): continue
591      if not exfile.startswith("ex"): continue
592
593      # Convenience
594      fullex=os.path.join(root,exfile)
595      relpfile=self.relpath(self.petsc_dir,fullex)
596      if debug: print relpfile
597      dataDict[root].update(testparse.parseTestFile(fullex))
598      # Need to check and make sure tests are in the file
599      # if verbosity>=1: print relpfile
600      if dataDict[root].has_key(exfile):
601        self.genScriptsAndInfo(exfile,root,dataDict[root][exfile])
602
603    return
604
605  def walktree(self,top,action="printFiles"):
606    """
607    Walk a directory tree, starting from 'top'
608    """
609    #print "action", action
610    # Goal of action is to fill this dictionary
611    dataDict={}
612    for root, dirs, files in os.walk(top, topdown=False):
613      if not "examples" in root: continue
614      if not os.path.isfile(os.path.join(root,"makefile")): continue
615      bname=os.path.basename(root.rstrip("/"))
616      if bname=="tests" or bname=="tutorials":
617        eval("self."+action+"(root,dirs,files,dataDict)")
618      if type(top) != types.StringType:
619          raise TypeError("top must be a string")
620    # Now summarize this dictionary
621    eval("self."+action+"_summarize(dataDict)")
622    return dataDict
623
624  def gen_gnumake(self, fd):
625    """
626     Overwrite of the method in the base PETSc class
627    """
628    def write(stem, srcs):
629        for lang in LANGS:
630            fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs'])))
631    for pkg in PKGS:
632        srcs = self.gen_pkg(pkg)
633        write('testsrcs-' + pkg, srcs)
634    return self.gendeps
635
636  def gen_pkg(self, pkg):
637    """
638     Overwrite of the method in the base PETSc class
639    """
640    return self.sources[pkg]
641
642  def write_gnumake(self,dataDict):
643    """
644     Write out something similar to files from gmakegen.py
645
646     There is not a lot of has_key type checking because
647     should just work and need to know if there are bugs
648
649     Test depends on script which also depends on source
650     file, but since I don't have a good way generating
651     acting on a single file (oops) just depend on
652     executable which in turn will depend on src file
653    """
654    # Different options for how to set up the targets
655    compileExecsFirst=False
656
657    # Open file
658    arch_files = self.arch_path('lib','petsc','conf', 'testfiles')
659    fd = open(arch_files, 'w')
660
661    # Write out the sources
662    gendeps = self.gen_gnumake(fd)
663
664    # Write out the tests and execname targets
665    fd.write("\n#Tests and executables\n")    # Delimiter
666
667    for pkg in PKGS:
668      # These grab the ones that are built
669      for lang in LANGS:
670        testdeps=[]
671        for ftest in self.tests[pkg][lang]:
672          test=os.path.basename(ftest)
673          basedir=os.path.dirname(ftest)
674          testdeps.append(self.nameSpace(test,basedir))
675        fd.write("test-"+pkg+"."+lang+" := "+' '.join(testdeps)+"\n")
676        fd.write('test-%s.%s : $(test-%s.%s)\n' % (pkg, lang, pkg, lang))
677
678        # test targets
679        for ftest in self.tests[pkg][lang]:
680          test=os.path.basename(ftest)
681          basedir=os.path.dirname(ftest)
682          testdir="${TESTDIR}/"+basedir+"/"
683          nmtest=self.nameSpace(test,basedir)
684          rundir=os.path.join(testdir,test)
685          #print test, nmtest
686          script=test+".sh"
687
688          # Deps
689          exfile=self.tests[pkg][lang][ftest]['exfile']
690          fullex=os.path.join(self.petsc_dir,exfile)
691          localexec=self.tests[pkg][lang][ftest]['exec']
692          execname=os.path.join(testdir,localexec)
693          fullscript=os.path.join(testdir,script)
694          tmpfile=os.path.join(testdir,test,test+".tmp")
695
696          # *.counts depends on the script and either executable (will
697          # be run) or the example source file (SKIP or TODO)
698          fd.write('%s.counts : %s %s\n'
699                   % (os.path.join('$(TESTDIR)/counts', nmtest),
700                      fullscript,
701                      execname if exfile in self.sources[pkg][lang]['srcs'] else fullex))
702          # Now write the args:
703          fd.write(nmtest+"_ARGS := '"+self.tests[pkg][lang][ftest]['argLabel']+"'\n")
704
705    fd.close()
706    return
707
708  def writeHarness(self,output,dataDict):
709    """
710     This is set up to write out multiple harness even if only gnumake
711     is supported now
712    """
713    eval("self.write_"+output+"(dataDict)")
714    return
715
716def main(petsc_dir=None, petsc_arch=None, output=None, verbose=False, single_ex=False):
717    if output is None:
718        output = 'gnumake'
719
720
721    pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose, single_ex=single_ex)
722    dataDict=pEx.walktree(os.path.join(pEx.petsc_dir,'src'),action="genPetscTests")
723    pEx.writeHarness(output,dataDict)
724
725if __name__ == '__main__':
726    import optparse
727    parser = optparse.OptionParser()
728    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
729    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
730    parser.add_option('--output', help='Location to write output file', default=None)
731    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')
732    opts, extra_args = parser.parse_args()
733    if extra_args:
734        import sys
735        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
736        exit(1)
737    main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose, single_ex=opts.single_executable)
738