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