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(os.path.expanduser(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 def copyexamplefiles(self, src, dst, copyFunc = shutil.copy2): 148 """Copies all files, but not directories in a single file """ 149 names = os.listdir(src) 150 for name in names: 151 if not name.endswith('.html'): 152 srcname = os.path.join(src, name) 153 if os.path.isfile(srcname): 154 self.copyfile(srcname,dst) 155 156 def fixExamplesMakefile(self, src): 157 '''Change ././${PETSC_ARCH} in makefile in root petsc directory with ${PETSC_DIR}''' 158 lines = [] 159 oldFile = open(src, 'r') 160 for line in oldFile.readlines(): 161 # paths generated by configure could be different link-path than whats used by user, so fix both 162 line = line.replace('././${PETSC_ARCH}','${PETSC_DIR}') 163 lines.append(line) 164 oldFile.close() 165 newFile = open(src, 'w') 166 newFile.write(''.join(lines)) 167 newFile.close() 168 return 169 170 def copyExamples(self, src, dst): 171 """Recursively copy the examples directories 172 """ 173 if not os.path.isdir(dst): 174 raise shutil.Error, 'Destination is not a directory' 175 176 self.copyfile(os.path.join(src,'makefile'),dst) 177 names = os.listdir(src) 178 nret2 = 0 179 for name in names: 180 srcname = os.path.join(src, name) 181 dstname = os.path.join(dst, name) 182 if not name.startswith('arch') and os.path.isdir(srcname) and os.path.isfile(os.path.join(srcname,'makefile')): 183 os.mkdir(dstname) 184 nret = self.copyExamples(srcname,dstname) 185 if name == 'tests' or name == 'tutorials': 186 self.copyexamplefiles(srcname,dstname) 187 if os.path.isdir(os.path.join(srcname,'output')): 188 os.mkdir(os.path.join(dstname,'output')) 189 self.copyexamplefiles(os.path.join(srcname,'output'),os.path.join(dstname,'output')) 190 nret = 1 191 if not nret: 192 # prune directory branches that don't have examples under them 193 os.unlink(os.path.join(dstname,'makefile')) 194 os.rmdir(dstname) 195 nret2 = nret + nret2 196 return nret2 197 198 def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = []): 199 """Recursively copy a directory tree using copyFunc, which defaults to shutil.copy2(). 200 201 The copyFunc() you provide is only used on the top level, lower levels always use shutil.copy2 202 203 The destination directory must not already exist. 204 If exception(s) occur, an shutil.Error is raised with a list of reasons. 205 206 If the optional symlinks flag is true, symbolic links in the 207 source tree result in symbolic links in the destination tree; if 208 it is false, the contents of the files pointed to by symbolic 209 links are copied. 210 """ 211 copies = [] 212 names = os.listdir(src) 213 if not os.path.exists(dst): 214 os.makedirs(dst) 215 elif not os.path.isdir(dst): 216 raise shutil.Error, 'Destination is not a directory' 217 errors = [] 218 for name in names: 219 srcname = os.path.join(src, name) 220 dstname = os.path.join(dst, name) 221 try: 222 if symlinks and os.path.islink(srcname): 223 linkto = os.readlink(srcname) 224 os.symlink(linkto, dstname) 225 elif os.path.isdir(srcname): 226 copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude)) 227 elif not os.path.basename(srcname) in exclude: 228 copyFunc(srcname, dstname) 229 copies.append((srcname, dstname)) 230 # XXX What about devices, sockets etc.? 231 except (IOError, os.error), why: 232 errors.append((srcname, dstname, str(why))) 233 # catch the Error from the recursive copytree so that we can 234 # continue with other files 235 except shutil.Error, err: 236 errors.extend((srcname,dstname,str(err.args[0]))) 237 try: 238 shutil.copystat(src, dst) 239 except OSError, e: 240 if WindowsError is not None and isinstance(e, WindowsError): 241 # Copying file access times may fail on Windows 242 pass 243 else: 244 errors.extend((src, dst, str(e))) 245 if errors: 246 raise shutil.Error, errors 247 return copies 248 249 250 def fixConfFile(self, src): 251 lines = [] 252 oldFile = open(src, 'r') 253 for line in oldFile.readlines(): 254 # paths generated by configure could be different link-path than whats used by user, so fix both 255 line = line.replace(os.path.join(self.rootDir, self.arch), self.installDir) 256 line = line.replace(os.path.realpath(os.path.join(self.rootDir, self.arch)), self.installDir) 257 line = line.replace(os.path.join(self.rootDir, 'bin'), self.installBinDir) 258 line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'bin')), self.installBinDir) 259 line = line.replace(os.path.join(self.rootDir, 'include'), self.installIncludeDir) 260 line = line.replace(os.path.realpath(os.path.join(self.rootDir, 'include')), self.installIncludeDir) 261 # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary 262 line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir) 263 line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '') 264 line = line.replace('${PETSC_DIR}', self.installDir) 265 lines.append(line) 266 oldFile.close() 267 newFile = open(src, 'w') 268 newFile.write(''.join(lines)) 269 newFile.close() 270 return 271 272 def fixConf(self): 273 import shutil 274 for file in ['rules', 'variables','petscrules', 'petscvariables']: 275 self.fixConfFile(os.path.join(self.destConfDir,file)) 276 self.fixConfFile(os.path.join(self.destLibDir,'pkgconfig','PETSc.pc')) 277 return 278 279 def createUninstaller(self): 280 uninstallscript = os.path.join(self.destConfDir, 'uninstall.py') 281 f = open(uninstallscript, 'w') 282 # Could use the Python AST to do this 283 f.write('#!'+sys.executable+'\n') 284 f.write('import os\n') 285 286 f.write('copies = '+repr(self.copies).replace(self.destDir,self.installDir)) 287 f.write(''' 288for src, dst in copies: 289 try: 290 os.remove(dst) 291 except: 292 pass 293''') 294 #TODO: need to delete libXXX.YYY.dylib.dSYM directory on Mac 295 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')] 296 newdirs = [] 297 for dir in dirs: newdirs.append(os.path.join(self.installDir,dir)) 298 f.write('dirs = '+str(newdirs)) 299 f.write(''' 300for dir in dirs: 301 import shutil 302 try: 303 shutil.rmtree(dir) 304 except: 305 pass 306''') 307 f.close() 308 os.chmod(uninstallscript,0744) 309 return 310 311 def installIncludes(self): 312 # TODO: should exclude petsc-mpi.uni except for uni builds 313 # TODO: should exclude petsc/finclude except for fortran builds 314 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = ['makefile'])) 315 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 316 return 317 318 def installConf(self): 319 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir)) 320 self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp'])) 321 return 322 323 def installBin(self): 324 self.copies.extend(self.copytree(os.path.join(self.rootBinDir), os.path.join(self.destBinDir))) 325 # TODO: should copy over petsc-mpiexec.uni only for uni builds 326 self.copies.extend(self.copyfile(os.path.join(self.rootBinDir,'petsc-mpiexec.uni'), self.destBinDir)) 327 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'])) 328 return 329 330 def installShare(self): 331 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 332 if os.path.exists(os.path.join(self.destShareDir,'petsc','examples')): 333 shutil.rmtree(os.path.join(self.destShareDir,'petsc','examples')) 334 os.mkdir(os.path.join(self.destShareDir,'petsc','examples')) 335 self.copyExamples(self.rootDir,os.path.join(self.destShareDir,'petsc','examples')) 336 self.fixExamplesMakefile(os.path.join(self.destShareDir,'petsc','examples','makefile')) 337 return 338 339 def copyLib(self, src, dst): 340 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 341 # Symlinks (assumed local) are recreated at dst 342 if os.path.islink(src): 343 linkto = os.readlink(src) 344 try: 345 os.remove(dst) # In case it already exists 346 except OSError: 347 pass 348 os.symlink(linkto, dst) 349 return 350 # Do not install object files 351 if not os.path.splitext(src)[1] == '.o': 352 shutil.copy2(src, dst) 353 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 354 self.executeShellCommand(self.ranlib+' '+dst) 355 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 356 [output,err,flg] = self.executeShellCommand("otool -D "+src) 357 oldname = output[output.find("\n")+1:] 358 installName = oldname.replace(self.archDir, self.installDir) 359 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 360 # preserve the original timestamps - so that the .a vs .so time order is preserved 361 shutil.copystat(src,dst) 362 return 363 364 def installLib(self): 365 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR', 'sowing'])) 366 return 367 368 369 def outputInstallDone(self): 370 print '''\ 371==================================== 372Install complete. 373Now to check if the libraries are working do (in current directory): 374make PETSC_DIR=%s PETSC_ARCH="" test 375====================================\ 376''' % (self.installDir) 377 return 378 379 def outputDestDirDone(self): 380 print '''\ 381==================================== 382Copy to DESTDIR %s is now complete. 383Before use - please copy/install over to specified prefix: %s 384====================================\ 385''' % (self.destDir,self.installDir) 386 return 387 388 def runsetup(self): 389 self.setup() 390 self.setupDirectories() 391 self.checkPrefix() 392 self.checkDestdir() 393 return 394 395 def runcopy(self): 396 if self.destDir == self.installDir: 397 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 398 else: 399 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 400 if not os.path.exists(self.destDir): 401 try: 402 os.makedirs(self.destDir) 403 except: 404 print '********************************************************************' 405 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 406 print '********************************************************************' 407 sys.exit(1) 408 self.installIncludes() 409 self.installConf() 410 self.installBin() 411 self.installLib() 412 self.installShare() 413 return 414 415 def runfix(self): 416 self.fixConf() 417 return 418 419 def rundone(self): 420 self.createUninstaller() 421 if self.destDir == self.installDir: 422 self.outputInstallDone() 423 else: 424 self.outputDestDirDone() 425 return 426 427 def run(self): 428 self.runsetup() 429 self.runcopy() 430 self.runfix() 431 self.rundone() 432 return 433 434if __name__ == '__main__': 435 Installer(sys.argv[1:]).run() 436 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 437 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 438 for delfile in delfiles: 439 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 440 os.remove(delfile) 441