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