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