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 os.mkdir(os.path.join(self.destShareDir,'petsc','examples')) 333 self.copyExamples(self.rootDir,os.path.join(self.destShareDir,'petsc','examples')) 334 self.fixExamplesMakefile(os.path.join(self.destShareDir,'petsc','examples','makefile')) 335 return 336 337 def copyLib(self, src, dst): 338 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 339 # Symlinks (assumed local) are recreated at dst 340 if os.path.islink(src): 341 linkto = os.readlink(src) 342 try: 343 os.remove(dst) # In case it already exists 344 except OSError: 345 pass 346 os.symlink(linkto, dst) 347 return 348 # Do not install object files 349 if not os.path.splitext(src)[1] == '.o': 350 shutil.copy2(src, dst) 351 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 352 self.executeShellCommand(self.ranlib+' '+dst) 353 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 354 [output,err,flg] = self.executeShellCommand("otool -D "+src) 355 oldname = output[output.find("\n")+1:] 356 installName = oldname.replace(self.archDir, self.installDir) 357 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 358 # preserve the original timestamps - so that the .a vs .so time order is preserved 359 shutil.copystat(src,dst) 360 return 361 362 def installLib(self): 363 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR', 'sowing'])) 364 return 365 366 367 def outputInstallDone(self): 368 print '''\ 369==================================== 370Install complete. 371Now to check if the libraries are working do (in current directory): 372make PETSC_DIR=%s PETSC_ARCH="" test 373====================================\ 374''' % (self.installDir) 375 return 376 377 def outputDestDirDone(self): 378 print '''\ 379==================================== 380Copy to DESTDIR %s is now complete. 381Before use - please copy/install over to specified prefix: %s 382====================================\ 383''' % (self.destDir,self.installDir) 384 return 385 386 def runsetup(self): 387 self.setup() 388 self.setupDirectories() 389 self.checkPrefix() 390 self.checkDestdir() 391 return 392 393 def runcopy(self): 394 if self.destDir == self.installDir: 395 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 396 else: 397 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 398 if not os.path.exists(self.destDir): 399 try: 400 os.makedirs(self.destDir) 401 except: 402 print '********************************************************************' 403 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 404 print '********************************************************************' 405 sys.exit(1) 406 self.installIncludes() 407 self.installConf() 408 self.installBin() 409 self.installLib() 410 self.installShare() 411 return 412 413 def runfix(self): 414 self.fixConf() 415 return 416 417 def rundone(self): 418 self.createUninstaller() 419 if self.destDir == self.installDir: 420 self.outputInstallDone() 421 else: 422 self.outputDestDirDone() 423 return 424 425 def run(self): 426 self.runsetup() 427 self.runcopy() 428 self.runfix() 429 self.rundone() 430 return 431 432if __name__ == '__main__': 433 Installer(sys.argv[1:]).run() 434 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 435 delfiles=['RDict.db','RDict.log','build.log','default.log','build.log.bkp','default.log.bkp'] 436 for delfile in delfiles: 437 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 438 os.remove(delfile) 439