xref: /petsc/config/install.py (revision d20b5d95fb5ed4c01fb0da19d63ae2a035fb24e8)
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    if not clArgs is None: self.clArgs = clArgs
48    self.copies = []
49    return
50
51  def setupHelp(self, help):
52    import nargs
53    script.Script.setupHelp(self, help)
54    help.addArgument('Installer', '-destDir=<path>', nargs.Arg(None, None, 'Destination Directory for install'))
55    return
56
57
58  def setupModules(self):
59    self.setCompilers  = self.framework.require('config.setCompilers',         None)
60    self.arch          = self.framework.require('PETSc.utilities.arch',        None)
61    self.petscdir      = self.framework.require('PETSc.utilities.petscdir',    None)
62    self.makesys       = self.framework.require('config.programs',             None)
63    self.compilers     = self.framework.require('config.compilers',            None)
64    return
65
66  def setup(self):
67    script.Script.setup(self)
68    self.framework = self.loadConfigure()
69    self.setupModules()
70    return
71
72  def setupDirectories(self):
73    self.rootDir    = self.petscdir.dir
74    self.destDir    = os.path.abspath(self.argDB['destDir'])
75    self.installDir = self.framework.argDB['prefix']
76    self.arch       = self.arch.arch
77    self.rootIncludeDir    = os.path.join(self.rootDir, 'include')
78    self.archIncludeDir    = os.path.join(self.rootDir, self.arch, 'include')
79    self.rootConfDir       = os.path.join(self.rootDir, 'conf')
80    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'conf')
81    self.rootBinDir        = os.path.join(self.rootDir, 'bin')
82    self.archBinDir        = os.path.join(self.rootDir, self.arch, 'bin')
83    self.archLibDir        = os.path.join(self.rootDir, self.arch, 'lib')
84    self.destIncludeDir    = os.path.join(self.destDir, 'include')
85    self.destConfDir       = os.path.join(self.destDir, 'conf')
86    self.destLibDir        = os.path.join(self.destDir, 'lib')
87    self.destBinDir        = os.path.join(self.destDir, 'bin')
88    self.installIncludeDir = os.path.join(self.installDir, 'include')
89    self.installBinDir     = os.path.join(self.installDir, 'bin')
90    self.rootShareDir      = os.path.join(self.rootDir, 'share')
91    self.destShareDir      = os.path.join(self.destDir, 'share')
92
93    self.make      = self.makesys.make+' '+self.makesys.flags
94    self.ranlib    = self.compilers.RANLIB
95    self.libSuffix = self.compilers.AR_LIB_SUFFIX
96    return
97
98  def checkPrefix(self):
99    if not self.installDir:
100      print '********************************************************************'
101      print 'PETSc is built without prefix option. So "make install" is not appropriate.'
102      print 'If you need a prefix install of PETSc - rerun configure with --prefix option.'
103      print '********************************************************************'
104      sys.exit(1)
105    return
106
107  def checkDestdir(self):
108    if os.path.exists(self.destDir):
109      if os.path.samefile(self.destDir, self.rootDir):
110        print '********************************************************************'
111        print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR'
112        print '********************************************************************'
113        sys.exit(1)
114      if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)):
115        print '********************************************************************'
116        print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH'
117        print '********************************************************************'
118        sys.exit(1)
119      if not os.path.isdir(os.path.realpath(self.destDir)):
120        print '********************************************************************'
121        print 'Specified destDir', self.destDir, 'is not a directory. Cannot proceed!'
122        print '********************************************************************'
123        sys.exit(1)
124      if not os.access(self.destDir, os.W_OK):
125        print '********************************************************************'
126        print 'Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"'
127        print '********************************************************************'
128        sys.exit(1)
129    return
130
131  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
132    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
133
134    The destination directory must not already exist.
135    If exception(s) occur, an shutil.Error is raised with a list of reasons.
136
137    If the optional symlinks flag is true, symbolic links in the
138    source tree result in symbolic links in the destination tree; if
139    it is false, the contents of the files pointed to by symbolic
140    links are copied.
141    """
142    copies = []
143    names  = os.listdir(src)
144    if not os.path.exists(dst):
145      os.makedirs(dst)
146    elif not os.path.isdir(dst):
147      raise shutil.Error, 'Destination is not a directory'
148    errors = []
149    for name in names:
150      srcname = os.path.join(src, name)
151      dstname = os.path.join(dst, name)
152      try:
153        if symlinks and os.path.islink(srcname):
154          linkto = os.readlink(srcname)
155          os.symlink(linkto, dstname)
156        elif os.path.isdir(srcname):
157          copies.extend(self.copytree(srcname, dstname, symlinks))
158        else:
159          copyFunc(srcname, dstname)
160          copies.append((srcname, dstname))
161        # XXX What about devices, sockets etc.?
162      except (IOError, os.error), why:
163        errors.append((srcname, dstname, str(why)))
164      # catch the Error from the recursive copytree so that we can
165      # continue with other files
166      except shutil.Error, err:
167        errors.extend((srcname,dstname,str(err.args[0])))
168    try:
169      shutil.copystat(src, dst)
170    except OSError, e:
171      if WindowsError is not None and isinstance(e, WindowsError):
172        # Copying file access times may fail on Windows
173        pass
174      else:
175        errors.extend((src, dst, str(e)))
176    if errors:
177      raise shutil.Error, errors
178    return copies
179
180
181  def fixConfFile(self, src):
182    lines   = []
183    oldFile = open(src, 'r')
184    for line in oldFile.readlines():
185      # paths generated by configure could be different link-path than whats used by user, so fix both
186      line = re.sub(re.escape(os.path.join(self.rootDir, self.arch)), self.installDir, line)
187      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, self.arch))), self.installDir, line)
188      line = re.sub(re.escape(os.path.join(self.rootDir, 'bin')), self.installBinDir, line)
189      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, 'bin'))), self.installBinDir, line)
190      line = re.sub(re.escape(os.path.join(self.rootDir, 'include')), self.installIncludeDir, line)
191      line = re.sub(re.escape(os.path.realpath(os.path.join(self.rootDir, 'include'))), self.installIncludeDir, line)
192      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
193      line = re.sub('\$\{PETSC_DIR\}/\$\{PETSC_ARCH\}', self.installDir, line)
194      line = re.sub('PETSC_ARCH=\$\{PETSC_ARCH\}', '', line)
195      line = re.sub('\$\{PETSC_DIR\}', self.installDir, line)
196      lines.append(line)
197    oldFile.close()
198    newFile = open(src, 'w')
199    newFile.write(''.join(lines))
200    newFile.close()
201    return
202
203  def fixConf(self):
204    import shutil
205    # copy standard rules and variables file so that we can change them in place
206    for file in ['rules', 'variables']:
207      shutil.copy2(os.path.join(self.rootConfDir,file),os.path.join(self.archConfDir,file))
208    for file in ['rules', 'variables','petscrules', 'petscvariables']:
209      self.fixConfFile(os.path.join(self.archConfDir,file))
210
211  def createUninstaller(self):
212    uninstallscript = os.path.join(self.archConfDir, 'uninstall.py')
213    f = open(uninstallscript, 'w')
214    # Could use the Python AST to do this
215    f.write('#!'+sys.executable+'\n')
216    f.write('import os\n')
217
218    f.write('copies = '+re.sub(self.destDir,self.installDir,repr(self.copies)))
219    f.write('''
220for src, dst in copies:
221  if os.path.exists(dst):
222    os.remove(dst)
223''')
224    f.close()
225    os.chmod(uninstallscript,0744)
226    return
227
228  def installIncludes(self):
229    self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir))
230    self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir))
231    return
232
233  def installConf(self):
234    self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir))
235    self.copies.extend(self.copytree(self.archConfDir, self.destConfDir))
236
237  def installBin(self):
238    self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir))
239    self.copies.extend(self.copytree(self.archBinDir, self.destBinDir))
240    return
241
242  def installShare(self):
243    self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir))
244    return
245
246  def copyLib(self, src, dst):
247    '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac'''
248    # Do not install object files
249    if not os.path.splitext(src)[1] == '.o':
250      shutil.copy2(src, dst)
251    if os.path.splitext(dst)[1] == '.'+self.libSuffix:
252      self.executeShellCommand(self.ranlib+' '+dst)
253    if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'):
254      installName = re.sub(self.destDir, self.installDir, dst)
255      self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst)
256    # preserve the original timestamps - so that the .a vs .so time order is preserved
257    shutil.copystat(src,dst)
258    return
259
260  def installLib(self):
261    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib))
262    return
263
264
265  def outputDone(self):
266    print '''\
267====================================
268Install complete. It is useable with PETSC_DIR=%s [and no more PETSC_ARCH].
269Now to check if the libraries are working do (in current directory):
270make PETSC_DIR=%s test
271====================================\
272''' % (self.installDir,self.installDir)
273    return
274
275  def runfix(self):
276    self.setup()
277    self.setupDirectories()
278    self.checkPrefix()
279    self.checkDestdir()
280    self.createUninstaller()
281    self.fixConf()
282
283
284  def runcopy(self):
285    print '*** Installing PETSc at',self.destDir, ' ***'
286    if not os.path.exists(self.destDir):
287      try:
288        os.makedirs(self.destDir)
289      except:
290        print '********************************************************************'
291        print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"'
292        print '********************************************************************'
293        sys.exit(1)
294    self.installIncludes()
295    self.installConf()
296    self.installBin()
297    self.installLib()
298    self.installShare()
299    self.outputDone()
300
301    return
302
303  def run(self):
304    self.runfix()
305    self.runcopy()
306
307if __name__ == '__main__':
308  Installer(sys.argv[1:]).run()
309  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
310  delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp']
311  for delfile in delfiles:
312    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
313      os.remove(delfile)
314