xref: /petsc/config/install.py (revision abf72cf01dcea18708dda3d818e7eb8d894422ca)
1df3bd252SSatish Balay#!/usr/bin/env python3
25b6bfdb9SJed Brownfrom __future__ import print_function
3e551db17SScott Krugerimport os, re, shutil, sys
40ee81e68SLisandro Dalcin
55b6bfdb9SJed Brownif 'PETSC_DIR' in os.environ:
60ee81e68SLisandro Dalcin  PETSC_DIR = os.environ['PETSC_DIR']
70ee81e68SLisandro Dalcinelse:
8c6ef1b5bSJed Brown  fd = open(os.path.join('lib','petsc','conf','petscvariables'))
90ee81e68SLisandro Dalcin  a = fd.readline()
100ee81e68SLisandro Dalcin  a = fd.readline()
110ee81e68SLisandro Dalcin  PETSC_DIR = a.split('=')[1][0:-1]
120ee81e68SLisandro Dalcin  fd.close()
130ee81e68SLisandro Dalcin
145b6bfdb9SJed Brownif 'PETSC_ARCH' in os.environ:
150ee81e68SLisandro Dalcin  PETSC_ARCH = os.environ['PETSC_ARCH']
160ee81e68SLisandro Dalcinelse:
17c6ef1b5bSJed Brown  fd = open(os.path.join('lib','petsc','conf','petscvariables'))
180ee81e68SLisandro Dalcin  a = fd.readline()
190ee81e68SLisandro Dalcin  PETSC_ARCH = a.split('=')[1][0:-1]
200ee81e68SLisandro Dalcin  fd.close()
210ee81e68SLisandro Dalcin
225b6bfdb9SJed Brownprint('*** Using PETSC_DIR='+PETSC_DIR+' PETSC_ARCH='+PETSC_ARCH+' ***')
230ee81e68SLisandro Dalcinsys.path.insert(0, os.path.join(PETSC_DIR, 'config'))
240ee81e68SLisandro Dalcinsys.path.insert(0, os.path.join(PETSC_DIR, 'config', 'BuildSystem'))
250ee81e68SLisandro Dalcin
260ee81e68SLisandro Dalcinimport script
270ee81e68SLisandro Dalcin
280ee81e68SLisandro Dalcintry:
290ee81e68SLisandro Dalcin  WindowsError
300ee81e68SLisandro Dalcinexcept NameError:
310ee81e68SLisandro Dalcin  WindowsError = None
320ee81e68SLisandro Dalcin
330ee81e68SLisandro Dalcinclass Installer(script.Script):
340ee81e68SLisandro Dalcin  def __init__(self, clArgs = None):
350ee81e68SLisandro Dalcin    import RDict
360ee81e68SLisandro Dalcin    argDB = RDict.RDict(None, None, 0, 0, readonly = True)
37af0996ceSBarry Smith    argDB.saveFilename = os.path.join(PETSC_DIR, PETSC_ARCH, 'lib','petsc','conf', 'RDict.db')
380ee81e68SLisandro Dalcin    argDB.load()
390ee81e68SLisandro Dalcin    script.Script.__init__(self, argDB = argDB)
400ee81e68SLisandro Dalcin    if not clArgs is None: self.clArgs = clArgs
410ee81e68SLisandro Dalcin    self.copies = []
420ee81e68SLisandro Dalcin    return
430ee81e68SLisandro Dalcin
440ee81e68SLisandro Dalcin  def setupHelp(self, help):
450ee81e68SLisandro Dalcin    import nargs
460ee81e68SLisandro Dalcin    script.Script.setupHelp(self, help)
47a26d7103SSatish Balay    help.addArgument('Installer', '-destDir=<path>', nargs.Arg(None, '', 'Destination Directory for install'))
48fad83eadSPatrick Sanan    help.addArgument('Installer', '-no-examples', nargs.Arg(None, '', 'Skip installing examples'))
490ee81e68SLisandro Dalcin    return
500ee81e68SLisandro Dalcin
510ee81e68SLisandro Dalcin
520ee81e68SLisandro Dalcin  def setupModules(self):
530ee81e68SLisandro Dalcin    self.setCompilers  = self.framework.require('config.setCompilers',         None)
540ee81e68SLisandro Dalcin    self.arch          = self.framework.require('PETSc.options.arch',          None)
550ee81e68SLisandro Dalcin    self.petscdir      = self.framework.require('PETSc.options.petscdir',      None)
560ee81e68SLisandro Dalcin    self.compilers     = self.framework.require('config.compilers',            None)
5732cabb2fSBarry Smith    self.mpi           = self.framework.require('config.packages.MPI',         None)
580ee81e68SLisandro Dalcin    return
590ee81e68SLisandro Dalcin
600ee81e68SLisandro Dalcin  def setup(self):
610ee81e68SLisandro Dalcin    script.Script.setup(self)
620ee81e68SLisandro Dalcin    self.framework = self.loadConfigure()
630ee81e68SLisandro Dalcin    self.setupModules()
640ee81e68SLisandro Dalcin    return
650ee81e68SLisandro Dalcin
660ee81e68SLisandro Dalcin  def setupDirectories(self):
670ee81e68SLisandro Dalcin    self.rootDir    = self.petscdir.dir
684a08bad0SBarry Smith    self.installDir = os.path.abspath(os.path.expanduser(self.framework.argDB['prefix']))
698727f567SSatish Balay    self.destDir    = os.path.abspath(self.argDB['destDir']+self.installDir)
700ee81e68SLisandro Dalcin    self.arch       = self.arch.arch
7102e047dfSSatish Balay    self.archDir           = os.path.join(self.rootDir, self.arch)
720ee81e68SLisandro Dalcin    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
730ee81e68SLisandro Dalcin    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
74af0996ceSBarry Smith    self.rootConfDir       = os.path.join(self.rootDir, 'lib','petsc','conf')
75af0996ceSBarry Smith    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf')
76c3a89c15SBarry Smith    self.rootBinDir        = os.path.join(self.rootDir, 'lib','petsc','bin')
770ee81e68SLisandro Dalcin    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
780ee81e68SLisandro Dalcin    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
790ee81e68SLisandro Dalcin    self.destIncludeDir    = os.path.join(self.destDir, 'include')
80af0996ceSBarry Smith    self.destConfDir       = os.path.join(self.destDir, 'lib','petsc','conf')
810ee81e68SLisandro Dalcin    self.destLibDir        = os.path.join(self.destDir, 'lib')
82c3a89c15SBarry Smith    self.destBinDir        = os.path.join(self.destDir, 'lib','petsc','bin')
830ee81e68SLisandro Dalcin    self.installIncludeDir = os.path.join(self.installDir, 'include')
842d10696aSSatish Balay    self.installLibDir     = os.path.join(self.installDir, 'lib')
85c3a89c15SBarry Smith    self.installBinDir     = os.path.join(self.installDir, 'lib','petsc','bin')
860ee81e68SLisandro Dalcin    self.rootShareDir      = os.path.join(self.rootDir, 'share')
870ee81e68SLisandro Dalcin    self.destShareDir      = os.path.join(self.destDir, 'share')
8879eaf171SScott Kruger    self.rootSrcDir        = os.path.join(self.rootDir, 'src')
890ee81e68SLisandro Dalcin
900ee81e68SLisandro Dalcin    self.ranlib      = self.compilers.RANLIB
910ee81e68SLisandro Dalcin    self.arLibSuffix = self.compilers.AR_LIB_SUFFIX
920ee81e68SLisandro Dalcin    return
930ee81e68SLisandro Dalcin
940ee81e68SLisandro Dalcin  def checkPrefix(self):
950ee81e68SLisandro Dalcin    if not self.installDir:
965b6bfdb9SJed Brown      print('********************************************************************')
975b6bfdb9SJed Brown      print('PETSc is built without prefix option. So "make install" is not appropriate.')
985b6bfdb9SJed Brown      print('If you need a prefix install of PETSc - rerun configure with --prefix option.')
995b6bfdb9SJed Brown      print('********************************************************************')
1000ee81e68SLisandro Dalcin      sys.exit(1)
1010ee81e68SLisandro Dalcin    return
1020ee81e68SLisandro Dalcin
1030ee81e68SLisandro Dalcin  def checkDestdir(self):
1040ee81e68SLisandro Dalcin    if os.path.exists(self.destDir):
1050ee81e68SLisandro Dalcin      if os.path.samefile(self.destDir, self.rootDir):
1065b6bfdb9SJed Brown        print('********************************************************************')
1075b6bfdb9SJed Brown        print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR')
1085b6bfdb9SJed Brown        print('********************************************************************')
1090ee81e68SLisandro Dalcin        sys.exit(1)
1100ee81e68SLisandro Dalcin      if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)):
1115b6bfdb9SJed Brown        print('********************************************************************')
1125b6bfdb9SJed Brown        print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH')
1135b6bfdb9SJed Brown        print('********************************************************************')
1140ee81e68SLisandro Dalcin        sys.exit(1)
1150ee81e68SLisandro Dalcin      if not os.path.isdir(os.path.realpath(self.destDir)):
1165b6bfdb9SJed Brown        print('********************************************************************')
1175b6bfdb9SJed Brown        print('Specified destDir', self.destDir, 'is not a directory. Cannot proceed!')
1185b6bfdb9SJed Brown        print('********************************************************************')
1190ee81e68SLisandro Dalcin        sys.exit(1)
1200ee81e68SLisandro Dalcin      if not os.access(self.destDir, os.W_OK):
1215b6bfdb9SJed Brown        print('********************************************************************')
1225b6bfdb9SJed Brown        print('Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"')
1235b6bfdb9SJed Brown        print('********************************************************************')
1240ee81e68SLisandro Dalcin        sys.exit(1)
1250ee81e68SLisandro Dalcin    return
1260ee81e68SLisandro Dalcin
1270ee81e68SLisandro Dalcin  def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
1280ee81e68SLisandro Dalcin    """Copies a single file    """
1290ee81e68SLisandro Dalcin    copies = []
1300ee81e68SLisandro Dalcin    errors = []
1310ee81e68SLisandro Dalcin    if not os.path.exists(dst):
1320ee81e68SLisandro Dalcin      os.makedirs(dst)
1330ee81e68SLisandro Dalcin    elif not os.path.isdir(dst):
1345b6bfdb9SJed Brown      raise shutil.Error('Destination is not a directory')
1350ee81e68SLisandro Dalcin    srcname = src
1360ee81e68SLisandro Dalcin    dstname = os.path.join(dst, os.path.basename(src))
1370ee81e68SLisandro Dalcin    try:
1380ee81e68SLisandro Dalcin      if symlinks and os.path.islink(srcname):
1390ee81e68SLisandro Dalcin        linkto = os.readlink(srcname)
1400ee81e68SLisandro Dalcin        os.symlink(linkto, dstname)
1410ee81e68SLisandro Dalcin      else:
1420ee81e68SLisandro Dalcin        copyFunc(srcname, dstname)
1430ee81e68SLisandro Dalcin        copies.append((srcname, dstname))
1445b6bfdb9SJed Brown    except (IOError, os.error) as why:
1450ee81e68SLisandro Dalcin      errors.append((srcname, dstname, str(why)))
1465b6bfdb9SJed Brown    except shutil.Error as err:
1472c852529SBarry Smith      errors.append((srcname,dstname,str(err.args[0])))
1480ee81e68SLisandro Dalcin    if errors:
1495b6bfdb9SJed Brown      raise shutil.Error(errors)
1500ee81e68SLisandro Dalcin    return copies
1510ee81e68SLisandro Dalcin
15226e8aaceSBarry Smith  def fixExamplesMakefile(self, src):
15326e8aaceSBarry Smith    '''Change ././${PETSC_ARCH} in makefile in root petsc directory with ${PETSC_DIR}'''
15426e8aaceSBarry Smith    lines   = []
15526e8aaceSBarry Smith    oldFile = open(src, 'r')
156e551db17SScott Kruger    alllines=oldFile.read()
15726e8aaceSBarry Smith    oldFile.close()
158e551db17SScott Kruger    newlines=alllines.split('\n')[0]+'\n'  # Firstline
159d5b43468SJose E. Roman    # Hardcode PETSC_DIR and PETSC_ARCH to avoid users doing the wrong thing
160e551db17SScott Kruger    newlines+='PETSC_DIR='+self.installDir+'\n'
161e551db17SScott Kruger    newlines+='PETSC_ARCH=\n'
162e551db17SScott Kruger    for line in alllines.split('\n')[1:]:
1634ff3c6a1SScott Kruger      if line.startswith('TESTLOGFILE'):
164c173c275SScott Kruger        newlines+='TESTLOGFILE = $(TESTDIR)/examples-install.log\n'
165e551db17SScott Kruger      elif line.startswith('CONFIGDIR'):
166e551db17SScott Kruger        newlines+='CONFIGDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples/config\n'
167fc46264cSScott Kruger      elif line.startswith('$(generatedtest)') and 'petscvariables' in line:
168c173c275SScott Kruger        newlines+='all: test\n\n'+line+'\n'
169e551db17SScott Kruger      else:
1704ff3c6a1SScott Kruger        newlines+=line+'\n'
17126e8aaceSBarry Smith    newFile = open(src, 'w')
172e551db17SScott Kruger    newFile.write(newlines)
17326e8aaceSBarry Smith    newFile.close()
17426e8aaceSBarry Smith    return
17526e8aaceSBarry Smith
176e551db17SScott Kruger  def copyConfig(self, src, dst):
1772f21b5d8SJed Brown    """Copy configuration/testing files
178e551db17SScott Kruger    """
179e551db17SScott Kruger    if not os.path.isdir(dst):
1805b6bfdb9SJed Brown      raise shutil.Error('Destination is not a directory')
181e551db17SScott Kruger
182adf35c6eSSatish Balay    self.copies.extend(self.copyfile('gmakefile.test',dst))
183e551db17SScott Kruger    newConfigDir=os.path.join(dst,'config')  # Am not renaming at present
184e551db17SScott Kruger    if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir)
185e551db17SScott Kruger    testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split()
18658780e5dSStefano Zampini    testConfFiles+="petsc_harness.sh report_tests.py query_tests.py".split()
187e551db17SScott Kruger    for tf in testConfFiles:
188adf35c6eSSatish Balay      self.copies.extend(self.copyfile(os.path.join('config',tf),newConfigDir))
189e551db17SScott Kruger    return
190e551db17SScott Kruger
19126e8aaceSBarry Smith  def copyExamples(self, src, dst):
1924ff3c6a1SScott Kruger    """copy the examples directories
19326e8aaceSBarry Smith    """
194ad246c4dSSatish Balay    for root, dirs, files in os.walk(src, topdown=False):
1952f21b5d8SJed Brown      if os.path.basename(root) not in ("tests", "tutorials"): continue
196ad246c4dSSatish Balay      self.copies.extend(self.copytree(root, root.replace(src,dst)))
1974ff3c6a1SScott Kruger    return
1980ee81e68SLisandro Dalcin
1990080bb28SSatish Balay  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= ['.DSYM','.o','.pyc'], recurse = 1):
2000ee81e68SLisandro Dalcin    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
2010ee81e68SLisandro Dalcin
2020ee81e68SLisandro Dalcin       The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2
2030ee81e68SLisandro Dalcin
2040ee81e68SLisandro Dalcin    The destination directory must not already exist.
2050ee81e68SLisandro Dalcin    If exception(s) occur, an shutil.Error is raised with a list of reasons.
2060ee81e68SLisandro Dalcin
2070ee81e68SLisandro Dalcin    If the optional symlinks flag is true, symbolic links in the
2080ee81e68SLisandro Dalcin    source tree result in symbolic links in the destination tree; if
2090ee81e68SLisandro Dalcin    it is false, the contents of the files pointed to by symbolic
2100ee81e68SLisandro Dalcin    links are copied.
2110ee81e68SLisandro Dalcin    """
2120ee81e68SLisandro Dalcin    copies = []
2130ee81e68SLisandro Dalcin    names  = os.listdir(src)
2140ee81e68SLisandro Dalcin    if not os.path.exists(dst):
2150ee81e68SLisandro Dalcin      os.makedirs(dst)
2160ee81e68SLisandro Dalcin    elif not os.path.isdir(dst):
2175b6bfdb9SJed Brown      raise shutil.Error('Destination is not a directory')
2180ee81e68SLisandro Dalcin    errors = []
2192c72da00SPierre Jolivet    srclinks = []
2202c72da00SPierre Jolivet    dstlinks = []
2210ee81e68SLisandro Dalcin    for name in names:
2220ee81e68SLisandro Dalcin      srcname = os.path.join(src, name)
2230ee81e68SLisandro Dalcin      dstname = os.path.join(dst, name)
2240ee81e68SLisandro Dalcin      try:
2250ee81e68SLisandro Dalcin        if symlinks and os.path.islink(srcname):
2260ee81e68SLisandro Dalcin          linkto = os.readlink(srcname)
2270ee81e68SLisandro Dalcin          os.symlink(linkto, dstname)
22832cabb2fSBarry Smith        elif os.path.isdir(srcname) and recurse and not os.path.basename(srcname) in exclude:
229adf35c6eSSatish Balay          copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext))
230adf35c6eSSatish Balay        elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext :
2312c72da00SPierre Jolivet          if os.path.islink(srcname):
2322c72da00SPierre Jolivet            srclinks.append(srcname)
2332c72da00SPierre Jolivet            dstlinks.append(dstname)
2342c72da00SPierre Jolivet          else:
2350ee81e68SLisandro Dalcin            copyFunc(srcname, dstname)
2360ee81e68SLisandro Dalcin            copies.append((srcname, dstname))
2370ee81e68SLisandro Dalcin        # XXX What about devices, sockets etc.?
2385b6bfdb9SJed Brown      except (IOError, os.error) as why:
2390ee81e68SLisandro Dalcin        errors.append((srcname, dstname, str(why)))
2400ee81e68SLisandro Dalcin      # catch the Error from the recursive copytree so that we can
2410ee81e68SLisandro Dalcin      # continue with other files
2425b6bfdb9SJed Brown      except shutil.Error as err:
2432c852529SBarry Smith        errors.append((srcname,dstname,str(err.args[0])))
2442c72da00SPierre Jolivet    for srcname, dstname in zip(srclinks, dstlinks):
2452c72da00SPierre Jolivet      try:
2462c72da00SPierre Jolivet        copyFunc(srcname, dstname)
2472c72da00SPierre Jolivet        copies.append((srcname, dstname))
2482c72da00SPierre Jolivet      except (IOError, os.error) as why:
2492c72da00SPierre Jolivet        errors.append((srcname, dstname, str(why)))
2502c72da00SPierre Jolivet      # catch the Error from the recursive copytree so that we can
2512c72da00SPierre Jolivet      # continue with other files
2522c72da00SPierre Jolivet      except shutil.Error as err:
2532c852529SBarry Smith        errors.append((srcname,dstname,str(err.args[0])))
2540ee81e68SLisandro Dalcin    try:
2550ee81e68SLisandro Dalcin      shutil.copystat(src, dst)
2565b6bfdb9SJed Brown    except OSError as e:
2570ee81e68SLisandro Dalcin      if WindowsError is not None and isinstance(e, WindowsError):
2580ee81e68SLisandro Dalcin        # Copying file access times may fail on Windows
2590ee81e68SLisandro Dalcin        pass
2600ee81e68SLisandro Dalcin      else:
2612c852529SBarry Smith        errors.append((src, dst, str(e)))
2620ee81e68SLisandro Dalcin    if errors:
2635b6bfdb9SJed Brown      raise shutil.Error(errors)
2640ee81e68SLisandro Dalcin    return copies
2650ee81e68SLisandro Dalcin
2660ee81e68SLisandro Dalcin
2670ee81e68SLisandro Dalcin  def fixConfFile(self, src):
2680ee81e68SLisandro Dalcin    lines   = []
2690ee81e68SLisandro Dalcin    oldFile = open(src, 'r')
2700ee81e68SLisandro Dalcin    for line in oldFile.readlines():
2715a21677cSJed Brown      if line.startswith('PETSC_CC_INCLUDES =') or line.startswith('PETSC_FC_INCLUDES ='):
2725a21677cSJed Brown        continue
2735a21677cSJed Brown      line = line.replace('PETSC_CC_INCLUDES_INSTALL', 'PETSC_CC_INCLUDES')
2745a21677cSJed Brown      line = line.replace('PETSC_FC_INCLUDES_INSTALL', 'PETSC_FC_INCLUDES')
2750ee81e68SLisandro Dalcin      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
2760ee81e68SLisandro Dalcin      line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir)
2770ee81e68SLisandro Dalcin      line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '')
2780ee81e68SLisandro Dalcin      line = line.replace('${PETSC_DIR}', self.installDir)
2792d10696aSSatish Balay      # replace PETSC_DIR/PETSC_ARCH/lib (i.e., build location) with prefix/lib
2802d10696aSSatish Balay      line = line.replace(self.archLibDir,self.installLibDir)
281011f288aSSatish Balay      # replace PETSC_DIR/lib/petsc/bin with prefix/lib/petsc/bin
282011f288aSSatish Balay      line = line.replace(self.rootBinDir,self.destBinDir)
2830ee81e68SLisandro Dalcin      lines.append(line)
2840ee81e68SLisandro Dalcin    oldFile.close()
2850ee81e68SLisandro Dalcin    newFile = open(src, 'w')
2860ee81e68SLisandro Dalcin    newFile.write(''.join(lines))
2870ee81e68SLisandro Dalcin    newFile.close()
2880ee81e68SLisandro Dalcin    return
2890ee81e68SLisandro Dalcin
2900ee81e68SLisandro Dalcin  def fixConf(self):
2910ee81e68SLisandro Dalcin    import shutil
292cb5db241SBarry Smith    for file in ['rules', 'rules_doc.mk', 'rules_util.mk', 'variables', 'petscrules', 'petscvariables']:
2930ee81e68SLisandro Dalcin      self.fixConfFile(os.path.join(self.destConfDir,file))
2940ee81e68SLisandro Dalcin    return
2950ee81e68SLisandro Dalcin
2965ee53db2SLisandro Dalcin  def fixPythonWheel(self):
2975ee53db2SLisandro Dalcin    import glob
2985ee53db2SLisandro Dalcin    import shutil
2995ee53db2SLisandro Dalcin    #
3005ee53db2SLisandro Dalcin    for pattern in (
3015ee53db2SLisandro Dalcin        self.destLibDir  + '/*.a',
3025ee53db2SLisandro Dalcin        self.destLibDir  + '/*.la',
3035ee53db2SLisandro Dalcin        self.destLibDir  + '/pkgconfig',  # TODO: keep?
3045ee53db2SLisandro Dalcin        self.destConfDir + '/configure-hash',
3055ee53db2SLisandro Dalcin        self.destConfDir + '/uninstall.py',
3065ee53db2SLisandro Dalcin        self.destConfDir + '/reconfigure-*.py',
3075ee53db2SLisandro Dalcin        self.destConfDir + '/pkg.conf.*',
3085ee53db2SLisandro Dalcin        self.destConfDir + '/pkg.git*.*',
3095ee53db2SLisandro Dalcin        self.destConfDir + '/modules',  # TODO: keep?
3105ee53db2SLisandro Dalcin        self.destShareDir + '/*/examples/src/*',
3115ee53db2SLisandro Dalcin        self.destShareDir + '/*/datafiles',
3125ee53db2SLisandro Dalcin    ):
3135ee53db2SLisandro Dalcin      for pathname in glob.glob(pattern):
3145ee53db2SLisandro Dalcin        if os.path.isdir(pathname):
3155ee53db2SLisandro Dalcin          shutil.rmtree(pathname)
3165ee53db2SLisandro Dalcin        elif os.path.exists(pathname):
3175ee53db2SLisandro Dalcin          os.remove(pathname)
3185ee53db2SLisandro Dalcin    #
3195ee53db2SLisandro Dalcin    for filename in (
3205ee53db2SLisandro Dalcin      self.destIncludeDir + '/petscconf.h',
3215ee53db2SLisandro Dalcin      self.destIncludeDir + '/petscconfiginfo.h',
3225ee53db2SLisandro Dalcin      self.destIncludeDir + '/petscmachineinfo.h',
3235ee53db2SLisandro Dalcin      self.destShareDir + '/petsc/examples/gmakefile.test',
3245ee53db2SLisandro Dalcin      self.destConfDir + '/rules',
3255ee53db2SLisandro Dalcin      self.destConfDir + '/rules_doc.mk',
3265ee53db2SLisandro Dalcin      self.destConfDir + '/rules_util.mk',
3275ee53db2SLisandro Dalcin      self.destConfDir + '/petscrules',
3285ee53db2SLisandro Dalcin      self.destConfDir + '/variables',
3295ee53db2SLisandro Dalcin      self.destConfDir + '/petscvariables',
3305ee53db2SLisandro Dalcin    ):
3315ee53db2SLisandro Dalcin      with open(filename, 'r') as oldFile:
3325ee53db2SLisandro Dalcin        contents = oldFile.read()
3335ee53db2SLisandro Dalcin      contents = contents.replace(self.installDir, '${PETSC_DIR}')
3345ee53db2SLisandro Dalcin      contents = contents.replace(self.rootDir, '${PETSC_DIR}')
3355ee53db2SLisandro Dalcin      contents = re.sub(
3365ee53db2SLisandro Dalcin        r'^(PYTHON(_EXE)?) = (.*)$',
3375ee53db2SLisandro Dalcin        r'\1 = python%d' % sys.version_info[0],
3385ee53db2SLisandro Dalcin        contents, flags=re.MULTILINE,
3395ee53db2SLisandro Dalcin      )
3405ee53db2SLisandro Dalcin      with open(filename, 'w') as newFile:
3415ee53db2SLisandro Dalcin        newFile.write(contents)
3425ee53db2SLisandro Dalcin    #
3435ee53db2SLisandro Dalcin    def lsdir(dirname, *patterns):
3445ee53db2SLisandro Dalcin      return glob.glob(os.path.join(dirname, *patterns))
3455ee53db2SLisandro Dalcin    def shell(*args):
3465ee53db2SLisandro Dalcin      return self.executeShellCommand(' '.join(args))[0]
3475ee53db2SLisandro Dalcin    libdir = os.path.join(self.installDir, 'lib')
3485ee53db2SLisandro Dalcin    if sys.platform == 'linux':
3495ee53db2SLisandro Dalcin      libraries = [
3505ee53db2SLisandro Dalcin        lib for lib in lsdir(self.destLibDir, 'lib*.so*')
3515ee53db2SLisandro Dalcin        if not os.path.islink(lib)
3525ee53db2SLisandro Dalcin      ]
3535ee53db2SLisandro Dalcin      for shlib in libraries:
3545ee53db2SLisandro Dalcin        # fix shared library rpath
3555ee53db2SLisandro Dalcin        rpath = shell('patchelf', '--print-rpath', shlib)
3565ee53db2SLisandro Dalcin        rpath = rpath.split(os.path.pathsep)
3575ee53db2SLisandro Dalcin        if libdir in rpath:
3585ee53db2SLisandro Dalcin          rpath.insert(0, '$ORIGIN')
3595ee53db2SLisandro Dalcin          while libdir in rpath:
3605ee53db2SLisandro Dalcin            rpath.remove(libdir)
3615ee53db2SLisandro Dalcin        if rpath:
3625ee53db2SLisandro Dalcin          rpath = os.path.pathsep.join(rpath)
3635ee53db2SLisandro Dalcin          shell('patchelf', '--set-rpath', "'%s'" % rpath, shlib)
3645ee53db2SLisandro Dalcin        # fix shared library file and symlink
3655ee53db2SLisandro Dalcin        basename = os.path.basename(shlib)
3665ee53db2SLisandro Dalcin        libname, ext, _ = basename.partition('.so')
3675ee53db2SLisandro Dalcin        liblink = libname + ext
3685ee53db2SLisandro Dalcin        soname = shell('patchelf', '--print-soname', shlib)
3695ee53db2SLisandro Dalcin        for symlink in lsdir(self.destLibDir, liblink + '*'):
3705ee53db2SLisandro Dalcin          if os.path.islink(symlink):
3715ee53db2SLisandro Dalcin            os.unlink(symlink)
3725ee53db2SLisandro Dalcin        curdir = os.getcwd()
3735ee53db2SLisandro Dalcin        try:
3745ee53db2SLisandro Dalcin          os.chdir(os.path.dirname(shlib))
3755ee53db2SLisandro Dalcin          if soname != basename:
3765ee53db2SLisandro Dalcin            os.rename(basename, soname)
3775ee53db2SLisandro Dalcin          if soname != liblink:
3785ee53db2SLisandro Dalcin            os.symlink(soname, liblink)
3795ee53db2SLisandro Dalcin        finally:
3805ee53db2SLisandro Dalcin          os.chdir(curdir)
3815ee53db2SLisandro Dalcin    if sys.platform == 'darwin':
3825ee53db2SLisandro Dalcin      def otool(cmd, dylib):
3835ee53db2SLisandro Dalcin        pattern = r'''
3845ee53db2SLisandro Dalcin          ^\s+ cmd \s %s$\n
3855ee53db2SLisandro Dalcin          ^\s+ cmdsize \s \d+$\n
3865ee53db2SLisandro Dalcin          ^\s+ (?:name|path) \s (.*) \s \(offset \s \d+\)$
3875ee53db2SLisandro Dalcin        ''' % cmd
3885ee53db2SLisandro Dalcin        return re.findall(
3895ee53db2SLisandro Dalcin          pattern, shell('otool', '-l', dylib),
3905ee53db2SLisandro Dalcin          flags=re.VERBOSE | re.MULTILINE,
3915ee53db2SLisandro Dalcin        )
3925ee53db2SLisandro Dalcin      libraries = [
3935ee53db2SLisandro Dalcin        lib for lib in lsdir(self.destLibDir, 'lib*.dylib')
3945ee53db2SLisandro Dalcin        if not os.path.islink(lib)
3955ee53db2SLisandro Dalcin      ]
3965ee53db2SLisandro Dalcin      for dylib in libraries:
3975ee53db2SLisandro Dalcin        install_name = otool('LC_ID_DYLIB', dylib)[0]
3985ee53db2SLisandro Dalcin        dependencies = otool('LC_LOAD_DYLIB', dylib)
3995ee53db2SLisandro Dalcin        runtime_path = otool('LC_RPATH', dylib)
4005ee53db2SLisandro Dalcin        # fix shared library install name and rpath
4015ee53db2SLisandro Dalcin        install_name = '@rpath/' + os.path.basename(install_name)
4025ee53db2SLisandro Dalcin        shell('install_name_tool', '-id', install_name, dylib)
4035ee53db2SLisandro Dalcin        if libdir in runtime_path:
4045ee53db2SLisandro Dalcin          shell('install_name_tool', '-delete_rpath', libdir, dylib)
4055ee53db2SLisandro Dalcin        for rpath in ('@loader_path',):
4065ee53db2SLisandro Dalcin          if rpath not in runtime_path:
4075ee53db2SLisandro Dalcin            shell('install_name_tool', '-add_rpath', rpath, dylib)
4085ee53db2SLisandro Dalcin        for dep in dependencies:
4095ee53db2SLisandro Dalcin          if os.path.dirname(dep) in (libdir,):
4105ee53db2SLisandro Dalcin            newid = '@rpath/' + os.path.basename(dep)
4115ee53db2SLisandro Dalcin            shell('install_name_tool', '-change', dep, newid, dylib)
4125ee53db2SLisandro Dalcin        # fix shared library file and symlink
4135ee53db2SLisandro Dalcin        basename = os.path.basename(dylib)
4145ee53db2SLisandro Dalcin        libname, ext = os.path.splitext(basename)
4155ee53db2SLisandro Dalcin        libname = libname.partition('.')[0]
4165ee53db2SLisandro Dalcin        liblink = libname + ext
4175ee53db2SLisandro Dalcin        dyname = os.path.basename(install_name)
4185ee53db2SLisandro Dalcin        for symlink in lsdir(self.destLibDir, libname + '*' + ext):
4195ee53db2SLisandro Dalcin          if os.path.islink(symlink):
4205ee53db2SLisandro Dalcin            os.unlink(symlink)
4215ee53db2SLisandro Dalcin        curdir = os.getcwd()
4225ee53db2SLisandro Dalcin        try:
4235ee53db2SLisandro Dalcin          os.chdir(os.path.dirname(dylib))
4245ee53db2SLisandro Dalcin          if dyname != basename:
4255ee53db2SLisandro Dalcin            os.rename(basename, dyname)
4265ee53db2SLisandro Dalcin          if dyname != liblink:
4275ee53db2SLisandro Dalcin            os.symlink(dyname, liblink)
4285ee53db2SLisandro Dalcin        finally:
4295ee53db2SLisandro Dalcin          os.chdir(curdir)
4305ee53db2SLisandro Dalcin    #
4315ee53db2SLisandro Dalcin    return
4325ee53db2SLisandro Dalcin
4330ee81e68SLisandro Dalcin  def createUninstaller(self):
4340ee81e68SLisandro Dalcin    uninstallscript = os.path.join(self.destConfDir, 'uninstall.py')
4350ee81e68SLisandro Dalcin    f = open(uninstallscript, 'w')
4360ee81e68SLisandro Dalcin    # Could use the Python AST to do this
4370ee81e68SLisandro Dalcin    f.write('#!'+sys.executable+'\n')
4380ee81e68SLisandro Dalcin    f.write('import os\n')
439d97f9ea1SSatish Balay    f.write('prefixdir = "'+self.installDir+'"\n')
440d97f9ea1SSatish Balay    files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies]
441d97f9ea1SSatish Balay    files.append(uninstallscript.replace(self.destDir,self.installDir))
442d97f9ea1SSatish Balay    f.write('files = '+repr(files))
4430ee81e68SLisandro Dalcin    f.write('''
444d97f9ea1SSatish Balayfor file in files:
445d97f9ea1SSatish Balay  if os.path.exists(file) or os.path.islink(file):
446d97f9ea1SSatish Balay    os.remove(file)
447d97f9ea1SSatish Balay    dir = os.path.dirname(file)
4482b39596bSSatish Balay    while dir not in [os.path.dirname(prefixdir),'/']:
4492b39596bSSatish Balay      try: os.rmdir(dir)
4502b39596bSSatish Balay      except: break
451d97f9ea1SSatish Balay      dir = os.path.dirname(dir)
4520ee81e68SLisandro Dalcin''')
4530ee81e68SLisandro Dalcin    f.close()
4545b6bfdb9SJed Brown    os.chmod(uninstallscript,0o744)
4550ee81e68SLisandro Dalcin    return
4560ee81e68SLisandro Dalcin
4570ee81e68SLisandro Dalcin  def installIncludes(self):
45832cabb2fSBarry Smith    exclude = ['makefile']
45932cabb2fSBarry Smith    if not hasattr(self.compilers.setCompilers, 'FC'):
46032cabb2fSBarry Smith      exclude.append('finclude')
46132cabb2fSBarry Smith    if not self.mpi.usingMPIUni:
46232cabb2fSBarry Smith      exclude.append('mpiuni')
46332cabb2fSBarry Smith    self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude))
4640ee81e68SLisandro Dalcin    self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir))
4650ee81e68SLisandro Dalcin    return
4660ee81e68SLisandro Dalcin
4670ee81e68SLisandro Dalcin  def installConf(self):
46832cabb2fSBarry Smith    self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log']))
469a035e9faSPierre Jolivet    self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp','configure.log','make.log','gmake.log','test.log','error.log','memoryerror.log','files','testfiles','RDict.db']))
4700ee81e68SLisandro Dalcin    return
4710ee81e68SLisandro Dalcin
4720ee81e68SLisandro Dalcin  def installBin(self):
47332cabb2fSBarry Smith    exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml']
47432cabb2fSBarry Smith    self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude ))
47532cabb2fSBarry Smith    exclude = ['maint']
47632cabb2fSBarry Smith    if not self.mpi.usingMPIUni:
47732cabb2fSBarry Smith      exclude.append('petsc-mpiexec.uni')
47832cabb2fSBarry Smith    self.setCompilers.pushLanguage('C')
479011f288aSSatish Balay    if self.setCompilers.getCompiler().find('win32fe') < 0:
48032cabb2fSBarry Smith      exclude.append('win32fe')
48132cabb2fSBarry Smith    self.setCompilers.popLanguage()
48232cabb2fSBarry Smith    self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude ))
4830ee81e68SLisandro Dalcin    return
4840ee81e68SLisandro Dalcin
4850ee81e68SLisandro Dalcin  def installShare(self):
4860ee81e68SLisandro Dalcin    self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir))
487c173c275SScott Kruger    examplesdir=os.path.join(self.destShareDir,'petsc','examples')
488c173c275SScott Kruger    if os.path.exists(examplesdir):
489c173c275SScott Kruger      shutil.rmtree(examplesdir)
490c173c275SScott Kruger    os.mkdir(examplesdir)
49179eaf171SScott Kruger    os.mkdir(os.path.join(examplesdir,'src'))
492c173c275SScott Kruger    self.copyConfig(self.rootDir,examplesdir)
493fad83eadSPatrick Sanan    if not self.argDB['no-examples']:
494fad83eadSPatrick Sanan        self.copyExamples(self.rootSrcDir,os.path.join(examplesdir,'src'))
495c173c275SScott Kruger        self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test'))
4960ee81e68SLisandro Dalcin    return
4970ee81e68SLisandro Dalcin
4980ee81e68SLisandro Dalcin  def copyLib(self, src, dst):
4990ee81e68SLisandro Dalcin    '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac'''
5000ee81e68SLisandro Dalcin    # Symlinks (assumed local) are recreated at dst
5010ee81e68SLisandro Dalcin    if os.path.islink(src):
5020ee81e68SLisandro Dalcin      linkto = os.readlink(src)
5030ee81e68SLisandro Dalcin      try:
5040ee81e68SLisandro Dalcin        os.remove(dst)            # In case it already exists
5050ee81e68SLisandro Dalcin      except OSError:
5060ee81e68SLisandro Dalcin        pass
5070ee81e68SLisandro Dalcin      os.symlink(linkto, dst)
5080ee81e68SLisandro Dalcin      return
5090ee81e68SLisandro Dalcin    shutil.copy2(src, dst)
510011f288aSSatish Balay    if self.setCompilers.getCompiler().find('win32fe') < 0 and os.path.splitext(dst)[1] == '.'+self.arLibSuffix:
511*abf72cf0SSatish Balay      import shlex
512*abf72cf0SSatish Balay      self.executeShellCommand(shlex.split(self.ranlib) + [dst])
513c9e802c5SMin RK    if os.path.splitext(dst)[1] == '.dylib' and shutil.which('otool') and shutil.which('install_name_tool'):
514793d7be8SSatish Balay      [output,err,flg] = self.executeShellCommand(['otool', '-D', src])
515af2c601bSBarry Smith      oldname = output[output.find("\n")+1:]
516d4c3e6c5SSatish Balay      installName = oldname.replace(os.path.realpath(self.archDir), self.installDir)
517793d7be8SSatish Balay      self.executeShellCommand(['install_name_tool', '-id', installName, dst])
5180ee81e68SLisandro Dalcin    # preserve the original timestamps - so that the .a vs .so time order is preserved
5190ee81e68SLisandro Dalcin    shutil.copystat(src,dst)
5200ee81e68SLisandro Dalcin    return
5210ee81e68SLisandro Dalcin
5220ee81e68SLisandro Dalcin  def installLib(self):
52332cabb2fSBarry Smith    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
52432cabb2fSBarry Smith    self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
5250ee81e68SLisandro Dalcin    return
5260ee81e68SLisandro Dalcin
5270ee81e68SLisandro Dalcin
5280ee81e68SLisandro Dalcin  def outputInstallDone(self):
5295b4fc442SVaclav Hapla    from config.packages.make import getMakeUserPath
5305b6bfdb9SJed Brown    print('''\
5310ee81e68SLisandro Dalcin====================================
5320ee81e68SLisandro DalcinInstall complete.
5330ee81e68SLisandro DalcinNow to check if the libraries are working do (in current directory):
5345b4fc442SVaclav Hapla%s PETSC_DIR=%s PETSC_ARCH="" check
5350ee81e68SLisandro Dalcin====================================\
5365b4fc442SVaclav Hapla''' % (getMakeUserPath(self.arch), self.installDir))
5370ee81e68SLisandro Dalcin    return
5380ee81e68SLisandro Dalcin
5390ee81e68SLisandro Dalcin  def outputDestDirDone(self):
5405b6bfdb9SJed Brown    print('''\
5410ee81e68SLisandro Dalcin====================================
5420ee81e68SLisandro DalcinCopy to DESTDIR %s is now complete.
5430ee81e68SLisandro DalcinBefore use - please copy/install over to specified prefix: %s
5440ee81e68SLisandro Dalcin====================================\
5455b6bfdb9SJed Brown''' % (self.destDir,self.installDir))
5460ee81e68SLisandro Dalcin    return
5470ee81e68SLisandro Dalcin
5480ee81e68SLisandro Dalcin  def runsetup(self):
5490ee81e68SLisandro Dalcin    self.setup()
5500ee81e68SLisandro Dalcin    self.setupDirectories()
5510ee81e68SLisandro Dalcin    self.checkPrefix()
5520ee81e68SLisandro Dalcin    self.checkDestdir()
5530ee81e68SLisandro Dalcin    return
5540ee81e68SLisandro Dalcin
5550ee81e68SLisandro Dalcin  def runcopy(self):
5560ee81e68SLisandro Dalcin    if self.destDir == self.installDir:
5575b6bfdb9SJed Brown      print('*** Installing PETSc at prefix location:',self.destDir, ' ***')
5580ee81e68SLisandro Dalcin    else:
5595b6bfdb9SJed Brown      print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***')
5600ee81e68SLisandro Dalcin    if not os.path.exists(self.destDir):
5610ee81e68SLisandro Dalcin      try:
5620ee81e68SLisandro Dalcin        os.makedirs(self.destDir)
5630ee81e68SLisandro Dalcin      except:
5645b6bfdb9SJed Brown        print('********************************************************************')
5655b6bfdb9SJed Brown        print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"')
5665b6bfdb9SJed Brown        print('********************************************************************')
5670ee81e68SLisandro Dalcin        sys.exit(1)
5680ee81e68SLisandro Dalcin    self.installIncludes()
5690ee81e68SLisandro Dalcin    self.installConf()
5700ee81e68SLisandro Dalcin    self.installBin()
5710ee81e68SLisandro Dalcin    self.installLib()
5720ee81e68SLisandro Dalcin    self.installShare()
5735ee53db2SLisandro Dalcin    self.createUninstaller()
5740ee81e68SLisandro Dalcin    return
5750ee81e68SLisandro Dalcin
5760ee81e68SLisandro Dalcin  def runfix(self):
5770ee81e68SLisandro Dalcin    self.fixConf()
5781eeab4b4SLisandro Dalcin    using_build_backend = any(
5791eeab4b4SLisandro Dalcin      os.environ.get(prefix + '_BUILD_BACKEND')
5801eeab4b4SLisandro Dalcin      for prefix in ('_PYPROJECT_HOOKS', 'PEP517')
5811eeab4b4SLisandro Dalcin    )
5821eeab4b4SLisandro Dalcin    if using_build_backend:
5835ee53db2SLisandro Dalcin      self.fixPythonWheel()
5840ee81e68SLisandro Dalcin    return
5850ee81e68SLisandro Dalcin
5860ee81e68SLisandro Dalcin  def rundone(self):
5870ee81e68SLisandro Dalcin    if self.destDir == self.installDir:
5880ee81e68SLisandro Dalcin      self.outputInstallDone()
5890ee81e68SLisandro Dalcin    else:
5900ee81e68SLisandro Dalcin      self.outputDestDirDone()
5910ee81e68SLisandro Dalcin    return
5920ee81e68SLisandro Dalcin
5930ee81e68SLisandro Dalcin  def run(self):
5940ee81e68SLisandro Dalcin    self.runsetup()
5950ee81e68SLisandro Dalcin    self.runcopy()
5960ee81e68SLisandro Dalcin    self.runfix()
5970ee81e68SLisandro Dalcin    self.rundone()
5980ee81e68SLisandro Dalcin    return
5990ee81e68SLisandro Dalcin
6000ee81e68SLisandro Dalcinif __name__ == '__main__':
6010ee81e68SLisandro Dalcin  Installer(sys.argv[1:]).run()
6020ee81e68SLisandro Dalcin  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
60303e6d329SSatish Balay  delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp']
6040ee81e68SLisandro Dalcin  for delfile in delfiles:
6050ee81e68SLisandro Dalcin    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
6060ee81e68SLisandro Dalcin      os.remove(delfile)
607