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