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