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