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