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 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 f.write('prefixdir = "'+self.installDir+'"\n') 299 files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies] 300 files.append(uninstallscript.replace(self.destDir,self.installDir)) 301 f.write('files = '+repr(files)) 302 f.write(''' 303for file in files: 304 if os.path.exists(file) or os.path.islink(file): 305 os.remove(file) 306 dir = os.path.dirname(file) 307 while dir not in [prefixdir,'/']: 308 if not os.listdir(dir): 309 os.rmdir(dir) 310 dir = os.path.dirname(dir) 311''') 312 f.close() 313 os.chmod(uninstallscript,0744) 314 return 315 316 def installIncludes(self): 317 exclude = ['makefile'] 318 if not hasattr(self.compilers.setCompilers, 'FC'): 319 exclude.append('finclude') 320 if not self.mpi.usingMPIUni: 321 exclude.append('mpiuni') 322 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude)) 323 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 324 return 325 326 def installConf(self): 327 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log'])) 328 self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp','configure.log','make.log','gmake.log','test.log','error.log'])) 329 return 330 331 def installBin(self): 332 exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'] 333 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude )) 334 exclude = ['maint'] 335 if not self.mpi.usingMPIUni: 336 exclude.append('petsc-mpiexec.uni') 337 self.setCompilers.pushLanguage('C') 338 if not self.setCompilers.isWindows(self.setCompilers.getCompiler(),self.log): 339 exclude.append('win32fe') 340 self.setCompilers.popLanguage() 341 self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude )) 342 return 343 344 def installShare(self): 345 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 346 examplesdir=os.path.join(self.destShareDir,'petsc','examples') 347 if os.path.exists(examplesdir): 348 shutil.rmtree(examplesdir) 349 os.mkdir(examplesdir) 350 os.mkdir(os.path.join(examplesdir,'src')) 351 self.copyExamples(self.rootSrcDir,examplesdir) 352 self.copyConfig(self.rootDir,examplesdir) 353 self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test')) 354 return 355 356 def copyLib(self, src, dst): 357 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 358 # Symlinks (assumed local) are recreated at dst 359 if os.path.islink(src): 360 linkto = os.readlink(src) 361 try: 362 os.remove(dst) # In case it already exists 363 except OSError: 364 pass 365 os.symlink(linkto, dst) 366 return 367 # Do not install object files 368 if not os.path.splitext(src)[1] == '.o': 369 shutil.copy2(src, dst) 370 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 371 self.executeShellCommand(self.ranlib+' '+dst) 372 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 373 [output,err,flg] = self.executeShellCommand("otool -D "+src) 374 oldname = output[output.find("\n")+1:] 375 installName = oldname.replace(self.archDir, self.installDir) 376 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 377 # preserve the original timestamps - so that the .a vs .so time order is preserved 378 shutil.copystat(src,dst) 379 return 380 381 def installLib(self): 382 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 383 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 384 return 385 386 387 def outputInstallDone(self): 388 print '''\ 389==================================== 390Install complete. 391Now to check if the libraries are working do (in current directory): 392make PETSC_DIR=%s PETSC_ARCH="" test 393====================================\ 394''' % (self.installDir) 395 return 396 397 def outputDestDirDone(self): 398 print '''\ 399==================================== 400Copy to DESTDIR %s is now complete. 401Before use - please copy/install over to specified prefix: %s 402====================================\ 403''' % (self.destDir,self.installDir) 404 return 405 406 def runsetup(self): 407 self.setup() 408 self.setupDirectories() 409 self.checkPrefix() 410 self.checkDestdir() 411 return 412 413 def runcopy(self): 414 if self.destDir == self.installDir: 415 print '*** Installing PETSc at prefix location:',self.destDir, ' ***' 416 else: 417 print '*** Copying PETSc to DESTDIR location:',self.destDir, ' ***' 418 if not os.path.exists(self.destDir): 419 try: 420 os.makedirs(self.destDir) 421 except: 422 print '********************************************************************' 423 print 'Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"' 424 print '********************************************************************' 425 sys.exit(1) 426 self.installIncludes() 427 self.installConf() 428 self.installBin() 429 self.installLib() 430 self.installShare() 431 return 432 433 def runfix(self): 434 self.fixConf() 435 return 436 437 def rundone(self): 438 self.createUninstaller() 439 if self.destDir == self.installDir: 440 self.outputInstallDone() 441 else: 442 self.outputDestDirDone() 443 return 444 445 def run(self): 446 self.runsetup() 447 self.runcopy() 448 self.runfix() 449 self.rundone() 450 return 451 452if __name__ == '__main__': 453 Installer(sys.argv[1:]).run() 454 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 455 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 456 for delfile in delfiles: 457 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 458 os.remove(delfile) 459