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