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