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