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, '', '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('$(generatedtest)') and 'petscvariables' in line: 165 newlines+='all: test\n\n'+line+'\n' 166 else: 167 newlines+=line+'\n' 168 newFile = open(src, 'w') 169 newFile.write(newlines) 170 newFile.close() 171 return 172 173 def copyConfig(self, src, dst): 174 """Recursively copy the examples directories 175 """ 176 if not os.path.isdir(dst): 177 raise shutil.Error, 'Destination is not a directory' 178 179 self.copies.extend(self.copyfile('gmakefile.test',dst)) 180 newConfigDir=os.path.join(dst,'config') # Am not renaming at present 181 if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir) 182 testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split() 183 testConfFiles+="petsc_harness.sh report_tests.py".split() 184 testConfFiles+=["cmakegen.py"] 185 for tf in testConfFiles: 186 self.copies.extend(self.copyfile(os.path.join('config',tf),newConfigDir)) 187 return 188 189 def copyExamples(self, src, dst): 190 """copy the examples directories 191 """ 192 top=os.path.relpath(src,os.path.abspath(os.curdir)) 193 for root, dirs, files in os.walk(top, topdown=False): 194 if not os.path.basename(root) == "examples": continue 195 self.copies.extend(self.copytree(root, os.path.join(dst,root))) 196 return 197 198 def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= ['.DSYM','.o','.pyc'], recurse = 1): 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) and recurse and not os.path.basename(srcname) in exclude: 226 copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext)) 227 elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext : 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 f.write('prefixdir = "'+self.installDir+'"\n') 286 files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies] 287 files.append(uninstallscript.replace(self.destDir,self.installDir)) 288 f.write('files = '+repr(files)) 289 f.write(''' 290for file in files: 291 if os.path.exists(file) or os.path.islink(file): 292 os.remove(file) 293 dir = os.path.dirname(file) 294 while dir not in [os.path.dirname(prefixdir),'/']: 295 try: os.rmdir(dir) 296 except: break 297 dir = os.path.dirname(dir) 298''') 299 f.close() 300 os.chmod(uninstallscript,0744) 301 return 302 303 def installIncludes(self): 304 exclude = ['makefile'] 305 if not hasattr(self.compilers.setCompilers, 'FC'): 306 exclude.append('finclude') 307 if not self.mpi.usingMPIUni: 308 exclude.append('mpiuni') 309 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude)) 310 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 311 return 312 313 def installConf(self): 314 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log'])) 315 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'])) 316 return 317 318 def installBin(self): 319 exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'] 320 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude )) 321 exclude = ['maint'] 322 if not self.mpi.usingMPIUni: 323 exclude.append('petsc-mpiexec.uni') 324 self.setCompilers.pushLanguage('C') 325 if not self.setCompilers.isWindows(self.setCompilers.getCompiler(),self.log): 326 exclude.append('win32fe') 327 self.setCompilers.popLanguage() 328 self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude )) 329 return 330 331 def installShare(self): 332 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 333 examplesdir=os.path.join(self.destShareDir,'petsc','examples') 334 if os.path.exists(examplesdir): 335 shutil.rmtree(examplesdir) 336 os.mkdir(examplesdir) 337 os.mkdir(os.path.join(examplesdir,'src')) 338 self.copyExamples(self.rootSrcDir,examplesdir) 339 self.copyConfig(self.rootDir,examplesdir) 340 self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test')) 341 return 342 343 def copyLib(self, src, dst): 344 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 345 # Symlinks (assumed local) are recreated at dst 346 if os.path.islink(src): 347 linkto = os.readlink(src) 348 try: 349 os.remove(dst) # In case it already exists 350 except OSError: 351 pass 352 os.symlink(linkto, dst) 353 return 354 shutil.copy2(src, dst) 355 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 356 self.executeShellCommand(self.ranlib+' '+dst) 357 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 358 [output,err,flg] = self.executeShellCommand("otool -D "+src) 359 oldname = output[output.find("\n")+1:] 360 installName = oldname.replace(self.archDir, self.installDir) 361 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 362 # preserve the original timestamps - so that the .a vs .so time order is preserved 363 shutil.copystat(src,dst) 364 return 365 366 def installLib(self): 367 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 368 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 369 return 370 371 372 def outputInstallDone(self): 373 print '''\ 374==================================== 375Install complete. 376Now to check if the libraries are working do (in current directory): 377make PETSC_DIR=%s PETSC_ARCH="" test 378====================================\ 379''' % (self.installDir) 380 return 381 382 def outputDestDirDone(self): 383 print '''\ 384==================================== 385Copy to DESTDIR %s is now complete. 386Before use - please copy/install over to specified prefix: %s 387====================================\ 388''' % (self.destDir,self.installDir) 389 return 390 391 def runsetup(self): 392 self.setup() 393 self.setupDirectories() 394 self.checkPrefix() 395 self.checkDestdir() 396 return 397 398 def runcopy(self): 399 if self.destDir == self.installDir: 400 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 401 else: 402 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 403 if not os.path.exists(self.destDir): 404 try: 405 os.makedirs(self.destDir) 406 except: 407 print '********************************************************************' 408 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 409 print '********************************************************************' 410 sys.exit(1) 411 self.installIncludes() 412 self.installConf() 413 self.installBin() 414 self.installLib() 415 self.installShare() 416 return 417 418 def runfix(self): 419 self.fixConf() 420 return 421 422 def rundone(self): 423 self.createUninstaller() 424 if self.destDir == self.installDir: 425 self.outputInstallDone() 426 else: 427 self.outputDestDirDone() 428 return 429 430 def run(self): 431 self.runsetup() 432 self.runcopy() 433 self.runfix() 434 self.rundone() 435 return 436 437if __name__ == '__main__': 438 Installer(sys.argv[1:]).run() 439 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 440 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 441 for delfile in delfiles: 442 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 443 os.remove(delfile) 444