xref: /petsc/config/gmakegentest.py (revision 64ca018daaeaf4d383e903d9f3c2732a45599de8)
1#!/usr/bin/env python
2
3import os,shutil, string, re
4from distutils.sysconfig import parse_makefile
5import sys
6import logging
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    # Copy petsc tests harness script
57    harness_file=os.path.join(self.petsc_dir,"config","petsc_harness.sh")
58    reports_file=os.path.join(self.petsc_dir,"config","report_tests.sh")
59    self.testroot_dir=os.path.join(self.arch_dir,"tests")
60    if not os.path.isdir(self.testroot_dir): os.makedirs(self.testroot_dir)
61    shutil.copy(harness_file,self.testroot_dir)
62    shutil.copy(reports_file,self.testroot_dir)
63
64    return
65
66  def nameSpace(self,srcfile,srcdir):
67    """
68    Because the scripts have a non-unique naming, the pretty-printing
69    needs to convey the srcdir and srcfile.  There are two ways of doing this.
70    """
71    if self.ptNaming:
72      cdir=srcdir.split('src')[1].lstrip("/").rstrip("/")
73      prefix=cdir.replace('/examples/','_').replace("/","_")+"-"
74      nameString=prefix+srcfile
75    else:
76      #nameString=srcdir+": "+srcfile
77      nameString=srcfile
78    return nameString
79
80  def getLanguage(self,srcfile):
81    """
82    Based on the source, determine associated language as found in gmakegen.LANGS
83    Can we just return srcext[1:\] now?
84    """
85    langReq=None
86    srcext=os.path.splitext(srcfile)[-1]
87    if srcext in ".F90".split(): langReq="F90"
88    if srcext in ".F".split(): langReq="F"
89    if srcext in ".cxx".split(): langReq="cxx"
90    if srcext == ".cu": langReq="cu"
91    if srcext == ".c": langReq="c"
92    if not langReq: print "ERROR: ", srcext, srcfile
93    return langReq
94
95  def getArgLabel(self,testDict):
96    """
97    In all of the arguments in the test dictionary, create a simple
98    string for searching within the makefile system.  For simplicity in
99    search, remove "-", for strings, etc.
100    Also, concatenate the arg commands
101    For now, ignore nsize -- seems hard to search for anyway
102    """
103    # Collect all of the args associated with a test
104    argStr=("" if not testDict.has_key('args') else testDict['args'])
105    if testDict.has_key('subtests'):
106      for stest in testDict["subtests"]:
107         sd=testDict[stest]
108         argStr=argStr+("" if not sd.has_key('args') else sd['args'])
109
110    # Now go through and cleanup
111    argStr=re.sub('{{(.*?)}}',"",argStr)
112    argStr=re.sub('-'," ",argStr)
113    for digit in string.digits: argStr=re.sub(digit," ",argStr)
114    argStr=re.sub("\.","",argStr)
115    argStr=re.sub(",","",argStr)
116    argStr=re.sub('\+',' ',argStr)
117    argStr=re.sub(' +',' ',argStr)  # Remove repeated white space
118    return argStr.strip()
119
120  def addToSources(self,exfile,root,srcDict):
121    """
122      Put into data structure that allows easy generation of makefile
123    """
124    pkg=self.relpath(self.petsc_dir,root).split("/")[1]
125    fullfile=os.path.join(root,exfile)
126    relpfile=self.relpath(self.petsc_dir,fullfile)
127    lang=self.getLanguage(exfile)
128    self.sources[pkg][lang]['srcs'].append(relpfile)
129    if srcDict.has_key('depends'):
130      depSrc=srcDict['depends']
131      depObj=os.path.splitext(depSrc)[0]+".o"
132      self.sources[pkg][lang][exfile]=depObj
133
134    # In gmakefile, ${TESTDIR} var specifies the object compilation
135    testsdir=self.relpath(self.petsc_dir,root)+"/"
136    objfile="${TESTDIR}/"+testsdir+os.path.splitext(exfile)[0]+".o"
137    self.objects[pkg].append(objfile)
138    return
139
140  def addToTests(self,test,root,exfile,execname,testDict):
141    """
142      Put into data structure that allows easy generation of makefile
143      Organized by languages to allow testing of languages
144    """
145    pkg=self.relpath(self.petsc_dir,root).split("/")[1]
146    #nmtest=self.nameSpace(test,root)
147    rpath=self.relpath(self.petsc_dir,root)
148    nmtest=os.path.join(rpath,test)
149    lang=self.getLanguage(exfile)
150    self.tests[pkg][lang][nmtest]={}
151    self.tests[pkg][lang][nmtest]['exfile']=os.path.join(rpath,exfile)
152    self.tests[pkg][lang][nmtest]['exec']=execname
153    self.tests[pkg][lang][nmtest]['argLabel']=self.getArgLabel(testDict)
154    return
155
156  def getFor(self,subst,i,j):
157    """
158      Get the for and done lines
159    """
160    forlines=""
161    donlines=""
162    indent="   "
163    nsizeStr=subst['nsize']
164    for loop in re.findall('{{(.*?)}}',subst['nsize']):
165      lindex=string.ascii_lowercase[i]
166      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
167      nsizeStr=re.sub("{{"+loop+"}}","${"+lindex+"}",nsizeStr)
168      donline=indent*j+"done"
169      forlines=forlines+forline+"\n"
170      donlines=donlines+donline+"\n"
171      i=i+1
172      j=j+1
173    subst['nsize']=nsizeStr
174    argStr=subst['args']
175    for loop in re.findall('{{(.*?)}}',subst['args']):
176      lindex=string.ascii_lowercase[i]
177      forline=indent*j+"for "+lindex+" in '"+loop+"'; do"
178      argStr=re.sub("{{"+loop+"}}","${"+lindex+"}",argStr)
179      donline=indent*j+"done"
180      forlines=forlines+forline+"\n"
181      donlines=donlines+donline+"\n"
182      i=i+1
183      j=j+1
184    subst['args']=argStr
185
186    # The do lines have reverse order with respect to indentation
187    dl=donlines.rstrip("\n").split("\n")
188    dl.reverse()
189    donlines="\n".join(dl)+"\n"
190
191    return forlines,donlines,i,j
192
193
194  def getExecname(self,exfile,root):
195    """
196      Generate bash script using template found next to this file.
197      This file is read in at constructor time to avoid file I/O
198    """
199    rpath=self.relpath(self.petsc_dir,root)
200    if self.single_ex:
201      execname=rpath.split("/")[1]+"-ex"
202    else:
203      execname=os.path.splitext(exfile)[0]
204    return execname
205
206  def getSubstVars(self,testDict,rpath,testname):
207    """
208      Create a dictionary with all of the variables that get substituted
209      into the template commands found in example_template.py
210      TODO: Cleanup
211    """
212    subst={}
213    # Handle defaults
214    if not testDict.has_key('nsize'): testDict['nsize']=1
215    if not testDict.has_key('filter'): testDict['filter']=""
216    if not testDict.has_key('filter_output'): testDict['filter_output']=""
217    if not testDict.has_key('localrunfiles'): testDict['localrunfiles']=""
218    if not testDict.has_key('args'): testDict['args']=""
219    defroot=(re.sub("run","",testname) if testname.startswith("run") else testname)
220    if not testDict.has_key('redirect_file'): testDict['redirect_file']=defroot+".tmp"
221    if not testDict.has_key('output_file'): testDict['output_file']="output/"+defroot+".out"
222
223    # Setup the variables in template_string that need to be substituted
224    subst['srcdir']=os.path.join(self.petsc_dir,rpath)
225    subst['label']=self.nameSpace(defroot,subst['srcdir'])
226    subst['output_file']=os.path.join(subst['srcdir'],testDict['output_file'])
227    subst['exec']="../"+testDict['execname']
228    subst['redirect_file']=testDict['redirect_file']
229    subst['filter']="'"+testDict['filter']+"'"   # Quotes are tricky
230    subst['filter_output']=testDict['filter_output']
231    subst['localrunfiles']=testDict['localrunfiles']
232    subst['testroot']=self.testroot_dir
233    subst['testname']=testname
234
235    # Be careful with this
236    if testDict.has_key('command'): subst['command']=testDict['command']
237
238    # These can have for loops and are treated separately later
239    if testDict.has_key('nsize'): subst['nsize']=str(testDict['nsize'])
240    if testDict.has_key('args'):  subst['args']=testDict['args']
241
242    #Conf vars
243    subst['mpiexec']=self.conf['MPIEXEC']  # make sure PETSC_DIR is defined!
244    subst['diff']=self.conf['DIFF']
245    subst['rm']=self.conf['RM']
246    subst['grep']=self.conf['GREP']
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('@TESTSROOT@',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"),0777)
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" 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        # Scalar requirement
469        if requirement=="complex":
470          if self.conf['PETSC_SCALAR']=='complex':
471            testDict['SKIP']="Non-complex build required"
472            if isNull: return False
473          else:
474            testDict['SKIP']="Complex build required"
475            return False
476        # Precision requirement for reals
477        if requirement in self.precision_types:
478          if self.conf['PETSC_PRECISION']==requirement:
479            testDict['SKIP']="not "+requirement+" required"
480            if isNull: return False
481          else:
482            testDict['SKIP']=requirement+" required"
483            return False
484        # Precision requirement for ints
485        if requirement in self.integer_types:
486          if requirement=="int32":
487            if self.conf['PETSC_SIZEOF_INT']==4:
488              testDict['SKIP']="not int32 required"
489              if isNull: return False
490            else:
491              testDict['SKIP']="int32 required"
492              return False
493          if requirement=="int64":
494            if self.conf['PETSC_SIZEOF_INT']==8:
495              testDict['SKIP']="NOT int64 required"
496              if isNull: return False
497            else:
498              testDict['SKIP']="int64 required"
499              return False
500        # Datafilespath
501        if requirement=="datafilespath":
502          testDict['SKIP']="Requires DATAFILESPATH"
503          return False
504        # Defines -- not sure I have comments matching
505        if "define(" in requirement:
506          reqdef=requirement.split("(")[1].split(")")[0]
507          val=(reqdef.split()[1] if " " in reqdef else "")
508          if self.conf.has_key(reqdef):
509            if val:
510              if self.conf[reqdef]==val:
511                if isNull:
512                  testDict['SKIP']="Null requirement not met: "+requirement
513                  return False
514              else:
515                testDict['SKIP']="Required: "+requirement
516                return False
517            else:
518              if isNull:
519                testDict['SKIP']="Null requirement not met: "+requirement
520                return False
521          else:
522            testDict['SKIP']="Requirement not met: "+requirement
523            return False
524
525        # Rest should be packages that we can just get from conf
526        petscconfvar="PETSC_HAVE_"+requirement.upper()
527        if self.conf.get(petscconfvar):
528          if isNull:
529            testDict['SKIP']="Not "+petscconfvar+" requirement not met"
530            return False
531        else:
532          if debug: print "requirement not found: ", requirement
533          testDict['SKIP']=petscconfvar+" requirement not met"
534          return False
535
536    return True
537
538  def genPetscTests_summarize(self,dataDict):
539    """
540    Required method to state what happened
541    """
542    if not self.summarize: return
543    indent="   "
544    fhname="GenPetscTests_summarize.txt"
545    fh=open(fhname,"w")
546    #print "See ", fhname
547    for root in dataDict:
548      relroot=self.relpath(self.petsc_dir,root)
549      pkg=relroot.split("/")[1]
550      fh.write(relroot+"\n")
551      allSrcs=[]
552      for lang in LANGS: allSrcs=allSrcs+self.sources[pkg][lang]['srcs']
553      for exfile in dataDict[root]:
554        # Basic  information
555        fullfile=os.path.join(root,exfile)
556        rfile=self.relpath(self.petsc_dir,fullfile)
557        builtStatus=(" Is built" if rfile in allSrcs else " Is NOT built")
558        fh.write(indent+exfile+indent*4+builtStatus+"\n")
559
560        for test in dataDict[root][exfile]:
561          if test in self.buildkeys: continue
562          line=indent*2+test
563          fh.write(line+"\n")
564          # Looks nice to have the keys in order
565          #for key in dataDict[root][exfile][test]:
566          for key in "isrun abstracted nsize args requires script".split():
567            if not dataDict[root][exfile][test].has_key(key): continue
568            line=indent*3+key+": "+str(dataDict[root][exfile][test][key])
569            fh.write(line+"\n")
570          fh.write("\n")
571        fh.write("\n")
572      fh.write("\n")
573    #fh.write("\nClass Sources\n"+str(self.sources)+"\n")
574    #fh.write("\nClass Tests\n"+str(self.tests)+"\n")
575    fh.close()
576    return
577
578  def genPetscTests(self,root,dirs,files,dataDict):
579    """
580     Go through and parse the source files in the directory to generate
581     the examples based on the metadata contained in the source files
582    """
583    debug=False
584    # Use examplesAnalyze to get what the makefles think are sources
585    #self.examplesAnalyze(root,dirs,files,anlzDict)
586
587    dataDict[root]={}
588
589    for exfile in files:
590      #TST: Until we replace files, still leaving the orginals as is
591      #if not exfile.startswith("new_"+"ex"): continue
592      if not exfile.startswith("ex"): continue
593
594      # Convenience
595      fullex=os.path.join(root,exfile)
596      relpfile=self.relpath(self.petsc_dir,fullex)
597      if debug: print relpfile
598      dataDict[root].update(testparse.parseTestFile(fullex))
599      # Need to check and make sure tests are in the file
600      # if verbosity>=1: print relpfile
601      if dataDict[root].has_key(exfile):
602        self.genScriptsAndInfo(exfile,root,dataDict[root][exfile])
603
604    return
605
606  def walktree(self,top,action="printFiles"):
607    """
608    Walk a directory tree, starting from 'top'
609    """
610    #print "action", action
611    # Goal of action is to fill this dictionary
612    dataDict={}
613    for root, dirs, files in os.walk(top, topdown=False):
614      if not "examples" in root: continue
615      if not os.path.isfile(os.path.join(root,"makefile")): continue
616      bname=os.path.basename(root.rstrip("/"))
617      if bname=="tests" or bname=="tutorials":
618        eval("self."+action+"(root,dirs,files,dataDict)")
619      if type(top) != types.StringType:
620          raise TypeError("top must be a string")
621    # Now summarize this dictionary
622    eval("self."+action+"_summarize(dataDict)")
623    return dataDict
624
625  def gen_gnumake(self, fd,prefix='srcs-'):
626    """
627     Overwrite of the method in the base PETSc class
628    """
629    def write(stem, srcs):
630        fd.write('%s :=\n' % stem)
631        for lang in LANGS:
632            fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs'])))
633            fd.write('%(stem)s += $(%(stem)s.%(lang)s)\n' % dict(stem=stem, lang=lang))
634    for pkg in PKGS:
635        srcs = self.gen_pkg(pkg)
636        write(prefix + pkg, srcs)
637    return self.gendeps
638
639  def gen_pkg(self, pkg):
640    """
641     Overwrite of the method in the base PETSc class
642    """
643    return self.sources[pkg]
644
645  def write_gnumake(self,dataDict):
646    """
647     Write out something similar to files from gmakegen.py
648
649     There is not a lot of has_key type checking because
650     should just work and need to know if there are bugs
651
652     Test depends on script which also depends on source
653     file, but since I don't have a good way generating
654     acting on a single file (oops) just depend on
655     executable which in turn will depend on src file
656    """
657    # Open file
658    arch_files = self.arch_path('lib','petsc','conf', 'testfiles')
659    arg_files = self.arch_path('lib','petsc','conf', 'testargfiles')
660    fd = open(arch_files, 'w')
661    fa = open(arg_files, 'w')
662
663    # Write out the sources
664    gendeps = self.gen_gnumake(fd,prefix="testsrcs-")
665
666    # Write out the tests and execname targets
667    fd.write("\n#Tests and executables\n")    # Delimiter
668    testdeps=" ".join(["test-"+pkg for pkg in PKGS])
669    testexdeps=" ".join(["test-ex-"+pkg for pkg in PKGS])
670    fd.write("test: testex "+testdeps+" report_tests\n")    # Main test target
671    # Add executables to build right way to make the `make test` look
672    # nicer
673    fd.write("testex: "+testexdeps+"\n")    # Main test target
674
675    for pkg in PKGS:
676      # These grab the ones that are built
677      # Package tests
678      testdeps=" ".join(["test-"+pkg+"-"+lang for lang in LANGS])
679      fd.write("test-"+pkg+": "+testdeps+"\n")
680      testexdeps=" ".join(["test-ex-"+pkg+"-"+lang for lang in LANGS])
681      fd.write("test-ex-"+pkg+": "+testexdeps+"\n")
682      # This needs work
683      if self.single_ex:
684        execname=pkg+"-ex"
685        fd.write(execname+": "+" ".join(self.objects[pkg])+"\n\n")
686      for lang in LANGS:
687        testdeps=""
688        for ftest in self.tests[pkg][lang]:
689          test=os.path.basename(ftest)
690          basedir=os.path.dirname(ftest)
691          testdeps=testdeps+" "+self.nameSpace(test,basedir)
692        fd.write("test-"+pkg+"-"+lang+":"+testdeps+"\n")
693
694        # test targets
695        for ftest in self.tests[pkg][lang]:
696          test=os.path.basename(ftest)
697          basedir=os.path.dirname(ftest)
698          testdir="${TESTDIR}/"+basedir+"/"
699          nmtest=self.nameSpace(test,basedir)
700          rundir=os.path.join(testdir,test)
701          #print test, nmtest
702          script=test+".sh"
703
704          # Deps
705          exfile=self.tests[pkg][lang][ftest]['exfile']
706          fullex=os.path.join(self.petsc_dir,exfile)
707          localexec=self.tests[pkg][lang][ftest]['exec']
708          execname=os.path.join(testdir,localexec)
709
710          # SKIP and TODO tests do not depend on exec
711          if exfile in self.sources[pkg][lang]['srcs']:
712            #print "Found dep: "+exfile, execname
713            fd.write(nmtest+": "+execname+"\n")
714          else:
715            # Still add dependency to file
716            fd.write(nmtest+": "+fullex+"\n")
717          cmd=testdir+"/"+script+" ${TESTFLAGS} | tee ${PETSC_ARCH}/test.log"
718          fd.write("\t-@"+cmd+"\n")
719          # Now write the args:
720          fa.write(nmtest+"_ARGS='"+self.tests[pkg][lang][ftest]['argLabel']+"'\n")
721
722        # executable targets -- add these to build earlier
723        testexdeps=""
724        if not self.single_ex:
725          for exfile in self.sources[pkg][lang]['srcs']:
726            localexec=os.path.basename(os.path.splitext(exfile)[0])
727            basedir=os.path.dirname(exfile)
728            testdir="${TESTDIR}/"+basedir+"/"
729            execname=os.path.join(testdir,localexec)
730            testexdeps=testexdeps+" "+execname
731          fd.write("test-ex-"+pkg+"-"+lang+":"+testexdeps+"\n")
732
733        for exfile in self.sources[pkg][lang]['srcs']:
734          root=os.path.join(self.petsc_dir,os.path.dirname(exfile))
735          basedir=os.path.dirname(exfile)
736          testdir="${TESTDIR}/"+basedir+"/"
737          base=os.path.basename(exfile)
738          objfile=testdir+os.path.splitext(base)[0]+".o"
739          linker=self.getLanguage(exfile)[0].upper()+"LINKER"
740          if self.sources[pkg][lang].has_key(exfile):
741            # Dependency for file
742            objfile=objfile+" "+self.sources[pkg][lang][exfile]
743            print objfile
744          if not self.single_ex:
745            localexec=os.path.basename(os.path.splitext(exfile)[0])
746            execname=os.path.join(testdir,localexec)
747            localobj=os.path.basename(objfile)
748            petsc_lib="${PETSC_"+pkg.upper()+"_LIB}"
749            fd.write("\n"+execname+": "+objfile+" ${libpetscall}\n")
750            # There should be a better way here
751            line="\t-cd "+testdir+"; ${"+linker+"} -o "+localexec+" "+localobj+" "+petsc_lib
752            fd.write(line+"\n")
753          linker=self.getLanguage(exfile)[0].upper()+"LINKER"
754
755    fd.write("helptests:\n\t -@grep '^[a-z]' ${generatedtest} | cut -f1 -d:\n")
756    # Write out tests
757    return
758
759  def writeHarness(self,output,dataDict):
760    """
761     This is set up to write out multiple harness even if only gnumake
762     is supported now
763    """
764    eval("self.write_"+output+"(dataDict)")
765    return
766
767def main(petsc_dir=None, petsc_arch=None, output=None, verbose=False, single_ex=False):
768    if output is None:
769        output = 'gnumake'
770
771
772    pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose, single_ex=single_ex)
773    dataDict=pEx.walktree(os.path.join(pEx.petsc_dir,'src'),action="genPetscTests")
774    pEx.writeHarness(output,dataDict)
775
776if __name__ == '__main__':
777    import optparse
778    parser = optparse.OptionParser()
779    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
780    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
781    parser.add_option('--output', help='Location to write output file', default=None)
782    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')
783    opts, extra_args = parser.parse_args()
784    if extra_args:
785        import sys
786        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
787        exit(1)
788    main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose, single_ex=opts.single_executable)
789