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 = os.path.abspath(self.framework.argDB['prefix']) 67 self.arch = self.arch.arch 68 self.archDir = os.path.join(self.rootDir, self.arch) 69 self.rootIncludeDir = os.path.join(self.rootDir, 'include') 70 self.archIncludeDir = os.path.join(self.rootDir, self.arch, 'include') 71 self.rootConfDir = os.path.join(self.rootDir, 'lib','petsc','conf') 72 self.archConfDir = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf') 73 self.rootBinDir = os.path.join(self.rootDir, 'bin') 74 self.archBinDir = os.path.join(self.rootDir, self.arch, 'bin') 75 self.archLibDir = os.path.join(self.rootDir, self.arch, 'lib') 76 self.destIncludeDir = os.path.join(self.destDir, 'include') 77 self.destConfDir = os.path.join(self.destDir, 'lib','petsc','conf') 78 self.destLibDir = os.path.join(self.destDir, 'lib') 79 self.destBinDir = os.path.join(self.destDir, 'bin') 80 self.installIncludeDir = os.path.join(self.installDir, 'include') 81 self.installBinDir = os.path.join(self.installDir, 'bin') 82 self.rootShareDir = os.path.join(self.rootDir, 'share') 83 self.destShareDir = os.path.join(self.destDir, 'share') 84 85 self.ranlib = self.compilers.RANLIB 86 self.arLibSuffix = self.compilers.AR_LIB_SUFFIX 87 return 88 89 def checkPrefix(self): 90 if not self.installDir: 91 print '********************************************************************' 92 print 'PETSc is built without prefix option. So "make install" is not appropriate.' 93 print 'If you need a prefix install of PETSc - rerun configure with --prefix option.' 94 print '********************************************************************' 95 sys.exit(1) 96 return 97 98 def checkDestdir(self): 99 if os.path.exists(self.destDir): 100 if os.path.samefile(self.destDir, self.rootDir): 101 print '********************************************************************' 102 print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR' 103 print '********************************************************************' 104 sys.exit(1) 105 if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)): 106 print '********************************************************************' 107 print 'Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH' 108 print '********************************************************************' 109 sys.exit(1) 110 if not os.path.isdir(os.path.realpath(self.destDir)): 111 print '********************************************************************' 112 print 'Specified destDir', self.destDir, 'is not a directory. Cannot proceed!' 113 print '********************************************************************' 114 sys.exit(1) 115 if not os.access(self.destDir, os.W_OK): 116 print '********************************************************************' 117 print 'Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"' 118 print '********************************************************************' 119 sys.exit(1) 120 return 121 122 def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2): 123 """Copies a single file """ 124 copies = [] 125 errors = [] 126 if not os.path.exists(dst): 127 os.makedirs(dst) 128 elif not os.path.isdir(dst): 129 raise shutil.Error, 'Destination is not a directory' 130 srcname = src 131 dstname = os.path.join(dst, os.path.basename(src)) 132 try: 133 if symlinks and os.path.islink(srcname): 134 linkto = os.readlink(srcname) 135 os.symlink(linkto, dstname) 136 else: 137 copyFunc(srcname, dstname) 138 copies.append((srcname, dstname)) 139 except (IOError, os.error), why: 140 errors.append((srcname, dstname, str(why))) 141 except shutil.Error, err: 142 errors.extend((srcname,dstname,str(err.args[0]))) 143 if errors: 144 raise shutil.Error, errors 145 return copies 146 147 148 def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = []): 149 """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2(). 150 151 The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2 152 153 The destination directory must not already exist. 154 If exception(s) occur, an shutil.Error is raised with a list of reasons. 155 156 If the optional symlinks flag is true, symbolic links in the 157 source tree result in symbolic links in the destination tree; if 158 it is false, the contents of the files pointed to by symbolic 159 links are copied. 160 """ 161 copies = [] 162 names = os.listdir(src) 163 if not os.path.exists(dst): 164 os.makedirs(dst) 165 elif not os.path.isdir(dst): 166 raise shutil.Error, 'Destination is not a directory' 167 errors = [] 168 for name in names: 169 srcname = os.path.join(src, name) 170 dstname = os.path.join(dst, name) 171 try: 172 if symlinks and os.path.islink(srcname): 173 linkto = os.readlink(srcname) 174 os.symlink(linkto, dstname) 175 elif os.path.isdir(srcname): 176 copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude)) 177 elif not os.path.basename(srcname) in exclude: 178 copyFunc(srcname, dstname) 179 copies.append((srcname, dstname)) 180 # XXX What about devices, sockets etc.? 181 except (IOError, os.error), why: 182 errors.append((srcname, dstname, str(why))) 183 # catch the Error from the recursive copytree so that we can 184 # continue with other files 185 except shutil.Error, err: 186 errors.extend((srcname,dstname,str(err.args[0]))) 187 try: 188 shutil.copystat(src, dst) 189 except OSError, e: 190 if WindowsError is not None and isinstance(e, WindowsError): 191 # Copying file access times may fail on Windows 192 pass 193 else: 194 errors.extend((src, dst, str(e))) 195 if errors: 196 raise shutil.Error, errors 197 return copies 198 199 200 def fixConfFile(self, src): 201 lines = [] 202 oldFile = open(src, 'r') 203 for line in oldFile.readlines(): 204 # paths generated by configure could be different link-path than whats used by user, so fix both 205 line = line.replace(os.path.join(self.rootDir, self.arch), self.installDir) 206 line = line.replace(os.path.realpath(os.path.join(self.rootDir, self.arch)), self.installDir) 207 line = line.replace(os.path.join(self.rootDir, 'bin'), self.installBinDir) 208 line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'bin')), self.installBinDir) 209 line = line.replace(os.path.join(self.rootDir, 'include'), self.installIncludeDir) 210 line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'include')), self.installIncludeDir) 211 # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary 212 line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir) 213 line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '') 214 line = line.replace('${PETSC_DIR}', self.installDir) 215 lines.append(line) 216 oldFile.close() 217 newFile = open(src, 'w') 218 newFile.write(''.join(lines)) 219 newFile.close() 220 return 221 222 def fixConf(self): 223 import shutil 224 for file in ['rules', 'variables','petscrules', 'petscvariables']: 225 self.fixConfFile(os.path.join(self.destConfDir,file)) 226 self.fixConfFile(os.path.join(self.destLibDir,'pkgconfig','PETSc.pc')) 227 return 228 229 def createUninstaller(self): 230 uninstallscript = os.path.join(self.destConfDir, 'uninstall.py') 231 f = open(uninstallscript, 'w') 232 # Could use the Python AST to do this 233 f.write('#!'+sys.executable+'\n') 234 f.write('import os\n') 235 236 f.write('copies = '+repr(self.copies).replace(self.destDir,self.installDir)) 237 f.write(''' 238for src, dst in copies: 239 try: 240 os.remove(dst) 241 except: 242 pass 243''') 244 #TODO: need to delete libXXX.YYY.dylib.dSYM directory on Mac 245 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')] 246 newdirs = [] 247 for dir in dirs: newdirs.append(os.path.join(self.installDir,dir)) 248 f.write('dirs = '+str(newdirs)) 249 f.write(''' 250for dir in dirs: 251 import shutil 252 try: 253 shutil.rmtree(dir) 254 except: 255 pass 256''') 257 f.close() 258 os.chmod(uninstallscript,0744) 259 return 260 261 def installIncludes(self): 262 # TODO: should exclude petsc-mpi.uni except for uni builds 263 # TODO: should exclude petsc/finclude except for fortran builds 264 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = ['makefile'])) 265 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 266 return 267 268 def installConf(self): 269 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir)) 270 self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp'])) 271 return 272 273 def installBin(self): 274 self.copies.extend(self.copytree(os.path.join(self.rootBinDir), os.path.join(self.destBinDir))) 275 # TODO: should copy over petsc-mpiexec.uni only for uni builds 276 self.copies.extend(self.copyfile(os.path.join(self.rootBinDir,'petsc-mpiexec.uni'), self.destBinDir)) 277 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'])) 278 return 279 280 def installShare(self): 281 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 282 return 283 284 def copyLib(self, src, dst): 285 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 286 # Symlinks (assumed local) are recreated at dst 287 if os.path.islink(src): 288 linkto = os.readlink(src) 289 try: 290 os.remove(dst) # In case it already exists 291 except OSError: 292 pass 293 os.symlink(linkto, dst) 294 return 295 # Do not install object files 296 if not os.path.splitext(src)[1] == '.o': 297 shutil.copy2(src, dst) 298 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 299 self.executeShellCommand(self.ranlib+' '+dst) 300 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 301 [output,err,flg] = self.executeShellCommand("otool -D "+src) 302 oldname = output[output.find("\n")+1:] 303 installName = oldname.replace(self.archDir, self.installDir) 304 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 305 # preserve the original timestamps - so that the .a vs .so time order is preserved 306 shutil.copystat(src,dst) 307 return 308 309 def installLib(self): 310 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR', 'sowing'])) 311 return 312 313 314 def outputInstallDone(self): 315 print '''\ 316==================================== 317Install complete. 318Now to check if the libraries are working do (in current directory): 319make PETSC_DIR=%s PETSC_ARCH="" test 320====================================\ 321''' % (self.installDir) 322 return 323 324 def outputDestDirDone(self): 325 print '''\ 326==================================== 327Copy to DESTDIR %s is now complete. 328Before use - please copy/install over to specified prefix: %s 329====================================\ 330''' % (self.destDir,self.installDir) 331 return 332 333 def runsetup(self): 334 self.setup() 335 self.setupDirectories() 336 self.checkPrefix() 337 self.checkDestdir() 338 return 339 340 def runcopy(self): 341 if self.destDir == self.installDir: 342 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 343 else: 344 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 345 if not os.path.exists(self.destDir): 346 try: 347 os.makedirs(self.destDir) 348 except: 349 print '********************************************************************' 350 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 351 print '********************************************************************' 352 sys.exit(1) 353 self.installIncludes() 354 self.installConf() 355 self.installBin() 356 self.installLib() 357 self.installShare() 358 return 359 360 def runfix(self): 361 self.fixConf() 362 return 363 364 def rundone(self): 365 self.createUninstaller() 366 if self.destDir == self.installDir: 367 self.outputInstallDone() 368 else: 369 self.outputDestDirDone() 370 return 371 372 def run(self): 373 self.runsetup() 374 self.runcopy() 375 self.runfix() 376 self.rundone() 377 return 378 379if __name__ == '__main__': 380 Installer(sys.argv[1:]).run() 381 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 382 delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp'] 383 for delfile in delfiles: 384 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 385 os.remove(delfile) 386