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 if line.startswith('PETSC_CC_INCLUDES =') or line.startswith('PETSC_FC_INCLUDES ='): 255 continue 256 line = line.replace('PETSC_CC_INCLUDES_INSTALL', 'PETSC_CC_INCLUDES') 257 line = line.replace('PETSC_FC_INCLUDES_INSTALL', 'PETSC_FC_INCLUDES') 258 # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary 259 line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir) 260 line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '') 261 line = line.replace('${PETSC_DIR}', self.installDir) 262 lines.append(line) 263 oldFile.close() 264 newFile = open(src, 'w') 265 newFile.write(''.join(lines)) 266 newFile.close() 267 return 268 269 def fixConf(self): 270 import shutil 271 for file in ['rules', 'variables','petscrules', 'petscvariables']: 272 self.fixConfFile(os.path.join(self.destConfDir,file)) 273 return 274 275 def createUninstaller(self): 276 uninstallscript = os.path.join(self.destConfDir, 'uninstall.py') 277 f = open(uninstallscript, 'w') 278 # Could use the Python AST to do this 279 f.write('#!'+sys.executable+'\n') 280 f.write('import os\n') 281 f.write('prefixdir = "'+self.installDir+'"\n') 282 files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies] 283 files.append(uninstallscript.replace(self.destDir,self.installDir)) 284 f.write('files = '+repr(files)) 285 f.write(''' 286for file in files: 287 if os.path.exists(file) or os.path.islink(file): 288 os.remove(file) 289 dir = os.path.dirname(file) 290 while dir not in [os.path.dirname(prefixdir),'/']: 291 try: os.rmdir(dir) 292 except: break 293 dir = os.path.dirname(dir) 294''') 295 f.close() 296 os.chmod(uninstallscript,0o744) 297 return 298 299 def installIncludes(self): 300 exclude = ['makefile'] 301 if not hasattr(self.compilers.setCompilers, 'FC'): 302 exclude.append('finclude') 303 if not self.mpi.usingMPIUni: 304 exclude.append('mpiuni') 305 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude)) 306 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 307 return 308 309 def installConf(self): 310 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log'])) 311 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'])) 312 return 313 314 def installBin(self): 315 exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'] 316 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude )) 317 exclude = ['maint'] 318 if not self.mpi.usingMPIUni: 319 exclude.append('petsc-mpiexec.uni') 320 self.setCompilers.pushLanguage('C') 321 if not self.setCompilers.isWindows(self.setCompilers.getCompiler(),self.log): 322 exclude.append('win32fe') 323 self.setCompilers.popLanguage() 324 self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude )) 325 return 326 327 def installShare(self): 328 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 329 examplesdir=os.path.join(self.destShareDir,'petsc','examples') 330 if os.path.exists(examplesdir): 331 shutil.rmtree(examplesdir) 332 os.mkdir(examplesdir) 333 os.mkdir(os.path.join(examplesdir,'src')) 334 self.copyExamples(self.rootSrcDir,os.path.join(examplesdir,'src')) 335 self.copyConfig(self.rootDir,examplesdir) 336 self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test')) 337 return 338 339 def copyLib(self, src, dst): 340 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 341 # Symlinks (assumed local) are recreated at dst 342 if os.path.islink(src): 343 linkto = os.readlink(src) 344 try: 345 os.remove(dst) # In case it already exists 346 except OSError: 347 pass 348 os.symlink(linkto, dst) 349 return 350 shutil.copy2(src, dst) 351 if os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 352 self.executeShellCommand(self.ranlib+' '+dst) 353 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 354 [output,err,flg] = self.executeShellCommand("otool -D "+src) 355 oldname = output[output.find("\n")+1:] 356 installName = oldname.replace(self.archDir, self.installDir) 357 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 358 # preserve the original timestamps - so that the .a vs .so time order is preserved 359 shutil.copystat(src,dst) 360 return 361 362 def installLib(self): 363 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 364 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 365 return 366 367 368 def outputInstallDone(self): 369 print('''\ 370==================================== 371Install complete. 372Now to check if the libraries are working do (in current directory): 373make PETSC_DIR=%s PETSC_ARCH="" test 374====================================\ 375''' % (self.installDir)) 376 return 377 378 def outputDestDirDone(self): 379 print('''\ 380==================================== 381Copy to DESTDIR %s is now complete. 382Before use - please copy/install over to specified prefix: %s 383====================================\ 384''' % (self.destDir,self.installDir)) 385 return 386 387 def runsetup(self): 388 self.setup() 389 self.setupDirectories() 390 self.checkPrefix() 391 self.checkDestdir() 392 return 393 394 def runcopy(self): 395 if self.destDir == self.installDir: 396 print('*** Installing PETSc at prefix location:',self.destDir, ' ***') 397 else: 398 print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***') 399 if not os.path.exists(self.destDir): 400 try: 401 os.makedirs(self.destDir) 402 except: 403 print('********************************************************************') 404 print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"') 405 print('********************************************************************') 406 sys.exit(1) 407 self.installIncludes() 408 self.installConf() 409 self.installBin() 410 self.installLib() 411 self.installShare() 412 return 413 414 def runfix(self): 415 self.fixConf() 416 return 417 418 def rundone(self): 419 self.createUninstaller() 420 if self.destDir == self.installDir: 421 self.outputInstallDone() 422 else: 423 self.outputDestDirDone() 424 return 425 426 def run(self): 427 self.runsetup() 428 self.runcopy() 429 self.runfix() 430 self.rundone() 431 return 432 433if __name__ == '__main__': 434 Installer(sys.argv[1:]).run() 435 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 436 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 437 for delfile in delfiles: 438 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 439 os.remove(delfile) 440