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