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