xref: /petsc/config/gmakegentest.py (revision 7bf74fed42e05f6f6afa222aed70dd91967a12b9)
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.lower():
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                return True
523          else:
524            testDict['SKIP']="Requirement not met: "+requirement
525            return False
526
527        # Rest should be packages that we can just get from conf
528        petscconfvar="PETSC_HAVE_"+requirement.upper()
529        if self.conf.get(petscconfvar):
530          if isNull:
531            testDict['SKIP']="Not "+petscconfvar+" requirement not met"
532            return False
533        else:
534          if debug: print "requirement not found: ", requirement
535          testDict['SKIP']=petscconfvar+" requirement not met"
536          return False
537
538    return True
539
540  def genPetscTests_summarize(self,dataDict):
541    """
542    Required method to state what happened
543    """
544    if not self.summarize: return
545    indent="   "
546    fhname="GenPetscTests_summarize.txt"
547    fh=open(fhname,"w")
548    #print "See ", fhname
549    for root in dataDict:
550      relroot=self.relpath(self.petsc_dir,root)
551      pkg=relroot.split("/")[1]
552      fh.write(relroot+"\n")
553      allSrcs=[]
554      for lang in LANGS: allSrcs=allSrcs+self.sources[pkg][lang]['srcs']
555      for exfile in dataDict[root]:
556        # Basic  information
557        fullfile=os.path.join(root,exfile)
558        rfile=self.relpath(self.petsc_dir,fullfile)
559        builtStatus=(" Is built" if rfile in allSrcs else " Is NOT built")
560        fh.write(indent+exfile+indent*4+builtStatus+"\n")
561
562        for test in dataDict[root][exfile]:
563          if test in self.buildkeys: continue
564          line=indent*2+test
565          fh.write(line+"\n")
566          # Looks nice to have the keys in order
567          #for key in dataDict[root][exfile][test]:
568          for key in "isrun abstracted nsize args requires script".split():
569            if not dataDict[root][exfile][test].has_key(key): continue
570            line=indent*3+key+": "+str(dataDict[root][exfile][test][key])
571            fh.write(line+"\n")
572          fh.write("\n")
573        fh.write("\n")
574      fh.write("\n")
575    #fh.write("\nClass Sources\n"+str(self.sources)+"\n")
576    #fh.write("\nClass Tests\n"+str(self.tests)+"\n")
577    fh.close()
578    return
579
580  def genPetscTests(self,root,dirs,files,dataDict):
581    """
582     Go through and parse the source files in the directory to generate
583     the examples based on the metadata contained in the source files
584    """
585    debug=False
586    # Use examplesAnalyze to get what the makefles think are sources
587    #self.examplesAnalyze(root,dirs,files,anlzDict)
588
589    dataDict[root]={}
590
591    for exfile in files:
592      #TST: Until we replace files, still leaving the orginals as is
593      #if not exfile.startswith("new_"+"ex"): continue
594      if not exfile.startswith("ex"): continue
595
596      # Convenience
597      fullex=os.path.join(root,exfile)
598      relpfile=self.relpath(self.petsc_dir,fullex)
599      if debug: print relpfile
600      dataDict[root].update(testparse.parseTestFile(fullex))
601      # Need to check and make sure tests are in the file
602      # if verbosity>=1: print relpfile
603      if dataDict[root].has_key(exfile):
604        self.genScriptsAndInfo(exfile,root,dataDict[root][exfile])
605
606    return
607
608  def walktree(self,top,action="printFiles"):
609    """
610    Walk a directory tree, starting from 'top'
611    """
612    #print "action", action
613    # Goal of action is to fill this dictionary
614    dataDict={}
615    for root, dirs, files in os.walk(top, topdown=False):
616      if not "examples" in root: continue
617      if not os.path.isfile(os.path.join(root,"makefile")): continue
618      bname=os.path.basename(root.rstrip("/"))
619      if bname=="tests" or bname=="tutorials":
620        eval("self."+action+"(root,dirs,files,dataDict)")
621      if type(top) != types.StringType:
622          raise TypeError("top must be a string")
623    # Now summarize this dictionary
624    eval("self."+action+"_summarize(dataDict)")
625    return dataDict
626
627  def gen_gnumake(self, fd,prefix='srcs-'):
628    """
629     Overwrite of the method in the base PETSc class
630    """
631    def write(stem, srcs):
632        fd.write('%s :=\n' % stem)
633        for lang in LANGS:
634            fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs'])))
635            fd.write('%(stem)s += $(%(stem)s.%(lang)s)\n' % dict(stem=stem, lang=lang))
636    for pkg in PKGS:
637        srcs = self.gen_pkg(pkg)
638        write(prefix + pkg, srcs)
639    return self.gendeps
640
641  def gen_pkg(self, pkg):
642    """
643     Overwrite of the method in the base PETSc class
644    """
645    return self.sources[pkg]
646
647  def write_gnumake(self,dataDict):
648    """
649     Write out something similar to files from gmakegen.py
650
651     There is not a lot of has_key type checking because
652     should just work and need to know if there are bugs
653
654     Test depends on script which also depends on source
655     file, but since I don't have a good way generating
656     acting on a single file (oops) just depend on
657     executable which in turn will depend on src file
658    """
659    # Open file
660    arch_files = self.arch_path('lib','petsc','conf', 'testfiles')
661    arg_files = self.arch_path('lib','petsc','conf', 'testargfiles')
662    fd = open(arch_files, 'w')
663    fa = open(arg_files, 'w')
664
665    # Write out the sources
666    gendeps = self.gen_gnumake(fd,prefix="testsrcs-")
667
668    # Write out the tests and execname targets
669    fd.write("\n#Tests and executables\n")    # Delimiter
670    testdeps=" ".join(["test-"+pkg for pkg in PKGS])
671    testexdeps=" ".join(["test-ex-"+pkg for pkg in PKGS])
672    fd.write("test: testinit testex testpkgs report_tests\n")    # Main test target
673    fd.write("testpkgs: "+testdeps+"\n")    # The test for the pkgs
674    # Testinit handles the logging
675    fd.write("testinit:\n")
676    fd.write("\t-@rm -f ${PETSC_ARCH}/tests/test.log\n")
677    fd.write("\t-@touch ${PETSC_ARCH}/tests/test.log\n")
678    # Add executables to build right way to make the `make test` look
679    # nicer
680    fd.write("testex: "+testexdeps+"\n")    # Main test target
681
682    for pkg in PKGS:
683      # These grab the ones that are built
684      # Package tests
685      testdeps=" ".join(["test-"+pkg+"-"+lang for lang in LANGS])
686      fd.write("test-"+pkg+": "+testdeps+"\n")
687      testexdeps=" ".join(["test-ex-"+pkg+"-"+lang for lang in LANGS])
688      fd.write("test-ex-"+pkg+": "+testexdeps+"\n")
689      # This needs work
690      if self.single_ex:
691        execname=pkg+"-ex"
692        fd.write(execname+": "+" ".join(self.objects[pkg])+"\n\n")
693      for lang in LANGS:
694        testdeps=""
695        for ftest in self.tests[pkg][lang]:
696          test=os.path.basename(ftest)
697          basedir=os.path.dirname(ftest)
698          testdeps=testdeps+" "+self.nameSpace(test,basedir)
699        fd.write("test-"+pkg+"-"+lang+":"+testdeps+"\n")
700
701        # test targets
702        for ftest in self.tests[pkg][lang]:
703          test=os.path.basename(ftest)
704          basedir=os.path.dirname(ftest)
705          testdir="${TESTDIR}/"+basedir+"/"
706          nmtest=self.nameSpace(test,basedir)
707          rundir=os.path.join(testdir,test)
708          #print test, nmtest
709          script=test+".sh"
710
711          # Deps
712          exfile=self.tests[pkg][lang][ftest]['exfile']
713          fullex=os.path.join(self.petsc_dir,exfile)
714          localexec=self.tests[pkg][lang][ftest]['exec']
715          execname=os.path.join(testdir,localexec)
716
717          # SKIP and TODO tests do not depend on exec
718          if exfile in self.sources[pkg][lang]['srcs']:
719            #print "Found dep: "+exfile, execname
720            fd.write(nmtest+": "+execname+"\n")
721          else:
722            # Still add dependency to file
723            fd.write(nmtest+": "+fullex+"\n")
724          cmd=testdir+"/"+script+" ${TESTFLAGS} | tee -a ${PETSC_ARCH}/tests/test.log"
725          fd.write("\t-@"+cmd+"\n")
726          # Now write the args:
727          fa.write(nmtest+"_ARGS='"+self.tests[pkg][lang][ftest]['argLabel']+"'\n")
728
729        # executable targets -- add these to build earlier
730        testexdeps=""
731        if not self.single_ex:
732          for exfile in self.sources[pkg][lang]['srcs']:
733            localexec=os.path.basename(os.path.splitext(exfile)[0])
734            basedir=os.path.dirname(exfile)
735            testdir="${TESTDIR}/"+basedir+"/"
736            execname=os.path.join(testdir,localexec)
737            testexdeps=testexdeps+" "+execname
738          fd.write("test-ex-"+pkg+"-"+lang+":"+testexdeps+"\n")
739
740        for exfile in self.sources[pkg][lang]['srcs']:
741          root=os.path.join(self.petsc_dir,os.path.dirname(exfile))
742          basedir=os.path.dirname(exfile)
743          testdir="${TESTDIR}/"+basedir+"/"
744          base=os.path.basename(exfile)
745          objfile=testdir+os.path.splitext(base)[0]+".o"
746          linker=self.getLanguage(exfile)[0].upper()+"LINKER"
747          if self.sources[pkg][lang].has_key(exfile):
748            # Dependency for file
749            objfile=objfile+" "+self.sources[pkg][lang][exfile]
750            print objfile
751          if not self.single_ex:
752            localexec=os.path.basename(os.path.splitext(exfile)[0])
753            execname=os.path.join(testdir,localexec)
754            localobj=os.path.basename(objfile)
755            petsc_lib="${PETSC_"+pkg.upper()+"_LIB}"
756            fd.write("\n"+execname+": "+objfile+" ${libpetscall}\n")
757            # There should be a better way here
758            line="\t-cd "+testdir+"; ${"+linker+"} -o "+localexec+" "+localobj+" "+petsc_lib
759            fd.write(line+"\n")
760          linker=self.getLanguage(exfile)[0].upper()+"LINKER"
761
762    fd.write("helptests:\n\t -@grep '^[a-z]' ${generatedtest} | cut -f1 -d:\n")
763    # Write out tests
764    return
765
766  def writeHarness(self,output,dataDict):
767    """
768     This is set up to write out multiple harness even if only gnumake
769     is supported now
770    """
771    eval("self.write_"+output+"(dataDict)")
772    return
773
774def main(petsc_dir=None, petsc_arch=None, output=None, verbose=False, single_ex=False):
775    if output is None:
776        output = 'gnumake'
777
778
779    pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch, verbose=verbose, single_ex=single_ex)
780    dataDict=pEx.walktree(os.path.join(pEx.petsc_dir,'src'),action="genPetscTests")
781    pEx.writeHarness(output,dataDict)
782
783if __name__ == '__main__':
784    import optparse
785    parser = optparse.OptionParser()
786    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
787    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
788    parser.add_option('--output', help='Location to write output file', default=None)
789    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')
790    opts, extra_args = parser.parse_args()
791    if extra_args:
792        import sys
793        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
794        exit(1)
795    main(petsc_arch=opts.petsc_arch, output=opts.output, verbose=opts.verbose, single_ex=opts.single_executable)
796