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