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