xref: /petsc/config/gmakegentest.py (revision 4b2e7f16238410da1f2e51bff8dd1a9ccef4b84f)
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 getFor(self,subst,i,j):
154    """
155      Get the for and done lines
156    """
157    forlines=""
158    donlines=""
159    indent="   "
160    nsizeStr=subst['nsize']
161    for loop in re.findall('{{(.*?)}}',subst['nsize']):
162      lindex=string.ascii_lowercase[i]
163      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
164      nsizeStr=re.sub("{{"+loop+"}}","${"+lindex+"}",nsizeStr)
165      donline=indent*j+"done"
166      forlines=forlines+forline+"\n"
167      donlines=donlines+donline+"\n"
168      i=i+1
169      j=j+1
170    subst['nsize']=nsizeStr
171    argStr=subst['args']
172    for loop in re.findall('{{(.*?)}}',subst['args']):
173      lindex=string.ascii_lowercase[i]
174      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
175      argStr=re.sub("{{"+loop+"}}","${"+lindex+"}",argStr)
176      donline=indent*j+"done"
177      forlines=forlines+forline+"\n"
178      donlines=donlines+donline+"\n"
179      i=i+1
180      j=j+1
181    subst['args']=argStr
182
183    # The do lines have reverse order with respect to indentation
184    dl=donlines.rstrip("\n").split("\n")
185    dl.reverse()
186    donlines="\n".join(dl)+"\n"
187
188    return forlines,donlines,i,j
189
190
191  def getExecname(self,exfile,root):
192    """
193      Generate bash script using template found next to this file.
194      This file is read in at constructor time to avoid file I/O
195    """
196    rpath=self.relpath(self.petsc_dir,root)
197    if self.single_ex:
198      execname=rpath.split("/")[1]+"-ex"
199    else:
200      execname=os.path.splitext(exfile)[0]
201    return execname
202
203  def getSubstVars(self,testDict,rpath,testname):
204    """
205      Create a dictionary with all of the variables that get substituted
206      into the template commands found in example_template.py
207      TODO: Cleanup
208    """
209    subst={}
210    # Handle defaults
211    if not testDict.has_key('nsize'): testDict['nsize']=1
212    if not testDict.has_key('filter'): testDict['filter']=""
213    if not testDict.has_key('filter_output'): testDict['filter_output']=""
214    if not testDict.has_key('localrunfiles'): testDict['localrunfiles']=""
215    if not testDict.has_key('args'): testDict['args']=""
216    defroot=(re.sub("run","",testname) if testname.startswith("run") else testname)
217    if not "_" in defroot: defroot=defroot+"_1"
218    if not testDict.has_key('redirect_file'): testDict['redirect_file']=defroot+".tmp"
219    if not testDict.has_key('output_file'): testDict['output_file']="output/"+defroot+".out"
220
221    # Setup the variables in template_string that need to be substituted
222    subst['srcdir']=os.path.join(self.petsc_dir,rpath)
223    subst['label']=self.nameSpace(defroot,subst['srcdir'])
224    subst['redirect_file']=testDict['redirect_file']
225    subst['output_file']=os.path.join(subst['srcdir'],testDict['output_file'])
226    subst['exec']="../"+testDict['execname']
227    subst['filter']="'"+testDict['filter']+"'"   # Quotes are tricky
228    subst['filter_output']=testDict['filter_output']
229    subst['localrunfiles']=testDict['localrunfiles']
230    subst['testroot']=self.testroot_dir
231    subst['testname']=testname
232
233    # Be careful with this
234    if testDict.has_key('command'): subst['command']=testDict['command']
235
236    # These can have for loops and are treated separately later
237    if testDict.has_key('nsize'): subst['nsize']=str(testDict['nsize'])
238    if testDict.has_key('args'):  subst['args']=testDict['args']
239
240    #Conf vars
241    subst['mpiexec']=self.conf['MPIEXEC']  # make sure PETSC_DIR is defined!
242    subst['petsc_dir']=self.petsc_dir # not self.conf['PETSC_DIR'] as this could be windows path
243    subst['diff']=self.conf['DIFF']
244    subst['rm']=self.conf['RM']
245    subst['grep']=self.conf['GREP']
246    subst['petsc_lib_dir']=self.conf['PETSC_LIB_DIR']
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" or lang=="F90") 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=os.path.join(self.testroot_dir,'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):
621    """
622     Overwrite of the method in the base PETSc class
623    """
624    def write(stem, srcs):
625        for lang in LANGS:
626            fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs'])))
627    for pkg in PKGS:
628        srcs = self.gen_pkg(pkg)
629        write('testsrcs-' + pkg, srcs)
630    return self.gendeps
631
632  def gen_pkg(self, pkg):
633    """
634     Overwrite of the method in the base PETSc class
635    """
636    return self.sources[pkg]
637
638  def write_gnumake(self,dataDict):
639    """
640     Write out something similar to files from gmakegen.py
641
642     There is not a lot of has_key type checking because
643     should just work and need to know if there are bugs
644
645     Test depends on script which also depends on source
646     file, but since I don't have a good way generating
647     acting on a single file (oops) just depend on
648     executable which in turn will depend on src file
649    """
650    # Different options for how to set up the targets
651    compileExecsFirst=False
652
653    # Open file
654    arch_files = self.arch_path('lib','petsc','conf', 'testfiles')
655    fd = open(arch_files, 'w')
656
657    # Write out the sources
658    gendeps = self.gen_gnumake(fd)
659
660    # Write out the tests and execname targets
661    fd.write("\n#Tests and executables\n")    # Delimiter
662
663    for pkg in PKGS:
664      # These grab the ones that are built
665      for lang in LANGS:
666        testdeps=[]
667        for ftest in self.tests[pkg][lang]:
668          test=os.path.basename(ftest)
669          basedir=os.path.dirname(ftest)
670          testdeps.append(self.nameSpace(test,basedir))
671        fd.write("test-"+pkg+"."+lang+" := "+' '.join(testdeps)+"\n")
672        fd.write('test-%s.%s : $(test-%s.%s)\n' % (pkg, lang, pkg, lang))
673
674        # test targets
675        for ftest in self.tests[pkg][lang]:
676          test=os.path.basename(ftest)
677          basedir=os.path.dirname(ftest)
678          testdir="${TESTDIR}/"+basedir+"/"
679          nmtest=self.nameSpace(test,basedir)
680          rundir=os.path.join(testdir,test)
681          #print test, nmtest
682          script=test+".sh"
683
684          # Deps
685          exfile=self.tests[pkg][lang][ftest]['exfile']
686          fullex=os.path.join(self.petsc_dir,exfile)
687          localexec=self.tests[pkg][lang][ftest]['exec']
688          execname=os.path.join(testdir,localexec)
689          fullscript=os.path.join(testdir,script)
690          tmpfile=os.path.join(testdir,test,test+".tmp")
691
692          # *.counts depends on the script and either executable (will
693          # be run) or the example source file (SKIP or TODO)
694          fd.write('%s.counts : %s %s\n'
695                   % (os.path.join('$(TESTDIR)/counts', nmtest),
696                      fullscript,
697                      execname if exfile in self.sources[pkg][lang]['srcs'] else fullex))
698          # Now write the args:
699          fd.write(nmtest+"_ARGS := '"+self.tests[pkg][lang][ftest]['argLabel']+"'\n")
700
701    fd.close()
702    return
703
704  def writeHarness(self,output,dataDict):
705    """
706     This is set up to write out multiple harness even if only gnumake
707     is supported now
708    """
709    eval("self.write_"+output+"(dataDict)")
710    return
711
712def main(petsc_dir=None, petsc_arch=None, output=None, verbose=False, single_ex=False):
713    if output is None:
714        output = 'gnumake'
715
716
717    pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose, single_ex=single_ex)
718    dataDict=pEx.walktree(os.path.join(pEx.petsc_dir,'src'),action="genPetscTests")
719    pEx.writeHarness(output,dataDict)
720
721if __name__ == '__main__':
722    import optparse
723    parser = optparse.OptionParser()
724    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
725    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
726    parser.add_option('--output', help='Location to write output file', default=None)
727    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')
728    opts, extra_args = parser.parse_args()
729    if extra_args:
730        import sys
731        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
732        exit(1)
733    main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose, single_ex=opts.single_executable)
734