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