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