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