1#!/usr/bin/env python 2from __future__ import print_function 3import os, re, shutil, sys 4 5if 'PETSC_DIR' in os.environ: 6 PETSC_DIR = os.environ['PETSC_DIR'] 7else: 8 fd = file(os.path.join('lib','petsc','conf','petscvariables')) 9 a = fd.readline() 10 a = fd.readline() 11 PETSC_DIR = a.split('=')[1][0:-1] 12 fd.close() 13 14if 'PETSC_ARCH' in os.environ: 15 PETSC_ARCH = os.environ['PETSC_ARCH'] 16else: 17 fd = file(os.path.join('lib','petsc','conf','petscvariables')) 18 a = fd.readline() 19 PETSC_ARCH = a.split('=')[1][0:-1] 20 fd.close() 21 22print('*** Using PETSC_DIR='+PETSC_DIR+' PETSC_ARCH='+PETSC_ARCH+' ***') 23sys.path.insert(0, os.path.join(PETSC_DIR, 'config')) 24sys.path.insert(0, os.path.join(PETSC_DIR, 'config', 'BuildSystem')) 25 26import script 27 28try: 29 WindowsError 30except NameError: 31 WindowsError = None 32 33class Installer(script.Script): 34 def __init__(self, clArgs = None): 35 import RDict 36 argDB = RDict.RDict(None, None, 0, 0, readonly = True) 37 argDB.saveFilename = os.path.join(PETSC_DIR, PETSC_ARCH, 'lib','petsc','conf', 'RDict.db') 38 argDB.load() 39 script.Script.__init__(self, argDB = argDB) 40 if not clArgs is None: self.clArgs = clArgs 41 self.copies = [] 42 return 43 44 def setupHelp(self, help): 45 import nargs 46 script.Script.setupHelp(self, help) 47 help.addArgument('Installer', '-destDir=<path>', nargs.Arg(None, '', 'Destination Directory for install')) 48 return 49 50 51 def setupModules(self): 52 self.setCompilers = self.framework.require('config.setCompilers', None) 53 self.arch = self.framework.require('PETSc.options.arch', None) 54 self.petscdir = self.framework.require('PETSc.options.petscdir', None) 55 self.compilers = self.framework.require('config.compilers', None) 56 self.mpi = self.framework.require('config.packages.MPI', None) 57 return 58 59 def setup(self): 60 script.Script.setup(self) 61 self.framework = self.loadConfigure() 62 self.setupModules() 63 return 64 65 def setupDirectories(self): 66 self.rootDir = self.petscdir.dir 67 self.installDir = os.path.abspath(os.path.expanduser(self.framework.argDB['prefix'])) 68 self.destDir = os.path.abspath(self.argDB['destDir']+self.installDir) 69 self.arch = self.arch.arch 70 self.archDir = os.path.join(self.rootDir, self.arch) 71 self.rootIncludeDir = os.path.join(self.rootDir, 'include') 72 self.archIncludeDir = os.path.join(self.rootDir, self.arch, 'include') 73 self.rootConfDir = os.path.join(self.rootDir, 'lib','petsc','conf') 74 self.archConfDir = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf') 75 self.rootBinDir = os.path.join(self.rootDir, 'lib','petsc','bin') 76 self.archBinDir = os.path.join(self.rootDir, self.arch, 'bin') 77 self.archLibDir = os.path.join(self.rootDir, self.arch, 'lib') 78 self.destIncludeDir = os.path.join(self.destDir, 'include') 79 self.destConfDir = os.path.join(self.destDir, 'lib','petsc','conf') 80 self.destLibDir = os.path.join(self.destDir, 'lib') 81 self.destBinDir = os.path.join(self.destDir, 'lib','petsc','bin') 82 self.installIncludeDir = os.path.join(self.installDir, 'include') 83 self.installBinDir = os.path.join(self.installDir, 'lib','petsc','bin') 84 self.rootShareDir = os.path.join(self.rootDir, 'share') 85 self.destShareDir = os.path.join(self.destDir, 'share') 86 self.rootSrcDir = os.path.join(self.rootDir, 'src') 87 88 self.ranlib = self.compilers.RANLIB 89 self.arLibSuffix = self.compilers.AR_LIB_SUFFIX 90 return 91 92 def checkPrefix(self): 93 if not self.installDir: 94 print('********************************************************************') 95 print('PETSc is built without prefix option. So "make install" is not appropriate.') 96 print('If you need a prefix install of PETSc - rerun configure with --prefix option.') 97 print('********************************************************************') 98 sys.exit(1) 99 return 100 101 def checkDestdir(self): 102 if os.path.exists(self.destDir): 103 if os.path.samefile(self.destDir, self.rootDir): 104 print('********************************************************************') 105 print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR') 106 print('********************************************************************') 107 sys.exit(1) 108 if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)): 109 print('********************************************************************') 110 print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH') 111 print('********************************************************************') 112 sys.exit(1) 113 if not os.path.isdir(os.path.realpath(self.destDir)): 114 print('********************************************************************') 115 print('Specified destDir', self.destDir, 'is not a directory. Cannot proceed!') 116 print('********************************************************************') 117 sys.exit(1) 118 if not os.access(self.destDir, os.W_OK): 119 print('********************************************************************') 120 print('Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"') 121 print('********************************************************************') 122 sys.exit(1) 123 return 124 125 def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2): 126 """Copies a single file """ 127 copies = [] 128 errors = [] 129 if not os.path.exists(dst): 130 os.makedirs(dst) 131 elif not os.path.isdir(dst): 132 raise shutil.Error('Destination is not a directory') 133 srcname = src 134 dstname = os.path.join(dst, os.path.basename(src)) 135 try: 136 if symlinks and os.path.islink(srcname): 137 linkto = os.readlink(srcname) 138 os.symlink(linkto, dstname) 139 else: 140 copyFunc(srcname, dstname) 141 copies.append((srcname, dstname)) 142 except (IOError, os.error) as why: 143 errors.append((srcname, dstname, str(why))) 144 except shutil.Error as err: 145 errors.extend((srcname,dstname,str(err.args[0]))) 146 if errors: 147 raise shutil.Error(errors) 148 return copies 149 150 def fixExamplesMakefile(self, src): 151 '''Change ././${PETSC_ARCH} in makefile in root petsc directory with ${PETSC_DIR}''' 152 lines = [] 153 oldFile = open(src, 'r') 154 alllines=oldFile.read() 155 oldFile.close() 156 newlines=alllines.split('\n')[0]+'\n' # Firstline 157 # Hardcode PETSC_DIR and PETSC_ARCH to avoid users doing the worng thing 158 newlines+='PETSC_DIR='+self.installDir+'\n' 159 newlines+='PETSC_ARCH=\n' 160 for line in alllines.split('\n')[1:]: 161 if line.startswith('TESTLOGFILE'): 162 newlines+='TESTLOGFILE = $(TESTDIR)/examples-install.log\n' 163 elif line.startswith('CONFIGDIR'): 164 newlines+='CONFIGDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples/config\n' 165 elif line.startswith('$(generatedtest)') and 'petscvariables' in line: 166 newlines+='all: test\n\n'+line+'\n' 167 else: 168 newlines+=line+'\n' 169 newFile = open(src, 'w') 170 newFile.write(newlines) 171 newFile.close() 172 return 173 174 def copyConfig(self, src, dst): 175 """Recursively copy the examples directories 176 """ 177 if not os.path.isdir(dst): 178 raise shutil.Error('Destination is not a directory') 179 180 self.copies.extend(self.copyfile('gmakefile.test',dst)) 181 newConfigDir=os.path.join(dst,'config') # Am not renaming at present 182 if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir) 183 testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split() 184 testConfFiles+="petsc_harness.sh report_tests.py".split() 185 testConfFiles+=["cmakegen.py"] 186 for tf in testConfFiles: 187 self.copies.extend(self.copyfile(os.path.join('config',tf),newConfigDir)) 188 return 189 190 def copyExamples(self, src, dst): 191 """copy the examples directories 192 """ 193 for root, dirs, files in os.walk(src, topdown=False): 194 if not os.path.basename(root) == "examples": continue 195 self.copies.extend(self.copytree(root, root.replace(src,dst))) 196 return 197 198 def copytree(self, src, dst, symlinks = False, copyFunc = shutil.copy2, exclude = [], exclude_ext= ['.DSYM','.o','.pyc'], recurse = 1): 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) and recurse and not os.path.basename(srcname) in exclude: 226 copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext)) 227 elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext : 228 copyFunc(srcname, dstname) 229 copies.append((srcname, dstname)) 230 # XXX What about devices, sockets etc.? 231 except (IOError, os.error) as 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 as err: 236 errors.extend((srcname,dstname,str(err.args[0]))) 237 try: 238 shutil.copystat(src, dst) 239 except OSError as 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 return 277 278 def createUninstaller(self): 279 uninstallscript = os.path.join(self.destConfDir, 'uninstall.py') 280 f = open(uninstallscript, 'w') 281 # Could use the Python AST to do this 282 f.write('#!'+sys.executable+'\n') 283 f.write('import os\n') 284 f.write('prefixdir = "'+self.installDir+'"\n') 285 files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies] 286 files.append(uninstallscript.replace(self.destDir,self.installDir)) 287 f.write('files = '+repr(files)) 288 f.write(''' 289for file in files: 290 if os.path.exists(file) or os.path.islink(file): 291 os.remove(file) 292 dir = os.path.dirname(file) 293 while dir not in [os.path.dirname(prefixdir),'/']: 294 try: os.rmdir(dir) 295 except: break 296 dir = os.path.dirname(dir) 297''') 298 f.close() 299 os.chmod(uninstallscript,0o744) 300 return 301 302 def installIncludes(self): 303 exclude = ['makefile'] 304 if not hasattr(self.compilers.setCompilers, 'FC'): 305 exclude.append('finclude') 306 if not self.mpi.usingMPIUni: 307 exclude.append('mpiuni') 308 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude)) 309 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 310 return 311 312 def installConf(self): 313 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log'])) 314 self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp','configure.log','make.log','gmake.log','test.log','error.log','files','testfiles','RDict.db'])) 315 return 316 317 def installBin(self): 318 exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'] 319 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude )) 320 exclude = ['maint'] 321 if not self.mpi.usingMPIUni: 322 exclude.append('petsc-mpiexec.uni') 323 self.setCompilers.pushLanguage('C') 324 if not self.setCompilers.isWindows(self.setCompilers.getCompiler(),self.log): 325 exclude.append('win32fe') 326 self.setCompilers.popLanguage() 327 self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude )) 328 return 329 330 def installShare(self): 331 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 332 examplesdir=os.path.join(self.destShareDir,'petsc','examples') 333 if os.path.exists(examplesdir): 334 shutil.rmtree(examplesdir) 335 os.mkdir(examplesdir) 336 os.mkdir(os.path.join(examplesdir,'src')) 337 self.copyExamples(self.rootSrcDir,examplesdir) 338 self.copyConfig(self.rootDir,examplesdir) 339 self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test')) 340 return 341 342 def copyLib(self, src, dst): 343 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 344 # Symlinks (assumed local) are recreated at dst 345 if os.path.islink(src): 346 linkto = os.readlink(src) 347 try: 348 os.remove(dst) # In case it already exists 349 except OSError: 350 pass 351 os.symlink(linkto, dst) 352 return 353 shutil.copy2(src, dst) 354 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 355 self.executeShellCommand(self.ranlib+' '+dst) 356 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 357 [output,err,flg] = self.executeShellCommand("otool -D "+src) 358 oldname = output[output.find("\n")+1:] 359 installName = oldname.replace(self.archDir, self.installDir) 360 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 361 # preserve the original timestamps - so that the .a vs .so time order is preserved 362 shutil.copystat(src,dst) 363 return 364 365 def installLib(self): 366 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 367 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 368 return 369 370 371 def outputInstallDone(self): 372 print('''\ 373==================================== 374Install complete. 375Now to check if the libraries are working do (in current directory): 376make PETSC_DIR=%s PETSC_ARCH="" test 377====================================\ 378''' % (self.installDir)) 379 return 380 381 def outputDestDirDone(self): 382 print('''\ 383==================================== 384Copy to DESTDIR %s is now complete. 385Before use - please copy/install over to specified prefix: %s 386====================================\ 387''' % (self.destDir,self.installDir)) 388 return 389 390 def runsetup(self): 391 self.setup() 392 self.setupDirectories() 393 self.checkPrefix() 394 self.checkDestdir() 395 return 396 397 def runcopy(self): 398 if self.destDir == self.installDir: 399 print('*** Installing PETSc at prefix location:',self.destDir, ' ***') 400 else: 401 print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***') 402 if not os.path.exists(self.destDir): 403 try: 404 os.makedirs(self.destDir) 405 except: 406 print('********************************************************************') 407 print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"') 408 print('********************************************************************') 409 sys.exit(1) 410 self.installIncludes() 411 self.installConf() 412 self.installBin() 413 self.installLib() 414 self.installShare() 415 return 416 417 def runfix(self): 418 self.fixConf() 419 return 420 421 def rundone(self): 422 self.createUninstaller() 423 if self.destDir == self.installDir: 424 self.outputInstallDone() 425 else: 426 self.outputDestDirDone() 427 return 428 429 def run(self): 430 self.runsetup() 431 self.runcopy() 432 self.runfix() 433 self.rundone() 434 return 435 436if __name__ == '__main__': 437 Installer(sys.argv[1:]).run() 438 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 439 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 440 for delfile in delfiles: 441 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 442 os.remove(delfile) 443