xref: /petsc/config/install.py (revision bebe2cf65d55febe21a5af8db2bd2e168caaa2e7)
1#!/usr/bin/env python
2import 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('lib','petsc','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('lib','petsc','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, 'lib','petsc','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.options.arch',        None)
53    self.petscdir      = self.framework.require('PETSc.options.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, 'lib','petsc','conf')
71    self.archConfDir       = os.path.join(self.rootDir, self.arch, 'lib','petsc','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, 'lib','petsc','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 copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2):
122    """Copies a single file    """
123    copies = []
124    errors = []
125    if not os.path.exists(dst):
126      os.makedirs(dst)
127    elif not os.path.isdir(dst):
128      raise shutil.Error, 'Destination is not a directory'
129    srcname = src
130    dstname = os.path.join(dst, os.path.basename(src))
131    try:
132      if symlinks and os.path.islink(srcname):
133        linkto = os.readlink(srcname)
134        os.symlink(linkto, dstname)
135      else:
136        copyFunc(srcname, dstname)
137        copies.append((srcname, dstname))
138    except (IOError, os.error), why:
139      errors.append((srcname, dstname, str(why)))
140    except shutil.Error, err:
141      errors.extend((srcname,dstname,str(err.args[0])))
142    if errors:
143      raise shutil.Error, errors
144    return copies
145
146
147  def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = []):
148    """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2().
149
150       The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2
151
152    The destination directory must not already exist.
153    If exception(s) occur, an shutil.Error is raised with a list of reasons.
154
155    If the optional symlinks flag is true, symbolic links in the
156    source tree result in symbolic links in the destination tree; if
157    it is false, the contents of the files pointed to by symbolic
158    links are copied.
159    """
160    copies = []
161    names  = os.listdir(src)
162    if not os.path.exists(dst):
163      os.makedirs(dst)
164    elif not os.path.isdir(dst):
165      raise shutil.Error, 'Destination is not a directory'
166    errors = []
167    for name in names:
168      srcname = os.path.join(src, name)
169      dstname = os.path.join(dst, name)
170      try:
171        if symlinks and os.path.islink(srcname):
172          linkto = os.readlink(srcname)
173          os.symlink(linkto, dstname)
174        elif os.path.isdir(srcname):
175          copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude))
176        elif not os.path.basename(srcname) in exclude:
177          copyFunc(srcname, dstname)
178          copies.append((srcname, dstname))
179        # XXX What about devices, sockets etc.?
180      except (IOError, os.error), why:
181        errors.append((srcname, dstname, str(why)))
182      # catch the Error from the recursive copytree so that we can
183      # continue with other files
184      except shutil.Error, err:
185        errors.extend((srcname,dstname,str(err.args[0])))
186    try:
187      shutil.copystat(src, dst)
188    except OSError, e:
189      if WindowsError is not None and isinstance(e, WindowsError):
190        # Copying file access times may fail on Windows
191        pass
192      else:
193        errors.extend((src, dst, str(e)))
194    if errors:
195      raise shutil.Error, errors
196    return copies
197
198
199  def fixConfFile(self, src):
200    lines   = []
201    oldFile = open(src, 'r')
202    for line in oldFile.readlines():
203      # paths generated by configure could be different link-path than whats used by user, so fix both
204      line = line.replace(os.path.join(self.rootDir, self.arch), self.installDir)
205      line = line.replace(os.path.realpath(os.path.join(self.rootDir, self.arch)), self.installDir)
206      line = line.replace(os.path.join(self.rootDir, 'bin'), self.installBinDir)
207      line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'bin')), self.installBinDir)
208      line = line.replace(os.path.join(self.rootDir, 'include'), self.installIncludeDir)
209      line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'include')), self.installIncludeDir)
210      # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary
211      line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir)
212      line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '')
213      line = line.replace('${PETSC_DIR}', self.installDir)
214      lines.append(line)
215    oldFile.close()
216    newFile = open(src, 'w')
217    newFile.write(''.join(lines))
218    newFile.close()
219    return
220
221  def fixConf(self):
222    import shutil
223    for file in ['rules', 'variables','petscrules', 'petscvariables']:
224      self.fixConfFile(os.path.join(self.destConfDir,file))
225    self.fixConfFile(os.path.join(self.destLibDir,'pkgconfig','PETSc.pc'))
226    return
227
228  def createUninstaller(self):
229    uninstallscript = os.path.join(self.destConfDir, 'uninstall.py')
230    f = open(uninstallscript, 'w')
231    # Could use the Python AST to do this
232    f.write('#!'+sys.executable+'\n')
233    f.write('import os\n')
234
235    f.write('copies = '+repr(self.copies).replace(self.destDir,self.installDir))
236    f.write('''
237for src, dst in copies:
238  try:
239    os.remove(dst)
240  except:
241    pass
242''')
243    #TODO: need to delete libXXX.YYY.dylib.dSYM directory on Mac
244    dirs = [os.path.join('include','petsc','finclude'),os.path.join('include','petsc','mpiuni'),os.path.join('include','petsc','private'),os.path.join('bin'),os.path.join('lib','petsc','conf')]
245    newdirs = []
246    for dir in dirs: newdirs.append(os.path.join(self.installDir,dir))
247    f.write('dirs = '+str(newdirs))
248    f.write('''
249for dir in dirs:
250  import shutil
251  try:
252    shutil.rmtree(dir)
253  except:
254    pass
255''')
256    f.close()
257    os.chmod(uninstallscript,0744)
258    return
259
260  def installIncludes(self):
261    # TODO: should exclude petsc-mpi.uni except for uni builds
262    # TODO: should exclude petsc/finclude except for fortran builds
263    self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = ['makefile']))
264    self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir))
265    return
266
267  def installConf(self):
268    self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir))
269    self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp']))
270    return
271
272  def installBin(self):
273    self.copies.extend(self.copytree(os.path.join(self.rootBinDir), os.path.join(self.destBinDir)))
274    # TODO: should copy over petsc-mpiexec.uni only for uni builds
275    self.copies.extend(self.copyfile(os.path.join(self.rootBinDir,'petsc-mpiexec.uni'), self.destBinDir))
276    self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml']))
277    return
278
279  def installShare(self):
280    self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir))
281    return
282
283  def copyLib(self, src, dst):
284    '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac'''
285    # Symlinks (assumed local) are recreated at dst
286    if os.path.islink(src):
287      linkto = os.readlink(src)
288      try:
289        os.remove(dst)            # In case it already exists
290      except OSError:
291        pass
292      os.symlink(linkto, dst)
293      return
294    # Do not install object files
295    if not os.path.splitext(src)[1] == '.o':
296      shutil.copy2(src, dst)
297    if os.path.splitext(dst)[1] == '.'+self.arLibSuffix:
298      self.executeShellCommand(self.ranlib+' '+dst)
299    if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'):
300      installName = dst.replace(self.destDir, self.installDir)
301      self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst)
302    # preserve the original timestamps - so that the .a vs .so time order is preserved
303    shutil.copystat(src,dst)
304    return
305
306  def installLib(self):
307    self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR', 'sowing']))
308    return
309
310
311  def outputInstallDone(self):
312    print '''\
313====================================
314Install complete.
315Now to check if the libraries are working do (in current directory):
316make PETSC_DIR=%s PETSC_ARCH="" test
317====================================\
318''' % (self.installDir)
319    return
320
321  def outputDestDirDone(self):
322    print '''\
323====================================
324Copy to DESTDIR %s is now complete.
325Before use - please copy/install over to specified prefix: %s
326====================================\
327''' % (self.destDir,self.installDir)
328    return
329
330  def runsetup(self):
331    self.setup()
332    self.setupDirectories()
333    self.checkPrefix()
334    self.checkDestdir()
335    return
336
337  def runcopy(self):
338    if self.destDir == self.installDir:
339      print '*** Installing PETSc at prefix location:',self.destDir, ' ***'
340    else:
341      print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***'
342    if not os.path.exists(self.destDir):
343      try:
344        os.makedirs(self.destDir)
345      except:
346        print '********************************************************************'
347        print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"'
348        print '********************************************************************'
349        sys.exit(1)
350    self.installIncludes()
351    self.installConf()
352    self.installBin()
353    self.installLib()
354    self.installShare()
355    return
356
357  def runfix(self):
358    self.fixConf()
359    return
360
361  def rundone(self):
362    self.createUninstaller()
363    if self.destDir == self.installDir:
364      self.outputInstallDone()
365    else:
366      self.outputDestDirDone()
367    return
368
369  def run(self):
370    self.runsetup()
371    self.runcopy()
372    self.runfix()
373    self.rundone()
374    return
375
376if __name__ == '__main__':
377  Installer(sys.argv[1:]).run()
378  # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked
379  delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp']
380  for delfile in delfiles:
381    if os.path.exists(delfile) and (os.stat(delfile).st_uid==0):
382      os.remove(delfile)
383