xref: /petsc/config/install.py (revision 5d8720fa41fb4169420198de95a3fb9ffc339d07)
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      import shlex
512      self.executeShellCommand(shlex.split(self.ranlib) + [dst])
513    if os.path.splitext(dst)[1] == '.dylib' and shutil.which('otool') and shutil.which('install_name_tool'):
514      [output,err,flg] = self.executeShellCommand(['otool', '-D', src])
515      oldname = output[output.find("\n")+1:]
516      installName = oldname.replace(os.path.realpath(self.archDir), self.installDir)
517      self.executeShellCommand(['install_name_tool', '-id', installName, dst])
518    # preserve the original timestamps - so that the .a vs .so time order is preserved
519    shutil.copystat(src,dst)
520    return
521
522  def installLib(self):
523    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
524    self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0))
525    return
526
527
528  def outputInstallDone(self):
529    from config.packages.make import getMakeUserPath
530    print('''\
531====================================
532Install complete.
533Now to check if the libraries are working do (in current directory):
534%s PETSC_DIR=%s PETSC_ARCH="" check
535====================================\
536''' % (getMakeUserPath(self.arch), self.installDir))
537    return
538
539  def outputDestDirDone(self):
540    print('''\
541====================================
542Copy to DESTDIR %s is now complete.
543Before use - please copy/install over to specified prefix: %s
544====================================\
545''' % (self.destDir,self.installDir))
546    return
547
548  def runsetup(self):
549    self.setup()
550    self.setupDirectories()
551    self.checkPrefix()
552    self.checkDestdir()
553    return
554
555  def runcopy(self):
556    if self.destDir == self.installDir:
557      print('*** Installing PETSc at prefix location:',self.destDir, ' ***')
558    else:
559      print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***')
560    if not os.path.exists(self.destDir):
561      try:
562        os.makedirs(self.destDir)
563      except:
564        print('********************************************************************')
565        print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"')
566        print('********************************************************************')
567        sys.exit(1)
568    self.installIncludes()
569    self.installConf()
570    self.installBin()
571    self.installLib()
572    self.installShare()
573    self.createUninstaller()
574    return
575
576  def runfix(self):
577    self.fixConf()
578    using_build_backend = any(
579      os.environ.get(prefix + '_BUILD_BACKEND')
580      for prefix in ('_PYPROJECT_HOOKS', 'PEP517')
581    )
582    if using_build_backend:
583      self.fixPythonWheel()
584    return
585
586  def rundone(self):
587    if self.destDir == self.installDir:
588      self.outputInstallDone()
589    else:
590      self.outputDestDirDone()
591    return
592
593  def run(self):
594    self.runsetup()
595    self.runcopy()
596    self.runfix()
597    self.rundone()
598    return
599
600if __name__ == '__main__':
601  Installer(sys.argv[1:]).run()
602  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
603  delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp']
604  for delfile in delfiles:
605    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
606      os.remove(delfile)
607