xref: /petsc/config/install.py (revision 71fd2e92b2fee25f70818ffc1dca9fc57f8584d7)
1#!/usr/bin/env python
2import re, os, sys, shutil
3
4sys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config'))
5sys.path.insert(0, os.path.join(os.environ['PETSC_DIR'], 'config', 'BuildSystem'))
6
7import script
8
9try:
10  WindowsError
11except NameError:
12  WindowsError = None
13
14class Installer(script.Script):
15  def __init__(self, clArgs = None):
16    import RDict
17    argDB = RDict.RDict(None, None, 0, 0, readonly = True)
18    argDB.saveFilename = os.path.join(os.environ['PETSC_DIR'], os.environ['PETSC_ARCH'], 'conf', 'RDict.db')
19    argDB.load()
20    script.Script.__init__(self, argDB = argDB)
21    self.copies = []
22    return
23
24  def setupModules(self):
25    self.setCompilers  = self.framework.require('config.setCompilers',         None)
26    self.arch          = self.framework.require('PETSc.utilities.arch',        None)
27    self.petscdir      = self.framework.require('PETSc.utilities.petscdir',    None)
28    self.makesys       = self.framework.require('PETSc.utilities.Make',        None)
29    self.compilers     = self.framework.require('config.compilers',            None)
30    return
31
32  def setup(self):
33    script.Script.setup(self)
34    self.framework = self.loadConfigure()
35    self.setupModules()
36    return
37
38  def setupDirectories(self):
39    self.rootDir    = self.petscdir.dir
40    self.installDir = self.framework.argDB['prefix']
41    self.arch       = self.arch.arch
42    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
43    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
44    self.rootConfDir       = os.path.join(self.rootDir, 'conf')
45    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'conf')
46    self.rootBinDir        = os.path.join(self.rootDir, 'bin')
47    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
48    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
49    self.installIncludeDir = os.path.join(self.installDir, 'include')
50    self.installConfDir    = os.path.join(self.installDir, 'conf')
51    self.installLibDir     = os.path.join(self.installDir, 'lib')
52    self.installBinDir     = os.path.join(self.installDir, 'bin')
53
54    self.make      = self.makesys.make+' '+self.makesys.flags
55    self.ranlib    = self.compilers.RANLIB
56    self.libSuffix = self.compilers.AR_LIB_SUFFIX
57    return
58
59  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
60    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
61
62    The destination directory must not already exist.
63    If exception(s) occur, an shutil.Error is raised with a list of reasons.
64
65    If the optional symlinks flag is true, symbolic links in the
66    source tree result in symbolic links in the destination tree; if
67    it is false, the contents of the files pointed to by symbolic
68    links are copied.
69    """
70    copies = []
71    names  = os.listdir(src)
72    if not os.path.exists(dst):
73      os.makedirs(dst)
74    elif not os.path.isdir(dst):
75      raise shutil.Error, 'Destination is not a directory'
76    errors = []
77    for name in names:
78      srcname = os.path.join(src, name)
79      dstname = os.path.join(dst, name)
80      try:
81        if symlinks and os.path.islink(srcname):
82          linkto = os.readlink(srcname)
83          os.symlink(linkto, dstname)
84        elif os.path.isdir(srcname):
85          copies.extend(self.copytree(srcname, dstname, symlinks))
86        else:
87          copyFunc(srcname, dstname)
88          copies.append((srcname, dstname))
89        # XXX What about devices, sockets etc.?
90      except (IOError, os.error), why:
91        errors.append((srcname, dstname, str(why)))
92      # catch the Error from the recursive copytree so that we can
93      # continue with other files
94      except shutil.Error, err:
95        errors.extend(err.args[0])
96    try:
97      shutil.copystat(src, dst)
98    except OSError as why:
99      if WindowsError is not None and isinstance(why, WindowsError):
100        # Copying file access times may fail on Windows
101        pass
102      else:
103        errors.extend((src, dst, str(why)))
104    if errors:
105      raise shutil.Error, errors
106    return copies
107
108  def installIncludes(self):
109    self.copies.extend(self.copytree(self.rootIncludeDir, self.installIncludeDir))
110    self.copies.extend(self.copytree(self.archIncludeDir, self.installIncludeDir))
111    return
112
113  def copyConf(self, src, dst):
114    if os.path.isdir(dst):
115      dst = os.path.join(dst, os.path.basename(src))
116    lines   = []
117    oldFile = open(src, 'r')
118    for line in oldFile.readlines():
119      # paths generated by configure could be different link-path than whats used by user, so fix both
120      line = re.sub(re.escape(os.path.join(self.rootDir, self.arch)), self.installDir, line)
121      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, self.arch))), self.installDir, line)
122      line = re.sub(re.escape(os.path.join(self.rootDir, 'bin')), self.installDir, line)
123      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, 'bin'))), self.installDir, line)
124      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
125      line = re.sub('\$\{PETSC_DIR\}/\$\{PETSC_ARCH\}', self.installDir, line)
126      line = re.sub('PETSC_ARCH=\$\{PETSC_ARCH\}', '', line)
127      line = re.sub('\$\{PETSC_DIR\}', self.installDir, line)
128      lines.append(line)
129    oldFile.close()
130    newFile = open(dst, 'w')
131    newFile.write(''.join(lines))
132    newFile.close()
133    shutil.copystat(src, dst)
134    return
135
136  def installConf(self):
137    # rootConfDir can have a duplicate petscvariables - so processing it first removes the appropriate duplicate file.
138    self.copies.extend(self.copytree(self.rootConfDir, self.installConfDir, copyFunc = self.copyConf))
139    self.copies.extend(self.copytree(self.archConfDir, self.installConfDir))
140    # Just copyConf() a couple of files manually [as the rest of the files should not be modified]
141    for file in ['petscrules', 'petscvariables']:
142      self.copyConf(os.path.join(self.archConfDir,file),os.path.join(self.installConfDir,file))
143    return
144
145  def installBin(self):
146    self.copies.extend(self.copytree(self.rootBinDir, self.installBinDir))
147    self.copies.extend(self.copytree(self.archBinDir, self.installBinDir))
148    return
149
150  def copyLib(self, src, dst):
151    '''Run ranlib on the destination library if it is an archive'''
152    shutil.copy2(src, dst)
153    if os.path.splitext(dst)[1] == '.'+self.libSuffix:
154      self.executeShellCommand(self.ranlib+' '+dst)
155    return
156
157  def installLib(self):
158    self.copies.extend(self.copytree(self.archLibDir, self.installLibDir, copyFunc = self.copyLib))
159    return
160
161  def createUninstaller(self):
162    uninstallscript = os.path.join(self.installConfDir, 'uninstall.py')
163    f = open(uninstallscript, 'w')
164    # Could use the Python AST to do this
165    f.write('#!'+sys.executable+'\n')
166    f.write('import os\n')
167
168    f.write('copies = '+repr(self.copies))
169    f.write('''
170for src, dst in copies:
171  if os.path.exists(dst):
172    os.remove(dst)
173''')
174    f.close()
175    os.chmod(uninstallscript,0744)
176    return
177
178  def outputHelp(self):
179    print '''
180====================================
181If using sh/bash, do the following:
182  PETSC_DIR=%s; export PETSC_DIR
183  unset PETSC_ARCH
184If using csh/tcsh, do the following:
185  setenv PETSC_DIR %s
186  unsetenv PETSC_ARCH
187Run the following to verify the install (remain in current directory for the tests):
188  make test
189====================================
190''' % (self.installDir, self.installDir)
191    return
192
193  def run(self):
194    self.setup()
195    self.setupDirectories()
196    if os.path.exists(self.installDir) and os.path.samefile(self.installDir, os.path.join(self.rootDir,self.arch)):
197      print '********************************************************************'
198      print 'Install directory is current directory; nothing needs to be done'
199      print '********************************************************************'
200      return
201    print '*** Installing PETSc at',self.installDir, ' ***'
202    if not os.path.exists(self.installDir):
203      try:
204        os.makedirs(self.installDir)
205      except:
206        print '********************************************************************'
207        print 'Unable to create', self.installDir, 'Perhaps you need to do "sudo make install"'
208        print '********************************************************************'
209        return
210    if not os.path.isdir(os.path.realpath(self.installDir)):
211      print '********************************************************************'
212      print 'Specified prefix', self.installDir, 'is not a directory. Cannot proceed!'
213      print '********************************************************************'
214      return
215    if not os.access(self.installDir, os.W_OK):
216      print '********************************************************************'
217      print 'Unable to write to ', self.installDir, 'Perhaps you need to do "sudo make install"'
218      print '********************************************************************'
219      return
220    self.installIncludes()
221    self.installConf()
222    self.installBin()
223    self.installLib()
224    output = self.executeShellCommand(self.make+' PETSC_ARCH=""'+' PETSC_DIR='+self.installDir+' shared mpi4py petsc4py')[0]
225    print output
226    # this file will mess up the make test run since it resets PETSC_ARCH when PETSC_ARCH needs to be null now
227    os.unlink(os.path.join(self.rootDir,'conf','petscvariables'))
228    self.createUninstaller()
229    self.outputHelp()
230    return
231
232if __name__ == '__main__':
233  Installer(sys.argv[1:]).run()
234  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
235  delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp']
236  for delfile in delfiles:
237    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
238      os.remove(delfile)
239