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