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 [output,err,flg] = self.executeShellCommand("otool -D "+src) 301 oldname = output[output.find("\n")+1:] 302 installName = oldname.replace(self.destDir, self.installDir) 303 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 304 # preserve the original timestamps - so that the .a vs .so time order is preserved 305 shutil.copystat(src,dst) 306 return 307 308 def installLib(self): 309 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR', 'sowing'])) 310 return 311 312 313 def outputInstallDone(self): 314 print '''\ 315==================================== 316Install complete. 317Now to check if the libraries are working do (in current directory): 318make PETSC_DIR=%s PETSC_ARCH="" test 319====================================\ 320''' % (self.installDir) 321 return 322 323 def outputDestDirDone(self): 324 print '''\ 325==================================== 326Copy to DESTDIR %s is now complete. 327Before use - please copy/install over to specified prefix: %s 328====================================\ 329''' % (self.destDir,self.installDir) 330 return 331 332 def runsetup(self): 333 self.setup() 334 self.setupDirectories() 335 self.checkPrefix() 336 self.checkDestdir() 337 return 338 339 def runcopy(self): 340 if self.destDir == self.installDir: 341 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 342 else: 343 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 344 if not os.path.exists(self.destDir): 345 try: 346 os.makedirs(self.destDir) 347 except: 348 print '********************************************************************' 349 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 350 print '********************************************************************' 351 sys.exit(1) 352 self.installIncludes() 353 self.installConf() 354 self.installBin() 355 self.installLib() 356 self.installShare() 357 return 358 359 def runfix(self): 360 self.fixConf() 361 return 362 363 def rundone(self): 364 self.createUninstaller() 365 if self.destDir == self.installDir: 366 self.outputInstallDone() 367 else: 368 self.outputDestDirDone() 369 return 370 371 def run(self): 372 self.runsetup() 373 self.runcopy() 374 self.runfix() 375 self.rundone() 376 return 377 378if __name__ == '__main__': 379 Installer(sys.argv[1:]).run() 380 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 381 delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp'] 382 for delfile in delfiles: 383 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 384 os.remove(delfile) 385