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