xref: /petsc/config/install.py (revision d2dffbf663ccc3d78d082b483cae224715b9dfa9)
1#!/usr/bin/env python3
2from __future__ import print_function
3import os, re, shutil, sys
4import sysconfig
5
6if 'PETSC_DIR' in os.environ:
7  PETSC_DIR = os.environ['PETSC_DIR']
8else:
9  fd = open(os.path.join('lib','petsc','conf','petscvariables'))
10  a = fd.readline()
11  a = fd.readline()
12  PETSC_DIR = a.split('=')[1][0:-1]
13  fd.close()
14
15if 'PETSC_ARCH' in os.environ:
16  PETSC_ARCH = os.environ['PETSC_ARCH']
17else:
18  fd = open(os.path.join('lib','petsc','conf','petscvariables'))
19  a = fd.readline()
20  PETSC_ARCH = a.split('=')[1][0:-1]
21  fd.close()
22
23sys.path.insert(0, os.path.join(PETSC_DIR, 'config'))
24sys.path.insert(0, os.path.join(PETSC_DIR, 'config', 'BuildSystem'))
25
26import script
27
28try:
29  WindowsError
30except NameError:
31  WindowsError = None
32
33class Installer(script.Script):
34  def __init__(self, clArgs = None):
35    import RDict
36    argDB = RDict.RDict(None, None, 0, 0, readonly = True)
37    argDB.saveFilename = os.path.join(PETSC_DIR, PETSC_ARCH, 'lib','petsc','conf', 'RDict.db')
38    argDB.load()
39    script.Script.__init__(self, argDB = argDB)
40    if not clArgs is None: self.clArgs = clArgs
41    self.copies = []
42    return
43
44  def setupHelp(self, help):
45    import nargs
46    script.Script.setupHelp(self, help)
47    help.addArgument('Installer', '-destDir=<path>', nargs.Arg(None, '', 'Destination Directory for install'))
48    help.addArgument('Installer', '-no-examples', nargs.Arg(None, '', 'Skip installing examples'))
49    return
50
51
52  def setupModules(self):
53    self.setCompilers  = self.framework.require('config.setCompilers',         None)
54    self.arch          = self.framework.require('PETSc.options.arch',          None)
55    self.petscdir      = self.framework.require('PETSc.options.petscdir',      None)
56    self.compilers     = self.framework.require('config.compilers',            None)
57    self.mpi           = self.framework.require('config.packages.MPI',         None)
58    return
59
60  def setup(self):
61    script.Script.setup(self)
62    self.framework = self.loadConfigure()
63    self.setupModules()
64    return
65
66  def setupDirectories(self):
67    self.rootDir    = self.petscdir.dir
68    self.installDir = os.path.abspath(os.path.expanduser(self.framework.argDB['prefix']))
69    self.destDir    = os.path.abspath(self.argDB['destDir']+self.installDir)
70    self.arch       = self.arch.arch
71    self.archDir           = os.path.join(self.rootDir, self.arch)
72    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
73    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
74    self.rootConfDir       = os.path.join(self.rootDir, 'lib','petsc','conf')
75    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf')
76    self.rootBinDir        = os.path.join(self.rootDir, 'lib','petsc','bin')
77    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
78    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
79    self.destIncludeDir    = os.path.join(self.destDir, 'include')
80    self.destConfDir       = os.path.join(self.destDir, 'lib','petsc','conf')
81    self.destLibDir        = os.path.join(self.destDir, 'lib')
82    self.destBinDir        = os.path.join(self.destDir, 'lib','petsc','bin')
83    self.installIncludeDir = os.path.join(self.installDir, 'include')
84    self.installLibDir     = os.path.join(self.installDir, 'lib')
85    self.installBinDir     = os.path.join(self.installDir, 'lib','petsc','bin')
86    self.rootShareDir      = os.path.join(self.rootDir, 'share')
87    self.destShareDir      = os.path.join(self.destDir, 'share')
88    self.rootSrcDir        = os.path.join(self.rootDir, 'src')
89
90    self.ranlib      = self.compilers.RANLIB
91    self.arLibSuffix = self.compilers.AR_LIB_SUFFIX
92    return
93
94  def checkPrefix(self):
95    if not self.installDir:
96      print('********************************************************************')
97      print('PETSc is built without prefix option. So "make install" is not appropriate.')
98      print('If you need a prefix install of PETSc - rerun configure with --prefix option.')
99      print('********************************************************************')
100      sys.exit(1)
101    return
102
103  def checkDestdir(self):
104    if os.path.exists(self.destDir):
105      if os.path.samefile(self.destDir, self.rootDir):
106        print('********************************************************************')
107        print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR')
108        print('********************************************************************')
109        sys.exit(1)
110      if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)):
111        print('********************************************************************')
112        print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH')
113        print('********************************************************************')
114        sys.exit(1)
115      if not os.path.isdir(os.path.realpath(self.destDir)):
116        print('********************************************************************')
117        print('Specified destDir', self.destDir, 'is not a directory. Cannot proceed!')
118        print('********************************************************************')
119        sys.exit(1)
120      if not os.access(self.destDir, os.W_OK):
121        print('********************************************************************')
122        print('Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"')
123        print('********************************************************************')
124        sys.exit(1)
125    return
126
127  def setupBuild(self):
128    self.using_build_backend = any(
129      os.environ.get(prefix + '_BUILD_BACKEND')
130      for prefix in ('_PYPROJECT_HOOKS', 'PEP517')
131    )
132    self.relocate_py_env = os.environ.get('CIBUILDWHEEL', '0') == '1'
133
134  def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
135    """Copies a single file    """
136    copies = []
137    errors = []
138    if not os.path.exists(dst):
139      os.makedirs(dst)
140    elif not os.path.isdir(dst):
141      raise shutil.Error('Destination is not a directory')
142    srcname = src
143    dstname = os.path.join(dst, os.path.basename(src))
144    try:
145      if symlinks and os.path.islink(srcname):
146        linkto = os.readlink(srcname)
147        os.symlink(linkto, dstname)
148      else:
149        copyFunc(srcname, dstname)
150        copies.append((srcname, dstname))
151    except (IOError, os.error) as why:
152      errors.append((srcname, dstname, str(why)))
153    except shutil.Error as err:
154      errors.append((srcname,dstname,str(err.args[0])))
155    if errors:
156      raise shutil.Error(errors)
157    return copies
158
159  def fixExamplesMakefile(self, src):
160    '''Change ././${PETSC_ARCH} in makefile in root PETSc directory with ${PETSC_DIR}'''
161    lines   = []
162    oldFile = open(src, 'r')
163    alllines=oldFile.read()
164    oldFile.close()
165    newlines=alllines.split('\n')[0]+'\n'  # Firstline
166    # Hardcode PETSC_DIR and PETSC_ARCH to avoid users doing the wrong thing
167    newlines+='PETSC_DIR='+self.installDir+'\n'
168    newlines+='PETSC_ARCH=\n'
169    for line in alllines.split('\n')[1:]:
170      if line.startswith('TESTLOGFILE'):
171        newlines+='TESTLOGFILE = $(TESTDIR)/examples-install.log\n'
172      elif line.startswith('CONFIGDIR'):
173        newlines+='CONFIGDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples/config\n'
174      elif line.startswith('$(generatedtest)') and 'petscvariables' in line:
175        newlines+='all: test\n\n'+line+'\n'
176      else:
177        newlines+=line+'\n'
178    newFile = open(src, 'w')
179    newFile.write(newlines)
180    newFile.close()
181    return
182
183  def copyConfig(self, src, dst):
184    """Copy configuration/testing files
185    """
186    if not os.path.isdir(dst):
187      raise shutil.Error('Destination is not a directory')
188
189    self.copies.extend(self.copyfile('gmakefile.test',dst))
190    newConfigDir=os.path.join(dst,'config')  # Am not renaming at present
191    if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir)
192    testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split()
193    testConfFiles+="petsc_harness.sh report_tests.py query_tests.py".split()
194    for tf in testConfFiles:
195      self.copies.extend(self.copyfile(os.path.join('config',tf),newConfigDir))
196    return
197
198  def copyExamples(self, src, dst):
199    """copy the examples directories
200    """
201    for root, dirs, files in os.walk(src, topdown=False):
202      if os.path.basename(root) not in ("tests", "tutorials"): continue
203      self.copies.extend(self.copytree(root, root.replace(src,dst)))
204    return
205
206  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= ['.DSYM','.o','.pyc'], recurse = 1):
207    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
208
209       The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2
210
211    The destination directory must not already exist.
212    If exception(s) occur, an shutil.Error is raised with a list of reasons.
213
214    If the optional symlinks flag is true, symbolic links in the
215    source tree result in symbolic links in the destination tree; if
216    it is false, the contents of the files pointed to by symbolic
217    links are copied.
218    """
219    copies = []
220    names  = os.listdir(src)
221    if not os.path.exists(dst):
222      os.makedirs(dst)
223    elif not os.path.isdir(dst):
224      raise shutil.Error('Destination is not a directory')
225    errors = []
226    srclinks = []
227    dstlinks = []
228    for name in names:
229      srcname = os.path.join(src, name)
230      dstname = os.path.join(dst, name)
231      try:
232        if symlinks and os.path.islink(srcname):
233          linkto = os.readlink(srcname)
234          os.symlink(linkto, dstname)
235        elif os.path.isdir(srcname) and recurse and not os.path.basename(srcname) in exclude:
236          copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext))
237        elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext :
238          if os.path.islink(srcname):
239            srclinks.append(srcname)
240            dstlinks.append(dstname)
241          else:
242            copyFunc(srcname, dstname)
243            copies.append((srcname, dstname))
244        # XXX What about devices, sockets etc.?
245      except (IOError, os.error) as why:
246        errors.append((srcname, dstname, str(why)))
247      # catch the Error from the recursive copytree so that we can
248      # continue with other files
249      except shutil.Error as err:
250        errors.append((srcname,dstname,str(err.args[0])))
251    for srcname, dstname in zip(srclinks, dstlinks):
252      try:
253        copyFunc(srcname, dstname)
254        copies.append((srcname, dstname))
255      except (IOError, os.error) as why:
256        errors.append((srcname, dstname, str(why)))
257      # catch the Error from the recursive copytree so that we can
258      # continue with other files
259      except shutil.Error as err:
260        errors.append((srcname,dstname,str(err.args[0])))
261    try:
262      shutil.copystat(src, dst)
263    except OSError as e:
264      if WindowsError is not None and isinstance(e, WindowsError):
265        # Copying file access times may fail on Windows
266        pass
267      else:
268        errors.append((src, dst, str(e)))
269    if errors:
270      raise shutil.Error(errors)
271    return copies
272
273
274  def fixConfFile(self, src):
275    lines   = []
276    oldFile = open(src, 'r')
277    for line in oldFile.readlines():
278      if line.startswith('PETSC_CC_INCLUDES =') or line.startswith('PETSC_FC_INCLUDES ='):
279        continue
280      line = line.replace('PETSC_CC_INCLUDES_INSTALL', 'PETSC_CC_INCLUDES')
281      line = line.replace('PETSC_FC_INCLUDES_INSTALL', 'PETSC_FC_INCLUDES')
282      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
283      line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir)
284      line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '')
285      line = line.replace('${PETSC_DIR}', self.installDir)
286      # replace PETSC_DIR/PETSC_ARCH/lib (i.e., build location) with prefix/lib
287      line = line.replace(self.archLibDir,self.installLibDir)
288      # replace PETSC_DIR/lib/petsc/bin with prefix/lib/petsc/bin
289      line = line.replace(self.rootBinDir,self.destBinDir)
290      lines.append(line)
291    oldFile.close()
292    newFile = open(src, 'w')
293    newFile.write(''.join(lines))
294    newFile.close()
295    return
296
297  def fixConf(self):
298    import shutil
299    for file in ['rules', 'rules_doc.mk', 'rules_util.mk', 'variables', 'petscrules', 'petscvariables']:
300      self.fixConfFile(os.path.join(self.destConfDir,file))
301    return
302
303  def fixPythonWheel(self):
304    import glob
305    import shutil
306    #
307    for pattern in (
308        self.destLibDir  + '/*.a',
309        self.destLibDir  + '/*.la',
310        self.destLibDir  + '/pkgconfig',  # TODO: keep?
311        self.destConfDir + '/configure-hash',
312        self.destConfDir + '/uninstall.py',
313        self.destConfDir + '/reconfigure-*.py',
314        self.destConfDir + '/pkg.conf.*',
315        self.destConfDir + '/pkg.git*.*',
316        self.destConfDir + '/modules',  # TODO: keep?
317        self.destShareDir + '/*/examples/src/*',
318        self.destShareDir + '/*/datafiles',
319    ):
320      for pathname in glob.glob(pattern):
321        if os.path.isdir(pathname):
322          shutil.rmtree(pathname)
323        elif os.path.exists(pathname):
324          os.remove(pathname)
325    #
326    if self.relocate_py_env:
327      pydir = sys.prefix
328      pylibdir = os.path.join(pydir, 'lib')
329      pysitedir = sysconfig.get_paths()["platlib"]
330      # petsc is installed in site-packages
331      petscdir = os.path.join(pysitedir, 'petsc')
332      petsclibdir = os.path.join(petscdir, 'lib')
333    for filename in (
334      self.destIncludeDir + '/petscconf.h',
335      self.destIncludeDir + '/petscconfiginfo.h',
336      self.destIncludeDir + '/petscmachineinfo.h',
337      self.destShareDir + '/petsc/examples/gmakefile.test',
338      self.destConfDir + '/rules',
339      self.destConfDir + '/rules_doc.mk',
340      self.destConfDir + '/rules_util.mk',
341      self.destConfDir + '/petscrules',
342      self.destConfDir + '/variables',
343      self.destConfDir + '/petscvariables',
344    ):
345      with open(filename, 'r') as oldFile:
346        contents = oldFile.read()
347      contents = contents.replace(self.installDir, '${PETSC_DIR}')
348      contents = contents.replace(self.rootDir, '${PETSC_DIR}')
349      if self.relocate_py_env:
350        pydir_from_petsc = os.path.relpath(pydir, petscdir)
351        contents = contents.replace(pydir, os.path.join('${PETSC_DIR}', pydir_from_petsc))
352      contents = re.sub(
353        r'^(PYTHON(_EXE)?) = (.*)$',
354        r'\1 = python%d' % sys.version_info[0],
355        contents, flags=re.MULTILINE,
356      )
357      with open(filename, 'w') as newFile:
358        newFile.write(contents)
359    #
360    def lsdir(dirname, *patterns):
361      return glob.glob(os.path.join(dirname, *patterns))
362    def shell(*args):
363      return self.executeShellCommand(' '.join(args))[0]
364    libdir = os.path.join(self.installDir, 'lib')
365    if sys.platform == 'linux':
366      libraries = [
367        lib for lib in lsdir(self.destLibDir, 'lib*.so*')
368        if not os.path.islink(lib)
369      ]
370      for shlib in libraries:
371        # fix shared library rpath
372        rpath = shell('patchelf', '--print-rpath', shlib)
373        rpath = rpath.split(os.path.pathsep)
374        if not self.relocate_py_env:
375          if libdir in rpath:
376            rpath.insert(0, '$ORIGIN')
377            while libdir in rpath:
378              rpath.remove(libdir)
379        else:
380          rpathold = rpath
381          rpath = []
382          # strip all rpath info, except for libraries in Python
383          # sys prefix, site-packages, or petsc/lib
384          rpath.insert(0, '$ORIGIN')
385          for libdir in rpathold:
386            if libdir.startswith(pysitedir):
387              libdir_from_petsc = os.path.relpath(libdir, petsclibdir)
388              rpath.insert(0, os.path.join('$ORIGIN',libdir_from_petsc))
389          pylibdir_from_petsc = os.path.relpath(pylibdir, petsclibdir)
390          rpath.insert(0, os.path.join('$ORIGIN',pylibdir_from_petsc))
391        if rpath:
392          rpath = os.path.pathsep.join(rpath)
393          shell('patchelf', '--set-rpath', "'%s'" % rpath, shlib)
394        # fix shared library file and symlink
395        basename = os.path.basename(shlib)
396        libname, ext, _ = basename.partition('.so')
397        liblink = libname + ext
398        soname = shell('patchelf', '--print-soname', shlib)
399        for symlink in lsdir(self.destLibDir, liblink + '*'):
400          if os.path.islink(symlink):
401            os.unlink(symlink)
402        curdir = os.getcwd()
403        try:
404          os.chdir(os.path.dirname(shlib))
405          if soname != basename:
406            os.rename(basename, soname)
407          if soname != liblink:
408            with open(liblink, 'w') as f:
409              f.write('INPUT(' + soname + ')\n')
410        finally:
411          os.chdir(curdir)
412    if sys.platform == 'darwin':
413      def otool(cmd, dylib):
414        pattern = r'''
415          ^\s+ cmd \s %s$\n
416          ^\s+ cmdsize \s \d+$\n
417          ^\s+ (?:name|path) \s (.*) \s \(offset \s \d+\)$
418        ''' % cmd
419        return re.findall(
420          pattern, shell('otool', '-l', dylib),
421          flags=re.VERBOSE | re.MULTILINE,
422        )
423      libraries = [
424        lib for lib in lsdir(self.destLibDir, 'lib*.dylib')
425        if not os.path.islink(lib)
426      ]
427      for dylib in libraries:
428        install_name = otool('LC_ID_DYLIB', dylib)[0]
429        dependencies = otool('LC_LOAD_DYLIB', dylib)
430        runtime_path = otool('LC_RPATH', dylib)
431        # fix shared library install name and rpath
432        install_name = '@rpath/' + os.path.basename(install_name)
433        shell('install_name_tool', '-id', install_name, dylib)
434        if libdir in runtime_path:
435          shell('install_name_tool', '-delete_rpath', libdir, dylib)
436        for rpath in ('@loader_path',):
437          if rpath not in runtime_path:
438            shell('install_name_tool', '-add_rpath', rpath, dylib)
439        for dep in dependencies:
440          if os.path.dirname(dep) in (libdir,):
441            newid = '@rpath/' + os.path.basename(dep)
442            shell('install_name_tool', '-change', dep, newid, dylib)
443        # fix shared library file and symlink
444        basename = os.path.basename(dylib)
445        libname, ext = os.path.splitext(basename)
446        libname = libname.partition('.')[0]
447        liblink = libname + ext
448        dyname = os.path.basename(install_name)
449        for symlink in lsdir(self.destLibDir, libname + '*' + ext):
450          if os.path.islink(symlink):
451            os.unlink(symlink)
452        curdir = os.getcwd()
453        try:
454          os.chdir(os.path.dirname(dylib))
455          if dyname != basename:
456            os.rename(basename, dyname)
457          if dyname != liblink:
458            os.symlink(dyname, liblink)
459        finally:
460          os.chdir(curdir)
461    #
462    return
463
464  def createUninstaller(self):
465    uninstallscript = os.path.join(self.destConfDir, 'uninstall.py')
466    f = open(uninstallscript, 'w')
467    # Could use the Python AST to do this
468    f.write('#!'+sys.executable+'\n')
469    f.write('import os\n')
470    f.write('prefixdir = "'+self.installDir+'"\n')
471    files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies]
472    files.append(uninstallscript.replace(self.destDir,self.installDir))
473    f.write('files = '+repr(files))
474    f.write('''
475for file in files:
476  if os.path.exists(file) or os.path.islink(file):
477    os.remove(file)
478    dir = os.path.dirname(file)
479    while dir not in [os.path.dirname(prefixdir),'/']:
480      try: os.rmdir(dir)
481      except: break
482      dir = os.path.dirname(dir)
483''')
484    f.close()
485    os.chmod(uninstallscript,0o744)
486    return
487
488  def installIncludes(self):
489    exclude = ['makefile']
490    if not hasattr(self.compilers.setCompilers, 'FC'):
491      exclude.append('finclude')
492    if not self.mpi.usingMPIUni:
493      exclude.append('mpiuni')
494    self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude))
495    self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir))
496    return
497
498  def installConf(self):
499    self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['test.log']))
500    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']))
501    return
502
503  def installBin(self):
504    exclude = ['bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml']
505    self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude ))
506    exclude = ['maint']
507    if not self.mpi.usingMPIUni:
508      exclude.append('petsc-mpiexec.uni')
509    self.setCompilers.pushLanguage('C')
510    if self.setCompilers.getCompiler().find('win32fe') < 0:
511      exclude.append('win32fe')
512    self.setCompilers.popLanguage()
513    self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude ))
514    return
515
516  def installShare(self):
517    if self.argDB['no-examples']: exclude = ['datafiles']
518    else: exclude = []
519    self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir, exclude=exclude))
520    examplesdir=os.path.join(self.destShareDir,'petsc','examples')
521    if os.path.exists(examplesdir):
522      shutil.rmtree(examplesdir)
523    os.mkdir(examplesdir)
524    os.mkdir(os.path.join(examplesdir,'src'))
525    self.copyConfig(self.rootDir,examplesdir)
526    if not self.argDB['no-examples']:
527        self.copyExamples(self.rootSrcDir,os.path.join(examplesdir,'src'))
528        self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test'))
529    return
530
531  def copyLib(self, src, dst):
532    '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac'''
533    # Symlinks (assumed local) are recreated at dst
534    if os.path.islink(src):
535      linkto = os.readlink(src)
536      try:
537        os.remove(dst)            # In case it already exists
538      except OSError:
539        pass
540      os.symlink(linkto, dst)
541      return
542    shutil.copy2(src, dst)
543    if self.setCompilers.getCompiler().find('win32fe') < 0 and os.path.splitext(dst)[1] == '.'+self.arLibSuffix:
544      import shlex
545      self.executeShellCommand(shlex.split(self.ranlib) + [dst])
546    if os.path.splitext(dst)[1] == '.dylib' and shutil.which('otool') and shutil.which('install_name_tool'):
547      [output,err,flg] = self.executeShellCommand(['otool', '-D', src])
548      oldname = output[output.find("\n")+1:]
549      installName = oldname.replace(os.path.realpath(self.archDir), self.installDir)
550      self.executeShellCommand(['install_name_tool', '-id', installName, dst])
551    # preserve the original timestamps - so that the .a vs .so time order is preserved
552    shutil.copystat(src,dst)
553    return
554
555  def installLib(self):
556    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
557    self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
558    return
559
560
561  def outputInstallDone(self):
562    from config.packages.make import getMakeUserPath
563    print('''\
564====================================
565To check if the libraries are working do (in current directory):
566%s PETSC_DIR=%s PETSC_ARCH="" check
567====================================\
568''' % (getMakeUserPath(self.arch), self.installDir))
569    return
570
571  def outputDestDirDone(self):
572    print('''\
573====================================
574Copy to DESTDIR %s is now complete.
575Before use - please copy/install over to specified prefix: %s
576====================================\
577''' % (self.destDir,self.installDir))
578    return
579
580  def runsetup(self):
581    self.setup()
582    self.setupDirectories()
583    self.setupBuild()
584    self.checkPrefix()
585    self.checkDestdir()
586    return
587
588  def runcopy(self):
589    if self.destDir == self.installDir:
590      print('*** Installing PETSc at prefix location:',self.destDir, ' ***')
591    else:
592      print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***')
593    if not os.path.exists(self.destDir):
594      try:
595        os.makedirs(self.destDir)
596      except:
597        print('********************************************************************')
598        print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"')
599        print('********************************************************************')
600        sys.exit(1)
601    self.installIncludes()
602    self.installConf()
603    self.installBin()
604    self.installLib()
605    self.installShare()
606    self.createUninstaller()
607    return
608
609  def runfix(self):
610    self.fixConf()
611    if self.using_build_backend:
612      self.fixPythonWheel()
613    return
614
615  def rundone(self):
616    if self.destDir == self.installDir:
617      self.outputInstallDone()
618    else:
619      self.outputDestDirDone()
620    return
621
622  def run(self):
623    self.runsetup()
624    self.runcopy()
625    self.runfix()
626    self.rundone()
627    return
628
629if __name__ == '__main__':
630  Installer(sys.argv[1:]).run()
631  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
632  delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp']
633  for delfile in delfiles:
634    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
635      os.remove(delfile)
636