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),exclude_ext=['DSYM'])) 198 return 199 200 def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= [], 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 [prefixdir,'/']: 297 if not os.listdir(dir): 298 os.rmdir(dir) 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'])) 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 # Do not install object files 357 if not os.path.splitext(src)[1] == '.o': 358 shutil.copy2(src, dst) 359 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 360 self.executeShellCommand(self.ranlib+' '+dst) 361 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 362 [output,err,flg] = self.executeShellCommand("otool -D "+src) 363 oldname = output[output.find("\n")+1:] 364 installName = oldname.replace(self.archDir, self.installDir) 365 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 366 # preserve the original timestamps - so that the .a vs .so time order is preserved 367 shutil.copystat(src,dst) 368 return 369 370 def installLib(self): 371 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 372 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 373 return 374 375 376 def outputInstallDone(self): 377 print '''\ 378==================================== 379Install complete. 380Now to check if the libraries are working do (in current directory): 381make PETSC_DIR=%s PETSC_ARCH="" test 382====================================\ 383''' % (self.installDir) 384 return 385 386 def outputDestDirDone(self): 387 print '''\ 388==================================== 389Copy to DESTDIR %s is now complete. 390Before use - please copy/install over to specified prefix: %s 391====================================\ 392''' % (self.destDir,self.installDir) 393 return 394 395 def runsetup(self): 396 self.setup() 397 self.setupDirectories() 398 self.checkPrefix() 399 self.checkDestdir() 400 return 401 402 def runcopy(self): 403 if self.destDir == self.installDir: 404 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 405 else: 406 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 407 if not os.path.exists(self.destDir): 408 try: 409 os.makedirs(self.destDir) 410 except: 411 print '********************************************************************' 412 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 413 print '********************************************************************' 414 sys.exit(1) 415 self.installIncludes() 416 self.installConf() 417 self.installBin() 418 self.installLib() 419 self.installShare() 420 return 421 422 def runfix(self): 423 self.fixConf() 424 return 425 426 def rundone(self): 427 self.createUninstaller() 428 if self.destDir == self.installDir: 429 self.outputInstallDone() 430 else: 431 self.outputDestDirDone() 432 return 433 434 def run(self): 435 self.runsetup() 436 self.runcopy() 437 self.runfix() 438 self.rundone() 439 return 440 441if __name__ == '__main__': 442 Installer(sys.argv[1:]).run() 443 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 444 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 445 for delfile in delfiles: 446 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 447 os.remove(delfile) 448