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