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