xref: /petsc/config/gmakegentest.py (revision 6a044f4f75af7bf8c1b8ddfba96aeb117435f4e3)
129921a8fSScott Kruger#!/usr/bin/env python
229921a8fSScott Kruger
35b6bfdb9SJed Brownfrom __future__ import print_function
429921a8fSScott Krugerimport os,shutil, string, re
529921a8fSScott Krugerfrom distutils.sysconfig import parse_makefile
629921a8fSScott Krugerimport sys
76ac365aeSScott Krugerimport logging, time
829921a8fSScott Krugerimport types
929921a8fSScott Krugersys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
1029921a8fSScott Krugerfrom cmakegen import Mistakes, stripsplit, AUTODIRS, SKIPDIRS
117b8851e6SJed Brownfrom collections import defaultdict
1229921a8fSScott Krugerfrom gmakegen import *
1329921a8fSScott Kruger
1429921a8fSScott Krugerimport inspect
1529921a8fSScott Krugerthisscriptdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
1629921a8fSScott Krugersys.path.insert(0,thisscriptdir)
1729921a8fSScott Krugerimport testparse
1829921a8fSScott Krugerimport example_template
1929921a8fSScott Kruger
204ff3c6a1SScott Kruger
214ff3c6a1SScott Kruger"""
224ff3c6a1SScott Kruger
238e69c5ecSJed BrownThere are 2 modes of running tests: Normal builds and run from prefix of
248e69c5ecSJed Browninstall.  They affect where to find things:
254ff3c6a1SScott Kruger
264ff3c6a1SScott Kruger
274ff3c6a1SScott KrugerCase 1.  Normal builds:
284ff3c6a1SScott Kruger
294ff3c6a1SScott Kruger     +---------------------+----------------------------------+
304ff3c6a1SScott Kruger     | PETSC_DIR           | <git dir>                        |
314ff3c6a1SScott Kruger     +---------------------+----------------------------------+
324ff3c6a1SScott Kruger     | PETSC_ARCH          | arch-foo                         |
334ff3c6a1SScott Kruger     +---------------------+----------------------------------+
344ff3c6a1SScott Kruger     | PETSC_LIBDIR        | PETSC_DIR/PETSC_ARCH/lib         |
354ff3c6a1SScott Kruger     +---------------------+----------------------------------+
364ff3c6a1SScott Kruger     | PETSC_EXAMPLESDIR   | PETSC_DIR/src                    |
374ff3c6a1SScott Kruger     +---------------------+----------------------------------+
384ff3c6a1SScott Kruger     | PETSC_TESTDIR       | PETSC_DIR/PETSC_ARCH/tests       |
394ff3c6a1SScott Kruger     +---------------------+----------------------------------+
404ff3c6a1SScott Kruger     | PETSC_GMAKEFILETEST | PETSC_DIR/gmakefile.test         |
414ff3c6a1SScott Kruger     +---------------------+----------------------------------+
424ff3c6a1SScott Kruger     | PETSC_GMAKEGENTEST  | PETSC_DIR/config/gmakegentest.py |
434ff3c6a1SScott Kruger     +---------------------+----------------------------------+
444ff3c6a1SScott Kruger
454ff3c6a1SScott Kruger
468e69c5ecSJed BrownCase 2.  From install dir:
474ff3c6a1SScott Kruger
484ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
494ff3c6a1SScott Kruger     | PETSC_DIR           | <prefix dir>                                          |
504ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
514ff3c6a1SScott Kruger     | PETSC_ARCH          | ''                                                    |
524ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
534ff3c6a1SScott Kruger     | PETSC_LIBDIR        | PETSC_DIR/PETSC_ARCH/lib                              |
544ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
554ff3c6a1SScott Kruger     | PETSC_EXAMPLESDIR   | PETSC_DIR/share/petsc/examples/src                    |
564ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
574ff3c6a1SScott Kruger     | PETSC_TESTDIR       | PETSC_DIR/PETSC_ARCH/tests                            |
584ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
594ff3c6a1SScott Kruger     | PETSC_GMAKEFILETEST | PETSC_DIR/share/petsc/examples/gmakefile.test         |
604ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
614ff3c6a1SScott Kruger     | PETSC_GMAKEGENTEST  | PETSC_DIR/share/petsc/examples/config/gmakegentest.py |
624ff3c6a1SScott Kruger     +---------------------+-------------------------------------------------------+
634ff3c6a1SScott Kruger
644ff3c6a1SScott Kruger"""
658e69c5ecSJed Brown
668e69c5ecSJed Browndef install_files(source, destdir):
678e69c5ecSJed Brown  """Install file or directory 'source' to 'destdir'.  Does not preserve
688e69c5ecSJed Brown  mode (permissions).
698e69c5ecSJed Brown  """
708e69c5ecSJed Brown  if not os.path.isdir(destdir):
718e69c5ecSJed Brown    os.makedirs(destdir)
728e69c5ecSJed Brown  if os.path.isdir(source):
738e69c5ecSJed Brown    for name in os.listdir(source):
748e69c5ecSJed Brown      install_files(os.path.join(source, name), os.path.join(destdir, os.path.basename(source)))
758e69c5ecSJed Brown  else:
768e69c5ecSJed Brown    shutil.copyfile(source, os.path.join(destdir, os.path.basename(source)))
778e69c5ecSJed Brown
7829921a8fSScott Krugerclass generateExamples(Petsc):
7929921a8fSScott Kruger  """
8029921a8fSScott Kruger    gmakegen.py has basic structure for finding the files, writing out
8129921a8fSScott Kruger      the dependencies, etc.
8229921a8fSScott Kruger  """
838e69c5ecSJed Brown  def __init__(self,petsc_dir=None, petsc_arch=None, testdir='tests', verbose=False, single_ex=False, srcdir=None):
84e551db17SScott Kruger    super(generateExamples, self).__init__(petsc_dir, petsc_arch, verbose)
8529921a8fSScott Kruger
8629921a8fSScott Kruger    self.single_ex=single_ex
87e551db17SScott Kruger
88c173c275SScott Kruger    # Set locations to handle movement
89fc46264cSScott Kruger    self.inInstallDir=self.getInInstallDir(thisscriptdir)
904ff3c6a1SScott Kruger
918e69c5ecSJed Brown    if self.inInstallDir:
924ff3c6a1SScott Kruger      # Case 2 discussed above
93c173c275SScott Kruger      # set PETSC_ARCH to install directory to allow script to work in both
94c173c275SScott Kruger      dirlist=thisscriptdir.split(os.path.sep)
95c173c275SScott Kruger      installdir=os.path.sep.join(dirlist[0:len(dirlist)-4])
96e551db17SScott Kruger      self.arch_dir=installdir
97c173c275SScott Kruger      self.srcdir=os.path.join(os.path.dirname(thisscriptdir),'src')
988e69c5ecSJed Brown    else:
998e69c5ecSJed Brown      if petsc_arch == '':
1008e69c5ecSJed Brown        raise RuntimeError('PETSC_ARCH must be set when running from build directory')
1018e69c5ecSJed Brown      # Case 1 discussed above
1028e69c5ecSJed Brown      self.arch_dir=os.path.join(self.petsc_dir,self.petsc_arch)
1038e69c5ecSJed Brown      self.srcdir=os.path.join(self.petsc_dir,'src')
104e551db17SScott Kruger
10594666443SJed Brown    self.testroot_dir=os.path.abspath(testdir)
106e551db17SScott Kruger
10729921a8fSScott Kruger    self.ptNaming=True
108cadd188bSScott Kruger    self.verbose=verbose
10929921a8fSScott Kruger    # Whether to write out a useful debugging
110cadd188bSScott Kruger    self.summarize=True if verbose else False
11129921a8fSScott Kruger
11229921a8fSScott Kruger    # For help in setting the requirements
11312df5dc0SToby Isaac    self.precision_types="single double __float128 int32".split()
11429921a8fSScott Kruger    self.integer_types="int32 int64".split()
11529921a8fSScott Kruger    self.languages="fortran cuda cxx".split()    # Always requires C so do not list
11629921a8fSScott Kruger
11729921a8fSScott Kruger    # Things that are not test
11829921a8fSScott Kruger    self.buildkeys=testparse.buildkeys
11929921a8fSScott Kruger
12029921a8fSScott Kruger    # Adding a dictionary for storing sources, objects, and tests
12129921a8fSScott Kruger    # to make building the dependency tree easier
12229921a8fSScott Kruger    self.sources={}
12329921a8fSScott Kruger    self.objects={}
12429921a8fSScott Kruger    self.tests={}
12529921a8fSScott Kruger    for pkg in PKGS:
12629921a8fSScott Kruger      self.sources[pkg]={}
12729921a8fSScott Kruger      self.objects[pkg]=[]
12829921a8fSScott Kruger      self.tests[pkg]={}
12929921a8fSScott Kruger      for lang in LANGS:
13029921a8fSScott Kruger        self.sources[pkg][lang]={}
13129921a8fSScott Kruger        self.sources[pkg][lang]['srcs']=[]
13229921a8fSScott Kruger        self.tests[pkg][lang]={}
13329921a8fSScott Kruger
13429921a8fSScott Kruger    if not os.path.isdir(self.testroot_dir): os.makedirs(self.testroot_dir)
1350357d61fSScott Kruger
1360357d61fSScott Kruger    self.indent="   "
137cadd188bSScott Kruger    if self.verbose: print('Finishing the constructor')
13829921a8fSScott Kruger    return
13929921a8fSScott Kruger
140c173c275SScott Kruger  def srcrelpath(self,rdir):
141c173c275SScott Kruger    """
142c173c275SScott Kruger    Get relative path to source directory
143c173c275SScott Kruger    """
14494666443SJed Brown    return os.path.relpath(rdir,self.srcdir)
145c173c275SScott Kruger
146fc46264cSScott Kruger  def getInInstallDir(self,thisscriptdir):
147c173c275SScott Kruger    """
148c173c275SScott Kruger    When petsc is installed then this file in installed in:
149c173c275SScott Kruger         <PREFIX>/share/petsc/examples/config/gmakegentest.py
150c173c275SScott Kruger    otherwise the path is:
151c173c275SScott Kruger         <PETSC_DIR>/config/gmakegentest.py
152c173c275SScott Kruger    We use this difference to determine if we are in installdir
153c173c275SScott Kruger    """
154c173c275SScott Kruger    dirlist=thisscriptdir.split(os.path.sep)
155c173c275SScott Kruger    if len(dirlist)>4:
156c173c275SScott Kruger      lastfour=os.path.sep.join(dirlist[len(dirlist)-4:])
157c173c275SScott Kruger      if lastfour==os.path.join('share','petsc','examples','config'):
158c173c275SScott Kruger        return True
159c173c275SScott Kruger      else:
160c173c275SScott Kruger        return False
161c173c275SScott Kruger    else:
162c173c275SScott Kruger      return False
163c173c275SScott Kruger
16429921a8fSScott Kruger  def nameSpace(self,srcfile,srcdir):
16529921a8fSScott Kruger    """
16629921a8fSScott Kruger    Because the scripts have a non-unique naming, the pretty-printing
16729921a8fSScott Kruger    needs to convey the srcdir and srcfile.  There are two ways of doing this.
16829921a8fSScott Kruger    """
16929921a8fSScott Kruger    if self.ptNaming:
170886063b6SScott Kruger      if srcfile.startswith('run'): srcfile=re.sub('^run','',srcfile)
17194666443SJed Brown      cdir=srcdir
17229921a8fSScott Kruger      prefix=cdir.replace('/examples/','_').replace("/","_")+"-"
17329921a8fSScott Kruger      nameString=prefix+srcfile
17429921a8fSScott Kruger    else:
17529921a8fSScott Kruger      #nameString=srcdir+": "+srcfile
17629921a8fSScott Kruger      nameString=srcfile
17729921a8fSScott Kruger    return nameString
17829921a8fSScott Kruger
17929921a8fSScott Kruger  def getLanguage(self,srcfile):
18029921a8fSScott Kruger    """
18129921a8fSScott Kruger    Based on the source, determine associated language as found in gmakegen.LANGS
18229921a8fSScott Kruger    Can we just return srcext[1:\] now?
18329921a8fSScott Kruger    """
18429921a8fSScott Kruger    langReq=None
18529921a8fSScott Kruger    srcext=os.path.splitext(srcfile)[-1]
18629921a8fSScott Kruger    if srcext in ".F90".split(): langReq="F90"
18729921a8fSScott Kruger    if srcext in ".F".split(): langReq="F"
18829921a8fSScott Kruger    if srcext in ".cxx".split(): langReq="cxx"
18929921a8fSScott Kruger    if srcext == ".cu": langReq="cu"
19029921a8fSScott Kruger    if srcext == ".c": langReq="c"
19131ad0a39SScott Kruger    #if not langReq: print("ERROR: ", srcext, srcfile)
19229921a8fSScott Kruger    return langReq
19329921a8fSScott Kruger
19496d5c3b5SScott Kruger  def _getLoopVars(self,inDict,testname, isSubtest=False):
195e9b06b45SScott Kruger    """
196e9b06b45SScott Kruger    Given: 'args: -bs {{1 2 3 4 5}} -pc_type {{cholesky sor}} -ksp_monitor'
197e9b06b45SScott Kruger    Return:
19896d5c3b5SScott Kruger      inDict['args']: -ksp_monitor
19996d5c3b5SScott Kruger      inDict['subargs']: -bs ${bs} -pc_type ${pc_type}
2000357d61fSScott Kruger      loopVars['subargs']['varlist']=['bs' 'pc_type']   # Don't worry about OrderedDict
2017827e0d6SScott Kruger      loopVars['subargs']['bs']=[["bs"],["1 2 3 4 5"]]
2027827e0d6SScott Kruger      loopVars['subargs']['pc_type']=[["pc_type"],["cholesky sor"]]
20396d5c3b5SScott Kruger    subst should be passed in instead of inDict
204e9b06b45SScott Kruger    """
2050357d61fSScott Kruger    loopVars={}; newargs=""
20696d5c3b5SScott Kruger    lsuffix='_'
207089fa112SScott Kruger    argregex=re.compile('(?<![a-zA-Z])-(?=[a-zA-Z])')
208aae9f2d9SScott Kruger    from testparse import parseLoopArgs
209*6a044f4fSJed Brown    for key in inDict:
210*6a044f4fSJed Brown      if key in ('SKIP', 'regexes'):
211*6a044f4fSJed Brown        continue
2120357d61fSScott Kruger      akey=('subargs' if key=='args' else key)  # what to assign
213862148f4SScott Kruger      if akey not in inDict: inDict[akey]=''
214*6a044f4fSJed Brown      if akey == 'nsize' and not inDict['nsize'].startswith('{{'):
215*6a044f4fSJed Brown        # Always generate a loop over nsize, even if there is only one value
216*6a044f4fSJed Brown        inDict['nsize'] = '{{' + inDict['nsize'] + '}}'
217*6a044f4fSJed Brown      keystr = str(inDict[key])
2180357d61fSScott Kruger      varlist = []
219089fa112SScott Kruger      for varset in argregex.split(keystr):
220e9b06b45SScott Kruger        if not varset.strip(): continue
221aae9f2d9SScott Kruger        if '{{' in varset:
222aae9f2d9SScott Kruger          keyvar,lvars,ftype=parseLoopArgs(varset)
223862148f4SScott Kruger          if akey not in loopVars: loopVars[akey]={}
2247827e0d6SScott Kruger          varlist.append(keyvar)
2252521ea3cSScott Kruger          loopVars[akey][keyvar]=[keyvar,lvars]
2267827e0d6SScott Kruger          if akey=='nsize':
227*6a044f4fSJed Brown            if len(lvars.split()) > 1:
22896d5c3b5SScott Kruger              inDict[akey] = '${' + keyvar + '}'
22996d5c3b5SScott Kruger              lsuffix+=akey+'-'+inDict[akey]+'_'
2307827e0d6SScott Kruger          else:
23196d5c3b5SScott Kruger            inDict[akey] += ' -'+keyvar+' ${' + keyvar + '}'
23296d5c3b5SScott Kruger            lsuffix+=keyvar+'-${' + keyvar + '}_'
2337827e0d6SScott Kruger        else:
2347827e0d6SScott Kruger          if key=='args': newargs+=" -"+varset.strip()
235cadd188bSScott Kruger        if varlist: loopVars[akey]['varlist']=varlist
2368ba3acebSToby Isaac
2370357d61fSScott Kruger    # For subtests, args are always substituted in (not top level)
2380357d61fSScott Kruger    if isSubtest:
23996d5c3b5SScott Kruger      inDict['subargs']+=" "+newargs.strip()
24096d5c3b5SScott Kruger      inDict['args']=''
241862148f4SScott Kruger      if 'label_suffix' in inDict:
24296d5c3b5SScott Kruger        inDict['label_suffix']+=lsuffix.rstrip('_')
2430357d61fSScott Kruger      else:
24496d5c3b5SScott Kruger        inDict['label_suffix']=lsuffix.rstrip('_')
24596d5c3b5SScott Kruger    else:
2462b757757SJed Brown      if loopVars:
24796d5c3b5SScott Kruger        inDict['args']=newargs.strip()
24896d5c3b5SScott Kruger        inDict['label_suffix']=lsuffix.rstrip('_')
249e9b06b45SScott Kruger    return loopVars
2508ba3acebSToby Isaac
25129921a8fSScott Kruger  def getArgLabel(self,testDict):
25229921a8fSScott Kruger    """
25329921a8fSScott Kruger    In all of the arguments in the test dictionary, create a simple
25429921a8fSScott Kruger    string for searching within the makefile system.  For simplicity in
25529921a8fSScott Kruger    search, remove "-", for strings, etc.
25629921a8fSScott Kruger    Also, concatenate the arg commands
25729921a8fSScott Kruger    For now, ignore nsize -- seems hard to search for anyway
25829921a8fSScott Kruger    """
25929921a8fSScott Kruger    # Collect all of the args associated with a test
260862148f4SScott Kruger    argStr=("" if 'args' not in testDict else testDict['args'])
261862148f4SScott Kruger    if 'subtests' in testDict:
26229921a8fSScott Kruger      for stest in testDict["subtests"]:
26329921a8fSScott Kruger         sd=testDict[stest]
264862148f4SScott Kruger         argStr=argStr+("" if 'args' not in sd else sd['args'])
26529921a8fSScott Kruger
26629921a8fSScott Kruger    # Now go through and cleanup
26729921a8fSScott Kruger    argStr=re.sub('{{(.*?)}}',"",argStr)
26829921a8fSScott Kruger    argStr=re.sub('-'," ",argStr)
26929921a8fSScott Kruger    for digit in string.digits: argStr=re.sub(digit," ",argStr)
27029921a8fSScott Kruger    argStr=re.sub("\.","",argStr)
27129921a8fSScott Kruger    argStr=re.sub(",","",argStr)
27229921a8fSScott Kruger    argStr=re.sub('\+',' ',argStr)
27329921a8fSScott Kruger    argStr=re.sub(' +',' ',argStr)  # Remove repeated white space
27429921a8fSScott Kruger    return argStr.strip()
27529921a8fSScott Kruger
27631ad0a39SScott Kruger  def addToSources(self,exfile,rpath,srcDict):
27729921a8fSScott Kruger    """
27829921a8fSScott Kruger      Put into data structure that allows easy generation of makefile
27929921a8fSScott Kruger    """
28094666443SJed Brown    pkg=rpath.split(os.path.sep)[0]
281e551db17SScott Kruger    relpfile=os.path.join(rpath,exfile)
28229921a8fSScott Kruger    lang=self.getLanguage(exfile)
2830aac2865SBarry Smith    if not lang: return
28429921a8fSScott Kruger    self.sources[pkg][lang]['srcs'].append(relpfile)
285fa9d32b8SSatish Balay    self.sources[pkg][lang][relpfile] = []
286862148f4SScott Kruger    if 'depends' in srcDict:
287fa9d32b8SSatish Balay      depSrcList=srcDict['depends'].split()
288fa9d32b8SSatish Balay      for depSrc in depSrcList:
28929921a8fSScott Kruger        depObj=os.path.splitext(depSrc)[0]+".o"
290fa9d32b8SSatish Balay        self.sources[pkg][lang][relpfile].append(os.path.join(rpath,depObj))
29129921a8fSScott Kruger
29229921a8fSScott Kruger    # In gmakefile, ${TESTDIR} var specifies the object compilation
29331ad0a39SScott Kruger    testsdir=rpath+"/"
29429921a8fSScott Kruger    objfile="${TESTDIR}/"+testsdir+os.path.splitext(exfile)[0]+".o"
29529921a8fSScott Kruger    self.objects[pkg].append(objfile)
29629921a8fSScott Kruger    return
29729921a8fSScott Kruger
29831ad0a39SScott Kruger  def addToTests(self,test,rpath,exfile,execname,testDict):
29929921a8fSScott Kruger    """
30029921a8fSScott Kruger      Put into data structure that allows easy generation of makefile
30129921a8fSScott Kruger      Organized by languages to allow testing of languages
30229921a8fSScott Kruger    """
30394666443SJed Brown    pkg=rpath.split("/")[0]
30429921a8fSScott Kruger    nmtest=os.path.join(rpath,test)
30529921a8fSScott Kruger    lang=self.getLanguage(exfile)
3060aac2865SBarry Smith    if not lang: return
30729921a8fSScott Kruger    self.tests[pkg][lang][nmtest]={}
30829921a8fSScott Kruger    self.tests[pkg][lang][nmtest]['exfile']=os.path.join(rpath,exfile)
30929921a8fSScott Kruger    self.tests[pkg][lang][nmtest]['exec']=execname
31029921a8fSScott Kruger    self.tests[pkg][lang][nmtest]['argLabel']=self.getArgLabel(testDict)
31129921a8fSScott Kruger    return
31229921a8fSScott Kruger
31331ad0a39SScott Kruger  def getExecname(self,exfile,rpath):
31429921a8fSScott Kruger    """
31529921a8fSScott Kruger      Generate bash script using template found next to this file.
31629921a8fSScott Kruger      This file is read in at constructor time to avoid file I/O
31729921a8fSScott Kruger    """
31829921a8fSScott Kruger    if self.single_ex:
31929921a8fSScott Kruger      execname=rpath.split("/")[1]+"-ex"
32029921a8fSScott Kruger    else:
32129921a8fSScott Kruger      execname=os.path.splitext(exfile)[0]
32229921a8fSScott Kruger    return execname
32329921a8fSScott Kruger
32429921a8fSScott Kruger  def getSubstVars(self,testDict,rpath,testname):
32529921a8fSScott Kruger    """
32629921a8fSScott Kruger      Create a dictionary with all of the variables that get substituted
32729921a8fSScott Kruger      into the template commands found in example_template.py
32829921a8fSScott Kruger    """
32929921a8fSScott Kruger    subst={}
33096d5c3b5SScott Kruger
33196d5c3b5SScott Kruger    # Handle defaults of testparse.acceptedkeys (e.g., ignores subtests)
332*6a044f4fSJed Brown    if 'nsize' not in testDict: testDict['nsize'] = '1'
3330a091e3eSScott Kruger    if 'timeoutfactor' not in testDict: testDict['timeoutfactor']="1"
33496d5c3b5SScott Kruger    for ak in testparse.acceptedkeys:
33596d5c3b5SScott Kruger      if ak=='test': continue
336862148f4SScott Kruger      subst[ak]=(testDict[ak] if ak in testDict else '')
33796d5c3b5SScott Kruger
33896d5c3b5SScott Kruger    # Now do other variables
33996d5c3b5SScott Kruger    subst['execname']=testDict['execname']
340862148f4SScott Kruger    if 'filter' in testDict:
34196d5c3b5SScott Kruger      subst['filter']="'"+testDict['filter']+"'"   # Quotes are tricky - overwrite
34268a9e459SScott Kruger
34396d5c3b5SScott Kruger    # Others
34496d5c3b5SScott Kruger    subst['subargs']=''  # Default.  For variables override
34594666443SJed Brown    subst['srcdir']=os.path.join(os.path.dirname(self.srcdir), 'src', rpath)
34668a9e459SScott Kruger    subst['label_suffix']=''
34796d5c3b5SScott Kruger    subst['comments']="\n#".join(subst['comments'].split("\n"))
34868a9e459SScott Kruger    if subst['comments']: subst['comments']="#"+subst['comments']
34996d5c3b5SScott Kruger    subst['exec']="../"+subst['execname']
35029921a8fSScott Kruger    subst['testroot']=self.testroot_dir
35129921a8fSScott Kruger    subst['testname']=testname
35209cf0baaSJed Brown    dp = self.conf.get('DATAFILESPATH','')
35309cf0baaSJed Brown    subst['datafilespath_line'] = 'DATAFILESPATH=${DATAFILESPATH:-"'+dp+'"}'
35429921a8fSScott Kruger
3550bcc1aabSScott Kruger    # This is used to label some matrices
3560bcc1aabSScott Kruger    subst['petsc_index_size']=str(self.conf['PETSC_INDEX_SIZE'])
3570bcc1aabSScott Kruger    subst['petsc_scalar_size']=str(self.conf['PETSC_SCALAR_SIZE'])
3580bcc1aabSScott Kruger
35929921a8fSScott Kruger    #Conf vars
36026646c0bSSatish Balay    if self.petsc_arch.find('valgrind')>=0:
36126646c0bSSatish Balay      subst['mpiexec']='petsc_mpiexec_valgrind ' + self.conf['MPIEXEC']
36226646c0bSSatish Balay    else:
36326646c0bSSatish Balay      subst['mpiexec']=self.conf['MPIEXEC']
3644c8d737cSSatish Balay    subst['petsc_dir']=self.petsc_dir # not self.conf['PETSC_DIR'] as this could be windows path
3650a091e3eSScott Kruger    subst['petsc_arch']=self.petsc_arch
3668e69c5ecSJed Brown    if self.inInstallDir:
3678e69c5ecSJed Brown      # Case 2
3688e69c5ecSJed Brown      subst['CONFIG_DIR']=os.path.join(os.path.dirname(self.srcdir),'config')
3698e69c5ecSJed Brown    else:
3704ff3c6a1SScott Kruger      # Case 1
371e551db17SScott Kruger      subst['CONFIG_DIR']=os.path.join(self.petsc_dir,'config')
372c3a89c15SBarry Smith    subst['PETSC_BINDIR']=os.path.join(self.petsc_dir,'lib','petsc','bin')
37329921a8fSScott Kruger    subst['diff']=self.conf['DIFF']
37429921a8fSScott Kruger    subst['rm']=self.conf['RM']
37529921a8fSScott Kruger    subst['grep']=self.conf['GREP']
376d6f00007SSatish Balay    subst['petsc_lib_dir']=self.conf['PETSC_LIB_DIR']
3770eb9b082SSatish Balay    subst['wpetsc_dir']=self.conf['wPETSC_DIR']
37829921a8fSScott Kruger
3793bcca444SScott Kruger    # Output file is special because of subtests override
3803bcca444SScott Kruger    defroot=(re.sub("run","",testname) if testname.startswith("run") else testname)
3813bcca444SScott Kruger    if not "_" in defroot: defroot=defroot+"_1"
3823bcca444SScott Kruger    subst['defroot']=defroot
38394666443SJed Brown    subst['label']=self.nameSpace(defroot,self.srcrelpath(subst['srcdir']))
3843bcca444SScott Kruger    subst['redirect_file']=defroot+".tmp"
385862148f4SScott Kruger    if 'output_file' not in testDict:
3863bcca444SScott Kruger      subst['output_file']="output/"+defroot+".out"
3873bcca444SScott Kruger    # Add in the full path here.
3883bcca444SScott Kruger    subst['output_file']=os.path.join(subst['srcdir'],subst['output_file'])
3893bcca444SScott Kruger    if not os.path.isfile(os.path.join(self.petsc_dir,subst['output_file'])):
3903bcca444SScott Kruger      if not subst['TODO']:
3915b6bfdb9SJed Brown        print("Warning: "+subst['output_file']+" not found.")
3923bcca444SScott Kruger    # Worry about alt files here -- see
3933bcca444SScott Kruger    #   src/snes/examples/tutorials/output/ex22*.out
3943bcca444SScott Kruger    altlist=[subst['output_file']]
3959af0da4bSSatish Balay    basefile,ext = os.path.splitext(subst['output_file'])
396940ae1cbSBarry Smith    for i in range(1,9):
3979af0da4bSSatish Balay      altroot=basefile+"_alt"
398940ae1cbSBarry Smith      if i > 1: altroot=altroot+"_"+str(i)
3999af0da4bSSatish Balay      af=altroot+".out"
4003bcca444SScott Kruger      srcaf=os.path.join(subst['srcdir'],af)
4013bcca444SScott Kruger      fullaf=os.path.join(self.petsc_dir,srcaf)
4023bcca444SScott Kruger      if os.path.isfile(fullaf): altlist.append(srcaf)
4033bcca444SScott Kruger    if len(altlist)>1: subst['altfiles']=altlist
40431ad0a39SScott Kruger    #if len(altlist)>1: print("Found alt files: ",altlist)
40531ad0a39SScott Kruger
40631ad0a39SScott Kruger    subst['regexes']={}
40731ad0a39SScott Kruger    for subkey in subst:
40831ad0a39SScott Kruger      if subkey=='regexes': continue
40931ad0a39SScott Kruger      if not isinstance(subst[subkey],str): continue
41031ad0a39SScott Kruger      patt="@"+subkey.upper()+"@"
41131ad0a39SScott Kruger      subst['regexes'][subkey]=re.compile(patt)
4123bcca444SScott Kruger
41329921a8fSScott Kruger    return subst
41429921a8fSScott Kruger
41531ad0a39SScott Kruger  def _substVars(self,subst,origStr):
41631ad0a39SScott Kruger    """
41731ad0a39SScott Kruger      Substitute variables
41831ad0a39SScott Kruger    """
41931ad0a39SScott Kruger    Str=origStr
42031ad0a39SScott Kruger    for subkey in subst:
42131ad0a39SScott Kruger      if subkey=='regexes': continue
42231ad0a39SScott Kruger      if not isinstance(subst[subkey],str): continue
42331ad0a39SScott Kruger      if subkey.upper() not in Str: continue
42431ad0a39SScott Kruger      Str=subst['regexes'][subkey].sub(subst[subkey],Str)
42531ad0a39SScott Kruger    return Str
42631ad0a39SScott Kruger
4270357d61fSScott Kruger  def getCmds(self,subst,i):
42829921a8fSScott Kruger    """
42929921a8fSScott Kruger      Generate bash script using template found next to this file.
43029921a8fSScott Kruger      This file is read in at constructor time to avoid file I/O
43129921a8fSScott Kruger    """
4320357d61fSScott Kruger    nindnt=i # the start and has to be consistent with below
4337a853109SScott Kruger    cmdindnt=self.indent*nindnt
43429921a8fSScott Kruger    cmdLines=""
43568a9e459SScott Kruger
43629921a8fSScott Kruger    # MPI is the default -- but we have a few odd commands
43796d5c3b5SScott Kruger    if not subst['command']:
4387a853109SScott Kruger      cmd=cmdindnt+self._substVars(subst,example_template.mpitest)
43929921a8fSScott Kruger    else:
4407a853109SScott Kruger      cmd=cmdindnt+self._substVars(subst,example_template.commandtest)
4417a853109SScott Kruger    cmdLines+=cmd+"\n"+cmdindnt+"res=$?\n\n"
44229921a8fSScott Kruger
4437a853109SScott Kruger    cmdLines+=cmdindnt+'if test $res = 0; then\n'
4447a853109SScott Kruger    diffindnt=self.indent*(nindnt+1)
44564ca018dSScott Kruger    if not subst['filter_output']:
446862148f4SScott Kruger      if 'altfiles' not in subst:
4477a853109SScott Kruger        cmd=diffindnt+self._substVars(subst,example_template.difftest)
44864ca018dSScott Kruger      else:
4493bcca444SScott Kruger        # Have to do it by hand a bit because of variable number of alt files
4503bcca444SScott Kruger        rf=subst['redirect_file']
4517a853109SScott Kruger        cmd=diffindnt+example_template.difftest.split('@')[0]
4523bcca444SScott Kruger        for i in range(len(subst['altfiles'])):
4533bcca444SScott Kruger          af=subst['altfiles'][i]
454886063b6SScott Kruger          cmd+=af+' '+rf
4553bcca444SScott Kruger          if i!=len(subst['altfiles'])-1:
456886063b6SScott Kruger            cmd+=' > diff-${testname}-'+str(i)+'.out 2> diff-${testname}-'+str(i)+'.out'
457886063b6SScott Kruger            cmd+=' || ${diff_exe} '
4583bcca444SScott Kruger          else:
4593bcca444SScott Kruger            cmd+='" diff-${testname}.out diff-${testname}.out diff-${label}'
4603bcca444SScott Kruger            cmd+=subst['label_suffix']+' ""'  # Quotes are painful
4613bcca444SScott Kruger    else:
4627a853109SScott Kruger      cmd=diffindnt+self._substVars(subst,example_template.filterdifftest)
46368a9e459SScott Kruger    cmdLines+=cmd+"\n"
4647a853109SScott Kruger    cmdLines+=cmdindnt+'else\n'
4657a853109SScott Kruger    cmdLines+=diffindnt+'printf "ok ${label} # SKIP Command failed so no diff\\n"\n'
4667a853109SScott Kruger    cmdLines+=cmdindnt+'fi\n'
46729921a8fSScott Kruger    return cmdLines
46829921a8fSScott Kruger
46909cf0baaSJed Brown  def _writeTodoSkip(self,fh,tors,reasons,footer):
47068a9e459SScott Kruger    """
47168a9e459SScott Kruger    Write out the TODO and SKIP lines in the file
47268a9e459SScott Kruger    The TODO or SKIP variable, tors, should be lower case
47368a9e459SScott Kruger    """
47468a9e459SScott Kruger    TORS=tors.upper()
47568a9e459SScott Kruger    template=eval("example_template."+tors+"line")
47609cf0baaSJed Brown    tsStr=re.sub("@"+TORS+"COMMENT@",', '.join(reasons),template)
47709cf0baaSJed Brown    tab = ''
4782f2809e3SToby Isaac    if reasons:
4792f2809e3SToby Isaac      fh.write('if ! $force; then\n')
4802f2809e3SToby Isaac      tab = tab + '    '
48109cf0baaSJed Brown    if reasons == ["Requires DATAFILESPATH"]:
48209cf0baaSJed Brown      # The only reason not to run is DATAFILESPATH, which we check at run-time
4832f2809e3SToby Isaac      fh.write(tab + 'if test -z "${DATAFILESPATH}"; then\n')
4842f2809e3SToby Isaac      tab = tab + '    '
48509cf0baaSJed Brown    if reasons:
4862f2809e3SToby Isaac      fh.write(tab+tsStr+"\n" + tab + "total=1; "+tors+"=1\n")
48709cf0baaSJed Brown      fh.write(tab+footer+"\n")
48809cf0baaSJed Brown      fh.write(tab+"exit\n")
48909cf0baaSJed Brown    if reasons == ["Requires DATAFILESPATH"]:
49009cf0baaSJed Brown      fh.write('    fi\n')
4912f2809e3SToby Isaac    if reasons:
4922f2809e3SToby Isaac      fh.write('fi\n')
49309cf0baaSJed Brown    fh.write('\n\n')
49468a9e459SScott Kruger    return
49568a9e459SScott Kruger
4968ba3acebSToby Isaac  def getLoopVarsHead(self,loopVars,i):
4970357d61fSScott Kruger    """
4980357d61fSScott Kruger    Generate a nicely indented string with the format loops
4990357d61fSScott Kruger    Here is what the data structure looks like
5000357d61fSScott Kruger      loopVars['subargs']['varlist']=['bs' 'pc_type']   # Don't worry about OrderedDict
5010357d61fSScott Kruger      loopVars['subargs']['bs']=["i","1 2 3 4 5"]
5020357d61fSScott Kruger      loopVars['subargs']['pc_type']=["j","cholesky sor"]
5030357d61fSScott Kruger    """
5040357d61fSScott Kruger    outstr=''; indnt=self.indent
5058ba3acebSToby Isaac    for key in loopVars:
5060357d61fSScott Kruger      for var in loopVars[key]['varlist']:
5070357d61fSScott Kruger        varval=loopVars[key][var]
508*6a044f4fSJed Brown        outstr += indnt * i + "for {0} in ${{{0}:-{1}}}; do\n".format(*varval)
5098ba3acebSToby Isaac        i = i + 1
5108ba3acebSToby Isaac    return (outstr,i)
5118ba3acebSToby Isaac
5128ba3acebSToby Isaac  def getLoopVarsFoot(self,loopVars,i):
5130357d61fSScott Kruger    outstr=''; indnt=self.indent
5148ba3acebSToby Isaac    for key in loopVars:
5150357d61fSScott Kruger      for var in loopVars[key]['varlist']:
5168ba3acebSToby Isaac        i = i - 1
5170357d61fSScott Kruger        outstr += indnt * i + "done\n"
5188ba3acebSToby Isaac    return (outstr,i)
5198ba3acebSToby Isaac
52029921a8fSScott Kruger  def genRunScript(self,testname,root,isRun,srcDict):
52129921a8fSScott Kruger    """
52229921a8fSScott Kruger      Generate bash script using template found next to this file.
52329921a8fSScott Kruger      This file is read in at constructor time to avoid file I/O
52429921a8fSScott Kruger    """
52529921a8fSScott Kruger    # runscript_dir directory has to be consistent with gmakefile
52629921a8fSScott Kruger    testDict=srcDict[testname]
527c173c275SScott Kruger    rpath=self.srcrelpath(root)
52829921a8fSScott Kruger    runscript_dir=os.path.join(self.testroot_dir,rpath)
52929921a8fSScott Kruger    if not os.path.isdir(runscript_dir): os.makedirs(runscript_dir)
53029921a8fSScott Kruger    fh=open(os.path.join(runscript_dir,testname+".sh"),"w")
53129921a8fSScott Kruger
53296d5c3b5SScott Kruger    # Get variables to go into shell scripts.  last time testDict used
53329921a8fSScott Kruger    subst=self.getSubstVars(testDict,rpath,testname)
53496d5c3b5SScott Kruger    loopVars = self._getLoopVars(subst,testname)  # Alters subst as well
53529921a8fSScott Kruger
53664ca018dSScott Kruger    #Handle runfiles
537862148f4SScott Kruger    for lfile in subst.get('localrunfiles','').split():
538ed81ced6SJed Brown      install_files(os.path.join(root, lfile),
539ed81ced6SJed Brown                    os.path.join(runscript_dir, os.path.dirname(lfile)))
54064ca018dSScott Kruger    # Check subtests for local runfiles
541862148f4SScott Kruger    for stest in subst.get("subtests",[]):
542862148f4SScott Kruger      for lfile in testDict[stest].get('localrunfiles','').split():
543ed81ced6SJed Brown        install_files(os.path.join(root, lfile),
544ed81ced6SJed Brown                      os.path.join(runscript_dir, os.path.dirname(lfile)))
54564ca018dSScott Kruger
54629921a8fSScott Kruger    # Now substitute the key variables into the header and footer
54729921a8fSScott Kruger    header=self._substVars(subst,example_template.header)
5480bcc1aabSScott Kruger    # The header is done twice to enable @...@ in header
5490bcc1aabSScott Kruger    header=self._substVars(subst,header)
5505e7f8670SScott Kruger    footer=re.sub('@TESTROOT@',subst['testroot'],example_template.footer)
55129921a8fSScott Kruger
55229921a8fSScott Kruger    # Start writing the file
55329921a8fSScott Kruger    fh.write(header+"\n")
55429921a8fSScott Kruger
55529921a8fSScott Kruger    # If there is a TODO or a SKIP then we do it before writing out the
55629921a8fSScott Kruger    # rest of the command (which is useful for working on the test)
55729921a8fSScott Kruger    # SKIP and TODO can be for the source file or for the runs
55809cf0baaSJed Brown    self._writeTodoSkip(fh,'todo',[s for s in [srcDict.get('TODO',''), testDict.get('TODO','')] if s],footer)
55909cf0baaSJed Brown    self._writeTodoSkip(fh,'skip',srcDict.get('SKIP',[]) + testDict.get('SKIP',[]),footer)
56029921a8fSScott Kruger
56129921a8fSScott Kruger    j=0  # for indentation
56229921a8fSScott Kruger
5630357d61fSScott Kruger    if loopVars:
5648ba3acebSToby Isaac      (loopHead,j) = self.getLoopVarsHead(loopVars,j)
5658ba3acebSToby Isaac      if (loopHead): fh.write(loopHead+"\n")
56629921a8fSScott Kruger
56729921a8fSScott Kruger    # Subtests are special
568862148f4SScott Kruger    if 'subtests' in testDict:
56929921a8fSScott Kruger      substP=subst   # Subtests can inherit args but be careful
5704fedfc52SScott Kruger      k=0  # for label suffixes
57129921a8fSScott Kruger      for stest in testDict["subtests"]:
5721e4ea733SToby Isaac        subst=substP.copy()
5730357d61fSScott Kruger        subst.update(testDict[stest])
5744fedfc52SScott Kruger        subst['label_suffix']='-'+string.ascii_letters[k]; k+=1
5757827e0d6SScott Kruger        sLoopVars = self._getLoopVars(subst,testname,isSubtest=True)
5760357d61fSScott Kruger        if sLoopVars:
5778ba3acebSToby Isaac          (sLoopHead,j) = self.getLoopVarsHead(sLoopVars,j)
5780357d61fSScott Kruger          fh.write(sLoopHead+"\n")
5790357d61fSScott Kruger        fh.write(self.getCmds(subst,j)+"\n")
5800357d61fSScott Kruger        if sLoopVars:
5818ba3acebSToby Isaac          (sLoopFoot,j) = self.getLoopVarsFoot(sLoopVars,j)
5820357d61fSScott Kruger          fh.write(sLoopFoot+"\n")
58329921a8fSScott Kruger    else:
58429921a8fSScott Kruger      fh.write(self.getCmds(subst,j)+"\n")
5858ba3acebSToby Isaac
5860357d61fSScott Kruger    if loopVars:
5878ba3acebSToby Isaac      (loopFoot,j) = self.getLoopVarsFoot(loopVars,j)
5880357d61fSScott Kruger      fh.write(loopFoot+"\n")
58929921a8fSScott Kruger
59029921a8fSScott Kruger    fh.write(footer+"\n")
5915b6bfdb9SJed Brown    os.chmod(os.path.join(runscript_dir,testname+".sh"),0o755)
5927827e0d6SScott Kruger    #if '10_9' in testname: sys.exit()
59329921a8fSScott Kruger    return
59429921a8fSScott Kruger
59529921a8fSScott Kruger  def  genScriptsAndInfo(self,exfile,root,srcDict):
59629921a8fSScott Kruger    """
59729921a8fSScott Kruger    Generate scripts from the source file, determine if built, etc.
59829921a8fSScott Kruger     For every test in the exfile with info in the srcDict:
59929921a8fSScott Kruger      1. Determine if it needs to be run for this arch
60029921a8fSScott Kruger      2. Generate the script
60129921a8fSScott Kruger      3. Generate the data needed to write out the makefile in a
60229921a8fSScott Kruger         convenient way
60329921a8fSScott Kruger     All tests are *always* run, but some may be SKIP'd per the TAP standard
60429921a8fSScott Kruger    """
60529921a8fSScott Kruger    debug=False
60631ad0a39SScott Kruger    rpath=self.srcrelpath(root)
60731ad0a39SScott Kruger    execname=self.getExecname(exfile,rpath)
60829921a8fSScott Kruger    isBuilt=self._isBuilt(exfile,srcDict)
60929921a8fSScott Kruger    for test in srcDict:
61029921a8fSScott Kruger      if test in self.buildkeys: continue
6115b6bfdb9SJed Brown      if debug: print(self.nameSpace(exfile,root), test)
61229921a8fSScott Kruger      srcDict[test]['execname']=execname   # Convenience in generating scripts
61329921a8fSScott Kruger      isRun=self._isRun(srcDict[test])
61429921a8fSScott Kruger      self.genRunScript(test,root,isRun,srcDict)
61529921a8fSScott Kruger      srcDict[test]['isrun']=isRun
61631ad0a39SScott Kruger      self.addToTests(test,rpath,exfile,execname,srcDict[test])
61729921a8fSScott Kruger
61829921a8fSScott Kruger    # This adds to datastructure for building deps
61931ad0a39SScott Kruger    if isBuilt: self.addToSources(exfile,rpath,srcDict)
62029921a8fSScott Kruger    return
62129921a8fSScott Kruger
62229921a8fSScott Kruger  def _isBuilt(self,exfile,srcDict):
62329921a8fSScott Kruger    """
62429921a8fSScott Kruger    Determine if this file should be built.
62529921a8fSScott Kruger    """
62629921a8fSScott Kruger    # Get the language based on file extension
62709cf0baaSJed Brown    srcDict['SKIP'] = []
62829921a8fSScott Kruger    lang=self.getLanguage(exfile)
629c2426ab2SScott Kruger    if (lang=="F" or lang=="F90"):
630c2426ab2SScott Kruger      if not self.have_fortran:
63109cf0baaSJed Brown        srcDict["SKIP"].append("Fortran required for this test")
632c2426ab2SScott Kruger      elif lang=="F90" and 'PETSC_USING_F90FREEFORM' not in self.conf:
633c2426ab2SScott Kruger        srcDict["SKIP"].append("Fortran f90freeform required for this test")
634862148f4SScott Kruger    if lang=="cu" and 'PETSC_HAVE_CUDA' not in self.conf:
63509cf0baaSJed Brown      srcDict["SKIP"].append("CUDA required for this test")
636862148f4SScott Kruger    if lang=="cxx" and 'PETSC_HAVE_CXX' not in self.conf:
63709cf0baaSJed Brown      srcDict["SKIP"].append("C++ required for this test")
63829921a8fSScott Kruger
63929921a8fSScott Kruger    # Deprecated source files
64009cf0baaSJed Brown    if srcDict.get("TODO"):
64109cf0baaSJed Brown      return False
64229921a8fSScott Kruger
64329921a8fSScott Kruger    # isRun can work with srcDict to handle the requires
644862148f4SScott Kruger    if "requires" in srcDict:
645cadd188bSScott Kruger      if srcDict["requires"]:
64629921a8fSScott Kruger        return self._isRun(srcDict)
64729921a8fSScott Kruger
64809cf0baaSJed Brown    return srcDict['SKIP'] == []
64929921a8fSScott Kruger
65029921a8fSScott Kruger
651e4653983SScott Kruger  def _isRun(self,testDict, debug=False):
65229921a8fSScott Kruger    """
65329921a8fSScott Kruger    Based on the requirements listed in the src file and the petscconf.h
65429921a8fSScott Kruger    info, determine whether this test should be run or not.
65529921a8fSScott Kruger    """
65629921a8fSScott Kruger    indent="  "
65729921a8fSScott Kruger
65809cf0baaSJed Brown    if 'SKIP' not in testDict:
65909cf0baaSJed Brown      testDict['SKIP'] = []
66029921a8fSScott Kruger    # MPI requirements
6615b08e527SSatish Balay    if 'MPI_IS_MPIUNI' in self.conf:
662*6a044f4fSJed Brown      if testDict.get('nsize', '1') != '1':
66309cf0baaSJed Brown        testDict['SKIP'].append("Parallel test with serial build")
66429921a8fSScott Kruger
66529921a8fSScott Kruger      # The requirements for the test are the sum of all the run subtests
666862148f4SScott Kruger      if 'subtests' in testDict:
667862148f4SScott Kruger        if 'requires' not in testDict: testDict['requires']=""
66829921a8fSScott Kruger        for stest in testDict['subtests']:
669862148f4SScott Kruger          if 'requires' in testDict[stest]:
670862148f4SScott Kruger            testDict['requires']+=" "+testDict[stest]['requires']
671*6a044f4fSJed Brown          if testDict.get('nsize', '1') != '1':
67273de2b55SScott Kruger            testDict['SKIP'].append("Parallel test with serial build")
6735b08e527SSatish Balay            break
67429921a8fSScott Kruger
67529921a8fSScott Kruger    # Now go through all requirements
676862148f4SScott Kruger    if 'requires' in testDict:
67729921a8fSScott Kruger      for requirement in testDict['requires'].split():
67829921a8fSScott Kruger        requirement=requirement.strip()
67929921a8fSScott Kruger        if not requirement: continue
6805b6bfdb9SJed Brown        if debug: print(indent+"Requirement: ", requirement)
68129921a8fSScott Kruger        isNull=False
68229921a8fSScott Kruger        if requirement.startswith("!"):
68329921a8fSScott Kruger          requirement=requirement[1:]; isNull=True
68429921a8fSScott Kruger        # Precision requirement for reals
68529921a8fSScott Kruger        if requirement in self.precision_types:
68629921a8fSScott Kruger          if self.conf['PETSC_PRECISION']==requirement:
68709cf0baaSJed Brown            if isNull:
68809cf0baaSJed Brown              testDict['SKIP'].append("not "+requirement+" required")
68909cf0baaSJed Brown              continue
6904bb4d03dSScott Kruger            continue  # Success
69109cf0baaSJed Brown          elif not isNull:
69209cf0baaSJed Brown            testDict['SKIP'].append(requirement+" required")
69309cf0baaSJed Brown            continue
69429921a8fSScott Kruger        # Precision requirement for ints
69529921a8fSScott Kruger        if requirement in self.integer_types:
69629921a8fSScott Kruger          if requirement=="int32":
69729921a8fSScott Kruger            if self.conf['PETSC_SIZEOF_INT']==4:
69809cf0baaSJed Brown              if isNull:
69909cf0baaSJed Brown                testDict['SKIP'].append("not int32 required")
70009cf0baaSJed Brown                continue
7014bb4d03dSScott Kruger              continue  # Success
70209cf0baaSJed Brown            elif not isNull:
70309cf0baaSJed Brown              testDict['SKIP'].append("int32 required")
70409cf0baaSJed Brown              continue
70529921a8fSScott Kruger          if requirement=="int64":
70629921a8fSScott Kruger            if self.conf['PETSC_SIZEOF_INT']==8:
70709cf0baaSJed Brown              if isNull:
70809cf0baaSJed Brown                testDict['SKIP'].append("NOT int64 required")
70909cf0baaSJed Brown                continue
7104bb4d03dSScott Kruger              continue  # Success
71109cf0baaSJed Brown            elif not isNull:
71209cf0baaSJed Brown              testDict['SKIP'].append("int64 required")
71309cf0baaSJed Brown              continue
71429921a8fSScott Kruger        # Datafilespath
71509cf0baaSJed Brown        if requirement=="datafilespath" and not isNull:
71609cf0baaSJed Brown          testDict['SKIP'].append("Requires DATAFILESPATH")
71709cf0baaSJed Brown          continue
71829921a8fSScott Kruger        # Defines -- not sure I have comments matching
7198304fa3fSScott Kruger        if "define(" in requirement.lower():
72029921a8fSScott Kruger          reqdef=requirement.split("(")[1].split(")")[0]
72109cf0baaSJed Brown          if reqdef in self.conf:
72229921a8fSScott Kruger            if isNull:
72309cf0baaSJed Brown              testDict['SKIP'].append("Null requirement not met: "+requirement)
72409cf0baaSJed Brown              continue
7254bb4d03dSScott Kruger            continue  # Success
7264bb4d03dSScott Kruger          elif not isNull:
72709cf0baaSJed Brown            testDict['SKIP'].append("Required: "+requirement)
72809cf0baaSJed Brown            continue
72929921a8fSScott Kruger
73029921a8fSScott Kruger        # Rest should be packages that we can just get from conf
73109cf0baaSJed Brown        if requirement == "complex":
73209cf0baaSJed Brown          petscconfvar="PETSC_USE_COMPLEX"
73309cf0baaSJed Brown        else:
73409cf0baaSJed Brown          petscconfvar="PETSC_HAVE_"+requirement.upper()
73529921a8fSScott Kruger        if self.conf.get(petscconfvar):
73629921a8fSScott Kruger          if isNull:
73709cf0baaSJed Brown            testDict['SKIP'].append("Not "+petscconfvar+" requirement not met")
73809cf0baaSJed Brown            continue
7394bb4d03dSScott Kruger          continue  # Success
740df3aec83SJed Brown        elif not isNull:
7415b6bfdb9SJed Brown          if debug: print("requirement not found: ", requirement)
74209cf0baaSJed Brown          testDict['SKIP'].append(petscconfvar+" requirement not met")
74309cf0baaSJed Brown          continue
74429921a8fSScott Kruger
74509cf0baaSJed Brown    return testDict['SKIP'] == []
74629921a8fSScott Kruger
74729921a8fSScott Kruger  def genPetscTests_summarize(self,dataDict):
74829921a8fSScott Kruger    """
74929921a8fSScott Kruger    Required method to state what happened
75029921a8fSScott Kruger    """
75129921a8fSScott Kruger    if not self.summarize: return
75229921a8fSScott Kruger    indent="   "
753cfaa06beSSatish Balay    fhname=os.path.join(self.testroot_dir,'GenPetscTests_summarize.txt')
75429921a8fSScott Kruger    fh=open(fhname,"w")
75529921a8fSScott Kruger    for root in dataDict:
756c173c275SScott Kruger      relroot=self.srcrelpath(root)
75729921a8fSScott Kruger      pkg=relroot.split("/")[1]
75829921a8fSScott Kruger      fh.write(relroot+"\n")
75929921a8fSScott Kruger      allSrcs=[]
760e551db17SScott Kruger      for lang in LANGS: allSrcs+=self.sources[pkg][lang]['srcs']
76129921a8fSScott Kruger      for exfile in dataDict[root]:
76229921a8fSScott Kruger        # Basic  information
763c173c275SScott Kruger        rfile=os.path.join(relroot,exfile)
76429921a8fSScott Kruger        builtStatus=(" Is built" if rfile in allSrcs else " Is NOT built")
76529921a8fSScott Kruger        fh.write(indent+exfile+indent*4+builtStatus+"\n")
76629921a8fSScott Kruger
76729921a8fSScott Kruger        for test in dataDict[root][exfile]:
76829921a8fSScott Kruger          if test in self.buildkeys: continue
76929921a8fSScott Kruger          line=indent*2+test
77029921a8fSScott Kruger          fh.write(line+"\n")
77129921a8fSScott Kruger          # Looks nice to have the keys in order
77229921a8fSScott Kruger          #for key in dataDict[root][exfile][test]:
77329921a8fSScott Kruger          for key in "isrun abstracted nsize args requires script".split():
774862148f4SScott Kruger            if key not in dataDict[root][exfile][test]: continue
77529921a8fSScott Kruger            line=indent*3+key+": "+str(dataDict[root][exfile][test][key])
77629921a8fSScott Kruger            fh.write(line+"\n")
77729921a8fSScott Kruger          fh.write("\n")
77829921a8fSScott Kruger        fh.write("\n")
77929921a8fSScott Kruger      fh.write("\n")
78029921a8fSScott Kruger    #fh.write("\nClass Sources\n"+str(self.sources)+"\n")
78129921a8fSScott Kruger    #fh.write("\nClass Tests\n"+str(self.tests)+"\n")
78229921a8fSScott Kruger    fh.close()
78329921a8fSScott Kruger    return
78429921a8fSScott Kruger
78529921a8fSScott Kruger  def genPetscTests(self,root,dirs,files,dataDict):
78629921a8fSScott Kruger    """
78729921a8fSScott Kruger     Go through and parse the source files in the directory to generate
78829921a8fSScott Kruger     the examples based on the metadata contained in the source files
78929921a8fSScott Kruger    """
79029921a8fSScott Kruger    debug=False
79129921a8fSScott Kruger    # Use examplesAnalyze to get what the makefles think are sources
79229921a8fSScott Kruger    #self.examplesAnalyze(root,dirs,files,anlzDict)
79329921a8fSScott Kruger
79429921a8fSScott Kruger    dataDict[root]={}
79529921a8fSScott Kruger
79629921a8fSScott Kruger    for exfile in files:
79729921a8fSScott Kruger      #TST: Until we replace files, still leaving the orginals as is
79829921a8fSScott Kruger      #if not exfile.startswith("new_"+"ex"): continue
79962197512SBarry Smith      #if not exfile.startswith("ex"): continue
80029921a8fSScott Kruger
801e4653983SScott Kruger      # Ignore emacs and other temporary files
802e4653983SScott Kruger      if exfile.startswith("."): continue
803e4653983SScott Kruger      if exfile.startswith("#"): continue
8042cde73d3SScott Kruger      if exfile.endswith("~"): continue
80531ad0a39SScott Kruger      # Only parse source files
80631ad0a39SScott Kruger      ext=os.path.splitext(exfile)[-1].lstrip('.')
80731ad0a39SScott Kruger      if ext not in LANGS: continue
80813ddbc6dSScott Kruger
80929921a8fSScott Kruger      # Convenience
81029921a8fSScott Kruger      fullex=os.path.join(root,exfile)
811cadd188bSScott Kruger      if self.verbose: print('   --> '+fullex)
81296d5c3b5SScott Kruger      dataDict[root].update(testparse.parseTestFile(fullex,0))
813862148f4SScott Kruger      if exfile in dataDict[root]:
81429921a8fSScott Kruger        self.genScriptsAndInfo(exfile,root,dataDict[root][exfile])
81529921a8fSScott Kruger
81629921a8fSScott Kruger    return
81729921a8fSScott Kruger
818cadd188bSScott Kruger  def walktree(self,top):
81929921a8fSScott Kruger    """
82029921a8fSScott Kruger    Walk a directory tree, starting from 'top'
82129921a8fSScott Kruger    """
82229921a8fSScott Kruger    # Goal of action is to fill this dictionary
82329921a8fSScott Kruger    dataDict={}
824f98eef70SScott Kruger    for root, dirs, files in os.walk(top, topdown=True):
82509a6cbfcSBernhard M. Wiedemann      dirs.sort()
82609a6cbfcSBernhard M. Wiedemann      files.sort()
82729921a8fSScott Kruger      if not "examples" in root: continue
828f98eef70SScott Kruger      if "dSYM" in root: continue
829f98eef70SScott Kruger      if os.path.basename(root.rstrip("/")) == 'output': continue
830cadd188bSScott Kruger      if self.verbose: print(root)
831b6d69c80SScott Kruger      self.genPetscTests(root,dirs,files,dataDict)
83229921a8fSScott Kruger    # Now summarize this dictionary
83331ad0a39SScott Kruger    if self.verbose: self.genPetscTests_summarize(dataDict)
83429921a8fSScott Kruger    return dataDict
83529921a8fSScott Kruger
836b0790570SJed Brown  def gen_gnumake(self, fd):
83729921a8fSScott Kruger    """
83829921a8fSScott Kruger     Overwrite of the method in the base PETSc class
83929921a8fSScott Kruger    """
84029921a8fSScott Kruger    def write(stem, srcs):
84129921a8fSScott Kruger      for lang in LANGS:
8422ae8c56aSScott Kruger        if srcs[lang]['srcs']:
84329921a8fSScott Kruger          fd.write('%(stem)s.%(lang)s := %(srcs)s\n' % dict(stem=stem, lang=lang, srcs=' '.join(srcs[lang]['srcs'])))
84429921a8fSScott Kruger    for pkg in PKGS:
84529921a8fSScott Kruger        srcs = self.gen_pkg(pkg)
846b0790570SJed Brown        write('testsrcs-' + pkg, srcs)
8472ae8c56aSScott Kruger        # Handle dependencies
8482ae8c56aSScott Kruger        for lang in LANGS:
8492ae8c56aSScott Kruger            for exfile in srcs[lang]['srcs']:
8502ae8c56aSScott Kruger                if exfile in srcs[lang]:
8512ae8c56aSScott Kruger                    ex='$(TESTDIR)/'+os.path.splitext(exfile)[0]
8522ae8c56aSScott Kruger                    exfo='$(TESTDIR)/'+os.path.splitext(exfile)[0]+'.o'
85349defe6fSJed Brown                    deps = [os.path.join('$(TESTDIR)', dep) for dep in srcs[lang][exfile]]
85449defe6fSJed Brown                    if deps:
85549defe6fSJed Brown                        # The executable literally depends on the object file because it is linked
85649defe6fSJed Brown                        fd.write(ex   +": " + " ".join(deps) +'\n')
85749defe6fSJed Brown                        # The object file containing 'main' does not normally depend on other object
85849defe6fSJed Brown                        # files, but it does when it includes their modules.  This dependency is
85949defe6fSJed Brown                        # overly blunt and could be reduced to only depend on object files for
86049defe6fSJed Brown                        # modules that are used, like "*f90aux.o".
86149defe6fSJed Brown                        fd.write(exfo +": " + " ".join(deps) +'\n')
8622ae8c56aSScott Kruger
86329921a8fSScott Kruger    return self.gendeps
86429921a8fSScott Kruger
86529921a8fSScott Kruger  def gen_pkg(self, pkg):
86629921a8fSScott Kruger    """
86729921a8fSScott Kruger     Overwrite of the method in the base PETSc class
86829921a8fSScott Kruger    """
86929921a8fSScott Kruger    return self.sources[pkg]
87029921a8fSScott Kruger
8718e69c5ecSJed Brown  def write_gnumake(self, dataDict, output=None):
87229921a8fSScott Kruger    """
87329921a8fSScott Kruger     Write out something similar to files from gmakegen.py
87429921a8fSScott Kruger
87529921a8fSScott Kruger     Test depends on script which also depends on source
87629921a8fSScott Kruger     file, but since I don't have a good way generating
87729921a8fSScott Kruger     acting on a single file (oops) just depend on
87829921a8fSScott Kruger     executable which in turn will depend on src file
87929921a8fSScott Kruger    """
88068f6ad6bSScott Kruger    # Different options for how to set up the targets
88168f6ad6bSScott Kruger    compileExecsFirst=False
88268f6ad6bSScott Kruger
88329921a8fSScott Kruger    # Open file
8848e69c5ecSJed Brown    fd = open(output, 'w')
88529921a8fSScott Kruger
88629921a8fSScott Kruger    # Write out the sources
887b0790570SJed Brown    gendeps = self.gen_gnumake(fd)
88829921a8fSScott Kruger
88929921a8fSScott Kruger    # Write out the tests and execname targets
89029921a8fSScott Kruger    fd.write("\n#Tests and executables\n")    # Delimiter
89129921a8fSScott Kruger
89229921a8fSScott Kruger    for pkg in PKGS:
89329921a8fSScott Kruger      # These grab the ones that are built
89429921a8fSScott Kruger      for lang in LANGS:
89585a27222SJed Brown        testdeps=[]
89629921a8fSScott Kruger        for ftest in self.tests[pkg][lang]:
89729921a8fSScott Kruger          test=os.path.basename(ftest)
89829921a8fSScott Kruger          basedir=os.path.dirname(ftest)
89985a27222SJed Brown          testdeps.append(self.nameSpace(test,basedir))
900612eee3eSJed Brown        fd.write("test-"+pkg+"."+lang+" := "+' '.join(testdeps)+"\n")
90165ea9442SJed Brown        fd.write('test-%s.%s : $(test-%s.%s)\n' % (pkg, lang, pkg, lang))
90229921a8fSScott Kruger
90329921a8fSScott Kruger        # test targets
90429921a8fSScott Kruger        for ftest in self.tests[pkg][lang]:
90529921a8fSScott Kruger          test=os.path.basename(ftest)
90629921a8fSScott Kruger          basedir=os.path.dirname(ftest)
90729921a8fSScott Kruger          testdir="${TESTDIR}/"+basedir+"/"
90829921a8fSScott Kruger          nmtest=self.nameSpace(test,basedir)
90929921a8fSScott Kruger          rundir=os.path.join(testdir,test)
91029921a8fSScott Kruger          script=test+".sh"
91129921a8fSScott Kruger
91229921a8fSScott Kruger          # Deps
91329921a8fSScott Kruger          exfile=self.tests[pkg][lang][ftest]['exfile']
91494666443SJed Brown          fullex=os.path.join(self.srcdir,exfile)
91529921a8fSScott Kruger          localexec=self.tests[pkg][lang][ftest]['exec']
91629921a8fSScott Kruger          execname=os.path.join(testdir,localexec)
91768f6ad6bSScott Kruger          fullscript=os.path.join(testdir,script)
91868f6ad6bSScott Kruger          tmpfile=os.path.join(testdir,test,test+".tmp")
91929921a8fSScott Kruger
920b91d4a07SJed Brown          # *.counts depends on the script and either executable (will
921b91d4a07SJed Brown          # be run) or the example source file (SKIP or TODO)
922fa9d32b8SSatish Balay          fd.write('%s.counts : %s %s'
923b91d4a07SJed Brown              % (os.path.join('$(TESTDIR)/counts', nmtest),
924b91d4a07SJed Brown                 fullscript,
9254fedfc52SScott Kruger                 execname if exfile in self.sources[pkg][lang]['srcs'] else fullex)
9264fedfc52SScott Kruger              )
927fa9d32b8SSatish Balay          if exfile in self.sources[pkg][lang]:
928fa9d32b8SSatish Balay            for dep in self.sources[pkg][lang][exfile]:
929fa9d32b8SSatish Balay              fd.write(' %s' % os.path.join('$(TESTDIR)',dep))
930fa9d32b8SSatish Balay          fd.write('\n')
9314fedfc52SScott Kruger
93229921a8fSScott Kruger          # Now write the args:
933612eee3eSJed Brown          fd.write(nmtest+"_ARGS := '"+self.tests[pkg][lang][ftest]['argLabel']+"'\n")
934df2e1f37SScott Kruger
935612eee3eSJed Brown    fd.close()
93629921a8fSScott Kruger    return
93729921a8fSScott Kruger
9388e69c5ecSJed Browndef main(petsc_dir=None, petsc_arch=None, verbose=False, single_ex=False, srcdir=None, testdir=None):
939c173c275SScott Kruger    # Allow petsc_arch to have both petsc_dir and petsc_arch for convenience
940cadd188bSScott Kruger    if petsc_arch:
941c173c275SScott Kruger        if len(petsc_arch.split(os.path.sep))>1:
942c173c275SScott Kruger            petsc_dir,petsc_arch=os.path.split(petsc_arch.rstrip(os.path.sep))
9438e69c5ecSJed Brown    output = os.path.join(testdir, 'testfiles')
94429921a8fSScott Kruger
945e551db17SScott Kruger    pEx=generateExamples(petsc_dir=petsc_dir, petsc_arch=petsc_arch,
946e551db17SScott Kruger                         verbose=verbose, single_ex=single_ex, srcdir=srcdir,
947e551db17SScott Kruger                         testdir=testdir)
948cadd188bSScott Kruger    dataDict=pEx.walktree(os.path.join(pEx.srcdir))
9498e69c5ecSJed Brown    pEx.write_gnumake(dataDict, output)
95029921a8fSScott Kruger
95129921a8fSScott Krugerif __name__ == '__main__':
95229921a8fSScott Kruger    import optparse
95329921a8fSScott Kruger    parser = optparse.OptionParser()
95429921a8fSScott Kruger    parser.add_option('--verbose', help='Show mismatches between makefiles and the filesystem', action='store_true', default=False)
9554ff3c6a1SScott Kruger    parser.add_option('--petsc-dir', help='Set PETSC_DIR different from environment', default=os.environ.get('PETSC_DIR'))
95629921a8fSScott Kruger    parser.add_option('--petsc-arch', help='Set PETSC_ARCH different from environment', default=os.environ.get('PETSC_ARCH'))
957e551db17SScott Kruger    parser.add_option('--srcdir', help='Set location of sources different from PETSC_DIR/src', default=None)
95829921a8fSScott Kruger    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')
9598e69c5ecSJed Brown    parser.add_option('-t', '--testdir', dest='testdir',  help='Test directory [$PETSC_ARCH/tests]')
9604ff3c6a1SScott Kruger
96129921a8fSScott Kruger    opts, extra_args = parser.parse_args()
96229921a8fSScott Kruger    if extra_args:
96329921a8fSScott Kruger        import sys
96429921a8fSScott Kruger        sys.stderr.write('Unknown arguments: %s\n' % ' '.join(extra_args))
96529921a8fSScott Kruger        exit(1)
9668e69c5ecSJed Brown    if opts.testdir is None:
9678e69c5ecSJed Brown      opts.testdir = os.path.join(opts.petsc_arch, 'tests')
9684ff3c6a1SScott Kruger
9694ff3c6a1SScott Kruger    main(petsc_dir=opts.petsc_dir, petsc_arch=opts.petsc_arch,
9708e69c5ecSJed Brown         verbose=opts.verbose,
9714ff3c6a1SScott Kruger         single_ex=opts.single_executable, srcdir=opts.srcdir,
9724ff3c6a1SScott Kruger         testdir=opts.testdir)
973