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