xref: /petsc/config/gmakegentest.py (revision ec8ba0ce9cd4e606806ac470a00e817c85e3ff41)
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        import csv
96        loopVars[key] = [r for r in csv.reader([keystr[2:-2]],skipinitialspace=True)][0]
97    varNames = testDict.copy()
98    for key in loopVars:
99      varNames[key] = '${' + str(key) + '}'
100    return (loopVars,varNames)
101
102
103  def getArgLabel(self,testDict):
104    """
105    In all of the arguments in the test dictionary, create a simple
106    string for searching within the makefile system.  For simplicity in
107    search, remove "-", for strings, etc.
108    Also, concatenate the arg commands
109    For now, ignore nsize -- seems hard to search for anyway
110    """
111    # Collect all of the args associated with a test
112    argStr=("" if not testDict.has_key('args') else testDict['args'])
113    if testDict.has_key('subtests'):
114      for stest in testDict["subtests"]:
115         sd=testDict[stest]
116         argStr=argStr+("" if not sd.has_key('args') else sd['args'])
117
118    # Now go through and cleanup
119    argStr=re.sub('{{(.*?)}}',"",argStr)
120    argStr=re.sub('-'," ",argStr)
121    for digit in string.digits: argStr=re.sub(digit," ",argStr)
122    argStr=re.sub("\.","",argStr)
123    argStr=re.sub(",","",argStr)
124    argStr=re.sub('\+',' ',argStr)
125    argStr=re.sub(' +',' ',argStr)  # Remove repeated white space
126    return argStr.strip()
127
128  def addToSources(self,exfile,root,srcDict):
129    """
130      Put into data structure that allows easy generation of makefile
131    """
132    pkg=self.relpath(self.petsc_dir,root).split("/")[1]
133    fullfile=os.path.join(root,exfile)
134    relpfile=self.relpath(self.petsc_dir,fullfile)
135    lang=self.getLanguage(exfile)
136    if not lang: return
137    self.sources[pkg][lang]['srcs'].append(relpfile)
138    if srcDict.has_key('depends'):
139      depSrc=srcDict['depends']
140      depObj=os.path.splitext(depSrc)[0]+".o"
141      self.sources[pkg][lang][exfile]=depObj
142
143    # In gmakefile, ${TESTDIR} var specifies the object compilation
144    testsdir=self.relpath(self.petsc_dir,root)+"/"
145    objfile="${TESTDIR}/"+testsdir+os.path.splitext(exfile)[0]+".o"
146    self.objects[pkg].append(objfile)
147    return
148
149  def addToTests(self,test,root,exfile,execname,testDict):
150    """
151      Put into data structure that allows easy generation of makefile
152      Organized by languages to allow testing of languages
153    """
154    pkg=self.relpath(self.petsc_dir,root).split("/")[1]
155    #nmtest=self.nameSpace(test,root)
156    rpath=self.relpath(self.petsc_dir,root)
157    nmtest=os.path.join(rpath,test)
158    lang=self.getLanguage(exfile)
159    if not lang: return
160    self.tests[pkg][lang][nmtest]={}
161    self.tests[pkg][lang][nmtest]['exfile']=os.path.join(rpath,exfile)
162    self.tests[pkg][lang][nmtest]['exec']=execname
163    self.tests[pkg][lang][nmtest]['argLabel']=self.getArgLabel(testDict)
164    return
165
166  def getFor(self,subst,i,j):
167    """
168      Get the for and done lines
169    """
170    forlines=""
171    donlines=""
172    indent="   "
173    nsizeStr=subst['nsize']
174    for loop in re.findall('{{(.*?)}}',subst['nsize']):
175      lindex=string.ascii_lowercase[i]
176      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
177      nsizeStr=re.sub("{{"+loop+"}}","${"+lindex+"}",nsizeStr)
178      donline=indent*j+"done"
179      forlines=forlines+forline+"\n"
180      donlines=donlines+donline+"\n"
181      i=i+1
182      j=j+1
183    subst['nsize']=nsizeStr
184    argStr=subst['args']
185    for loop in re.findall('{{(.*?)}}',subst['args']):
186      lindex=string.ascii_lowercase[i]
187      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
188      argStr=re.sub("{{"+loop+"}}","${"+lindex+"}",argStr)
189      donline=indent*j+"done"
190      forlines=forlines+forline+"\n"
191      donlines=donlines+donline+"\n"
192      i=i+1
193      j=j+1
194    subst['args']=argStr
195
196    # The do lines have reverse order with respect to indentation
197    dl=donlines.rstrip("\n").split("\n")
198    dl.reverse()
199    donlines="\n".join(dl)+"\n"
200
201    return forlines,donlines,i,j
202
203
204  def getExecname(self,exfile,root):
205    """
206      Generate bash script using template found next to this file.
207      This file is read in at constructor time to avoid file I/O
208    """
209    rpath=self.relpath(self.petsc_dir,root)
210    if self.single_ex:
211      execname=rpath.split("/")[1]+"-ex"
212    else:
213      execname=os.path.splitext(exfile)[0]
214    return execname
215
216  def getSubstVars(self,testDict,rpath,testname):
217    """
218      Create a dictionary with all of the variables that get substituted
219      into the template commands found in example_template.py
220      TODO: Cleanup
221    """
222    subst={}
223    # Handle defaults
224    if not testDict.has_key('nsize'): testDict['nsize']=1
225    if not testDict.has_key('filter'): testDict['filter']=""
226    if not testDict.has_key('filter_output'): testDict['filter_output']=""
227    if not testDict.has_key('localrunfiles'): testDict['localrunfiles']=""
228    if not testDict.has_key('args'): testDict['args']=""
229    defroot=(re.sub("run","",testname) if testname.startswith("run") else testname)
230    if not "_" in defroot: defroot=defroot+"_1"
231    if not testDict.has_key('redirect_file'): testDict['redirect_file']=defroot+".tmp"
232    if not testDict.has_key('output_file'): testDict['output_file']="output/"+defroot+".out"
233
234    # Setup the variables in template_string that need to be substituted
235    subst['srcdir']=os.path.join(self.petsc_dir,rpath)
236    subst['label']=self.nameSpace(defroot,subst['srcdir'])
237    subst['redirect_file']=testDict['redirect_file']
238    subst['output_file']=os.path.join(subst['srcdir'],testDict['output_file'])
239    subst['exec']="../"+testDict['execname']
240    subst['filter']="'"+testDict['filter']+"'"   # Quotes are tricky
241    subst['filter_output']=testDict['filter_output']
242    subst['localrunfiles']=testDict['localrunfiles']
243    subst['testroot']=self.testroot_dir
244    subst['testname']=testname
245
246    # Be careful with this
247    if testDict.has_key('command'): subst['command']=testDict['command']
248
249    # These can have for loops and are treated separately later
250    if testDict.has_key('nsize'): subst['nsize']=str(testDict['nsize'])
251    if testDict.has_key('args'):  subst['args']=testDict['args']
252
253    #Conf vars
254    if self.petsc_arch.find('valgrind')>=0:
255      subst['mpiexec']='petsc_mpiexec_valgrind ' + self.conf['MPIEXEC']
256    else:
257      subst['mpiexec']=self.conf['MPIEXEC']
258    subst['petsc_dir']=self.petsc_dir # not self.conf['PETSC_DIR'] as this could be windows path
259    subst['diff']=self.conf['DIFF']
260    subst['rm']=self.conf['RM']
261    subst['grep']=self.conf['GREP']
262    subst['petsc_lib_dir']=self.conf['PETSC_LIB_DIR']
263    subst['wpetsc_dir']=self.conf['wPETSC_DIR']
264
265    for key in subst:
266      if type(subst[key])!=types.StringType: continue
267      subst[key] = self._substVars(testDict,subst[key])
268
269    return subst
270
271  def getCmds(self,subst,i,subtest=False):
272    """
273      Generate bash script using template found next to this file.
274      This file is read in at constructor time to avoid file I/O
275    """
276    indent="   "
277    nindent=i # the start and has to be consistent with below
278    cmdLines=""
279    # MPI is the default -- but we have a few odd commands
280    if not subst.has_key('command'):
281      if not subtest:
282        cmd=indent*nindent+self._substVars(subst,example_template.mpitest)
283      else:
284        cmd=indent*nindent+self._substVars(subst,example_template.mpisubtest)
285    else:
286      cmd=indent*nindent+self._substVars(subst,example_template.commandtest)
287    cmdLines=cmdLines+cmd+"\n\n"
288
289    if not subst['filter_output']:
290      cmd=indent*nindent+self._substVars(subst,example_template.difftest)
291    else:
292      cmd=indent*nindent+self._substVars(subst,example_template.filterdifftest)
293    cmdLines=cmdLines+cmd+"\n"
294    return cmdLines
295
296  def _substVars(self,subst,origStr):
297    """
298      Substitute varial
299    """
300    Str=origStr
301    for subkey in subst:
302      if type(subst[subkey])!=types.StringType: continue
303      patt="@"+subkey.upper()+"@"
304      Str=re.sub(patt,subst[subkey],Str)
305    return Str
306
307  def getLoopVarsHead(self,loopVars,i):
308    outstr=''
309    indent="   "
310    for key in loopVars:
311      newstr = indent * i + "for {0} in {1}; do\n".format(key,' '.join(['"'+val+'"' for val in loopVars[key]]))
312      outstr = outstr + newstr
313      i = i + 1
314    return (outstr,i)
315
316  def getLoopVarsFoot(self,loopVars,i):
317    outstr=''
318    indent="   "
319    for key in loopVars:
320      i = i - 1
321      newstr = indent * i + "done\n"
322      outstr = outstr + newstr
323    return (outstr,i)
324
325
326  def genRunScript(self,testname,root,isRun,srcDict):
327    """
328      Generate bash script using template found next to this file.
329      This file is read in at constructor time to avoid file I/O
330    """
331    # runscript_dir directory has to be consistent with gmakefile
332    testDict=srcDict[testname]
333    rpath=self.relpath(self.petsc_dir,root)
334    runscript_dir=os.path.join(self.testroot_dir,rpath)
335    if not os.path.isdir(runscript_dir): os.makedirs(runscript_dir)
336    fh=open(os.path.join(runscript_dir,testname+".sh"),"w")
337    petscvarfile=os.path.join(self.arch_dir,'lib','petsc','conf','petscvariables')
338
339    (loopVars,varNames) = self._getLoopVars(testDict)
340
341    subst=self.getSubstVars(varNames,rpath,testname)
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