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