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