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