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