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