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