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