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