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