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