1#!/usr/bin/env python3 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 help.addArgument('Installer', '-no-examples', nargs.Arg(None, '', 'Skip installing examples')) 49 return 50 51 52 def setupModules(self): 53 self.setCompilers = self.framework.require('config.setCompilers', None) 54 self.arch = self.framework.require('PETSc.options.arch', None) 55 self.petscdir = self.framework.require('PETSc.options.petscdir', None) 56 self.compilers = self.framework.require('config.compilers', None) 57 self.mpi = self.framework.require('config.packages.MPI', None) 58 return 59 60 def setup(self): 61 script.Script.setup(self) 62 self.framework = self.loadConfigure() 63 self.setupModules() 64 return 65 66 def setupDirectories(self): 67 self.rootDir = self.petscdir.dir 68 self.installDir = os.path.abspath(os.path.expanduser(self.framework.argDB['prefix'])) 69 self.destDir = os.path.abspath(self.argDB['destDir']+self.installDir) 70 self.arch = self.arch.arch 71 self.archDir = os.path.join(self.rootDir, self.arch) 72 self.rootIncludeDir = os.path.join(self.rootDir, 'include') 73 self.archIncludeDir = os.path.join(self.rootDir, self.arch, 'include') 74 self.rootConfDir = os.path.join(self.rootDir, 'lib','petsc','conf') 75 self.archConfDir = os.path.join(self.rootDir, self.arch, 'lib','petsc','conf') 76 self.rootBinDir = os.path.join(self.rootDir, 'lib','petsc','bin') 77 self.archBinDir = os.path.join(self.rootDir, self.arch, 'bin') 78 self.archLibDir = os.path.join(self.rootDir, self.arch, 'lib') 79 self.destIncludeDir = os.path.join(self.destDir, 'include') 80 self.destConfDir = os.path.join(self.destDir, 'lib','petsc','conf') 81 self.destLibDir = os.path.join(self.destDir, 'lib') 82 self.destBinDir = os.path.join(self.destDir, 'lib','petsc','bin') 83 self.installIncludeDir = os.path.join(self.installDir, 'include') 84 self.installBinDir = os.path.join(self.installDir, 'lib','petsc','bin') 85 self.rootShareDir = os.path.join(self.rootDir, 'share') 86 self.destShareDir = os.path.join(self.destDir, 'share') 87 self.rootSrcDir = os.path.join(self.rootDir, 'src') 88 89 self.ranlib = self.compilers.RANLIB 90 self.arLibSuffix = self.compilers.AR_LIB_SUFFIX 91 return 92 93 def checkPrefix(self): 94 if not self.installDir: 95 print('********************************************************************') 96 print('PETSc is built without prefix option. So "make install" is not appropriate.') 97 print('If you need a prefix install of PETSc - rerun configure with --prefix option.') 98 print('********************************************************************') 99 sys.exit(1) 100 return 101 102 def checkDestdir(self): 103 if os.path.exists(self.destDir): 104 if os.path.samefile(self.destDir, self.rootDir): 105 print('********************************************************************') 106 print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR') 107 print('********************************************************************') 108 sys.exit(1) 109 if os.path.samefile(self.destDir, os.path.join(self.rootDir,self.arch)): 110 print('********************************************************************') 111 print('Incorrect prefix usage. Specified destDir same as current PETSC_DIR/PETSC_ARCH') 112 print('********************************************************************') 113 sys.exit(1) 114 if not os.path.isdir(os.path.realpath(self.destDir)): 115 print('********************************************************************') 116 print('Specified destDir', self.destDir, 'is not a directory. Cannot proceed!') 117 print('********************************************************************') 118 sys.exit(1) 119 if not os.access(self.destDir, os.W_OK): 120 print('********************************************************************') 121 print('Unable to write to ', self.destDir, 'Perhaps you need to do "sudo make install"') 122 print('********************************************************************') 123 sys.exit(1) 124 return 125 126 def copyfile(self, src, dst, symlinks = False, copyFunc = shutil.copy2): 127 """Copies a single file """ 128 copies = [] 129 errors = [] 130 if not os.path.exists(dst): 131 os.makedirs(dst) 132 elif not os.path.isdir(dst): 133 raise shutil.Error('Destination is not a directory') 134 srcname = src 135 dstname = os.path.join(dst, os.path.basename(src)) 136 try: 137 if symlinks and os.path.islink(srcname): 138 linkto = os.readlink(srcname) 139 os.symlink(linkto, dstname) 140 else: 141 copyFunc(srcname, dstname) 142 copies.append((srcname, dstname)) 143 except (IOError, os.error) as why: 144 errors.append((srcname, dstname, str(why))) 145 except shutil.Error as err: 146 errors.append((srcname,dstname,str(err.args[0]))) 147 if errors: 148 raise shutil.Error(errors) 149 return copies 150 151 def fixExamplesMakefile(self, src): 152 '''Change ././${PETSC_ARCH} in makefile in root petsc directory with ${PETSC_DIR}''' 153 lines = [] 154 oldFile = open(src, 'r') 155 alllines=oldFile.read() 156 oldFile.close() 157 newlines=alllines.split('\n')[0]+'\n' # Firstline 158 # Hardcode PETSC_DIR and PETSC_ARCH to avoid users doing the wrong thing 159 newlines+='PETSC_DIR='+self.installDir+'\n' 160 newlines+='PETSC_ARCH=\n' 161 for line in alllines.split('\n')[1:]: 162 if line.startswith('TESTLOGFILE'): 163 newlines+='TESTLOGFILE = $(TESTDIR)/examples-install.log\n' 164 elif line.startswith('CONFIGDIR'): 165 newlines+='CONFIGDIR:=$(PETSC_DIR)/$(PETSC_ARCH)/share/petsc/examples/config\n' 166 elif line.startswith('$(generatedtest)') and 'petscvariables' in line: 167 newlines+='all: test\n\n'+line+'\n' 168 else: 169 newlines+=line+'\n' 170 newFile = open(src, 'w') 171 newFile.write(newlines) 172 newFile.close() 173 return 174 175 def copyConfig(self, src, dst): 176 """Copy configuration/testing files 177 """ 178 if not os.path.isdir(dst): 179 raise shutil.Error('Destination is not a directory') 180 181 self.copies.extend(self.copyfile('gmakefile.test',dst)) 182 newConfigDir=os.path.join(dst,'config') # Am not renaming at present 183 if not os.path.isdir(newConfigDir): os.mkdir(newConfigDir) 184 testConfFiles="gmakegentest.py gmakegen.py testparse.py example_template.py".split() 185 testConfFiles+="petsc_harness.sh report_tests.py query_tests.py".split() 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 os.path.basename(root) not in ("tests", "tutorials"): 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 srclinks = [] 219 dstlinks = [] 220 for name in names: 221 srcname = os.path.join(src, name) 222 dstname = os.path.join(dst, name) 223 try: 224 if symlinks and os.path.islink(srcname): 225 linkto = os.readlink(srcname) 226 os.symlink(linkto, dstname) 227 elif os.path.isdir(srcname) and recurse and not os.path.basename(srcname) in exclude: 228 copies.extend(self.copytree(srcname, dstname, symlinks,exclude = exclude, exclude_ext = exclude_ext)) 229 elif os.path.isfile(srcname) and not os.path.basename(srcname) in exclude and os.path.splitext(name)[1] not in exclude_ext : 230 if os.path.islink(srcname): 231 srclinks.append(srcname) 232 dstlinks.append(dstname) 233 else: 234 copyFunc(srcname, dstname) 235 copies.append((srcname, dstname)) 236 # XXX What about devices, sockets etc.? 237 except (IOError, os.error) as why: 238 errors.append((srcname, dstname, str(why))) 239 # catch the Error from the recursive copytree so that we can 240 # continue with other files 241 except shutil.Error as err: 242 errors.append((srcname,dstname,str(err.args[0]))) 243 for srcname, dstname in zip(srclinks, dstlinks): 244 try: 245 copyFunc(srcname, dstname) 246 copies.append((srcname, dstname)) 247 except (IOError, os.error) as why: 248 errors.append((srcname, dstname, str(why))) 249 # catch the Error from the recursive copytree so that we can 250 # continue with other files 251 except shutil.Error as err: 252 errors.append((srcname,dstname,str(err.args[0]))) 253 try: 254 shutil.copystat(src, dst) 255 except OSError as e: 256 if WindowsError is not None and isinstance(e, WindowsError): 257 # Copying file access times may fail on Windows 258 pass 259 else: 260 errors.append((src, dst, str(e))) 261 if errors: 262 raise shutil.Error(errors) 263 return copies 264 265 266 def fixConfFile(self, src): 267 lines = [] 268 oldFile = open(src, 'r') 269 for line in oldFile.readlines(): 270 if line.startswith('PETSC_CC_INCLUDES =') or line.startswith('PETSC_FC_INCLUDES ='): 271 continue 272 line = line.replace('PETSC_CC_INCLUDES_INSTALL', 'PETSC_CC_INCLUDES') 273 line = line.replace('PETSC_FC_INCLUDES_INSTALL', 'PETSC_FC_INCLUDES') 274 # remove PETSC_DIR/PETSC_ARCH variables from conf-makefiles. They are no longer necessary 275 line = line.replace('${PETSC_DIR}/${PETSC_ARCH}', self.installDir) 276 line = line.replace('PETSC_ARCH=${PETSC_ARCH}', '') 277 line = line.replace('${PETSC_DIR}', self.installDir) 278 # replace PETSC_DIR/lib/petsc/bin with prefix/lib/petsc/bin 279 line = line.replace(self.rootBinDir,self.destBinDir) 280 lines.append(line) 281 oldFile.close() 282 newFile = open(src, 'w') 283 newFile.write(''.join(lines)) 284 newFile.close() 285 return 286 287 def fixConf(self): 288 import shutil 289 for file in ['rules', 'rules.doc', 'rules.utils', 'variables', 'petscrules', 'petscvariables']: 290 self.fixConfFile(os.path.join(self.destConfDir,file)) 291 return 292 293 def createUninstaller(self): 294 uninstallscript = os.path.join(self.destConfDir, 'uninstall.py') 295 f = open(uninstallscript, 'w') 296 # Could use the Python AST to do this 297 f.write('#!'+sys.executable+'\n') 298 f.write('import os\n') 299 f.write('prefixdir = "'+self.installDir+'"\n') 300 files = [dst.replace(self.destDir,self.installDir) for src, dst in self.copies] 301 files.append(uninstallscript.replace(self.destDir,self.installDir)) 302 f.write('files = '+repr(files)) 303 f.write(''' 304for file in files: 305 if os.path.exists(file) or os.path.islink(file): 306 os.remove(file) 307 dir = os.path.dirname(file) 308 while dir not in [os.path.dirname(prefixdir),'/']: 309 try: os.rmdir(dir) 310 except: break 311 dir = os.path.dirname(dir) 312''') 313 f.close() 314 os.chmod(uninstallscript,0o744) 315 return 316 317 def installIncludes(self): 318 exclude = ['makefile'] 319 if not hasattr(self.compilers.setCompilers, 'FC'): 320 exclude.append('finclude') 321 if not self.mpi.usingMPIUni: 322 exclude.append('mpiuni') 323 self.copies.extend(self.copytree(self.rootIncludeDir, self.destIncludeDir,exclude = exclude)) 324 self.copies.extend(self.copytree(self.archIncludeDir, self.destIncludeDir)) 325 return 326 327 def installConf(self): 328 self.copies.extend(self.copytree(self.rootConfDir, self.destConfDir, exclude = ['uncrustify.cfg','bfort-base.txt','bfort-petsc.txt','bfort-mpi.txt','test.log'])) 329 self.copies.extend(self.copytree(self.archConfDir, self.destConfDir, exclude = ['sowing', 'configure.log.bkp','configure.log','make.log','gmake.log','test.log','error.log','memoryerror.log','files','testfiles','RDict.db'])) 330 return 331 332 def installBin(self): 333 exclude = ['bfort','bib2html','doc2lt','doctext','mapnames', 'pstogif','pstoxbm','tohtml'] 334 self.copies.extend(self.copytree(self.archBinDir, self.destBinDir, exclude = exclude )) 335 exclude = ['maint'] 336 if not self.mpi.usingMPIUni: 337 exclude.append('petsc-mpiexec.uni') 338 self.setCompilers.pushLanguage('C') 339 if self.setCompilers.getCompiler().find('win32fe') < 0: 340 exclude.append('win32fe') 341 self.setCompilers.popLanguage() 342 self.copies.extend(self.copytree(self.rootBinDir, self.destBinDir, exclude = exclude )) 343 return 344 345 def installShare(self): 346 self.copies.extend(self.copytree(self.rootShareDir, self.destShareDir)) 347 examplesdir=os.path.join(self.destShareDir,'petsc','examples') 348 if os.path.exists(examplesdir): 349 shutil.rmtree(examplesdir) 350 os.mkdir(examplesdir) 351 os.mkdir(os.path.join(examplesdir,'src')) 352 self.copyConfig(self.rootDir,examplesdir) 353 if not self.argDB['no-examples']: 354 self.copyExamples(self.rootSrcDir,os.path.join(examplesdir,'src')) 355 self.fixExamplesMakefile(os.path.join(examplesdir,'gmakefile.test')) 356 return 357 358 def copyLib(self, src, dst): 359 '''Run ranlib on the destination library if it is an archive. Also run install_name_tool on dylib on Mac''' 360 # Symlinks (assumed local) are recreated at dst 361 if os.path.islink(src): 362 linkto = os.readlink(src) 363 try: 364 os.remove(dst) # In case it already exists 365 except OSError: 366 pass 367 os.symlink(linkto, dst) 368 return 369 shutil.copy2(src, dst) 370 if self.setCompilers.getCompiler().find('win32fe') < 0 and os.path.splitext(dst)[1] == '.'+self.arLibSuffix: 371 self.executeShellCommand(self.ranlib+' '+dst) 372 if os.path.splitext(dst)[1] == '.dylib' and os.path.isfile('/usr/bin/install_name_tool'): 373 [output,err,flg] = self.executeShellCommand("otool -D "+src) 374 oldname = output[output.find("\n")+1:] 375 installName = oldname.replace(os.path.realpath(self.archDir), self.installDir) 376 self.executeShellCommand('/usr/bin/install_name_tool -id ' + installName + ' ' + dst) 377 # preserve the original timestamps - so that the .a vs .so time order is preserved 378 shutil.copystat(src,dst) 379 return 380 381 def installLib(self): 382 self.copies.extend(self.copytree(self.archLibDir, self.destLibDir, copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 383 self.copies.extend(self.copytree(os.path.join(self.archLibDir,'pkgconfig'), os.path.join(self.destLibDir,'pkgconfig'), copyFunc = self.copyLib, exclude = ['.DIR'],recurse = 0)) 384 return 385 386 387 def outputInstallDone(self): 388 from config.packages.make import getMakeUserPath 389 print('''\ 390==================================== 391Install complete. 392Now to check if the libraries are working do (in current directory): 393%s PETSC_DIR=%s PETSC_ARCH="" check 394====================================\ 395''' % (getMakeUserPath(self.arch), self.installDir)) 396 return 397 398 def outputDestDirDone(self): 399 print('''\ 400==================================== 401Copy to DESTDIR %s is now complete. 402Before use - please copy/install over to specified prefix: %s 403====================================\ 404''' % (self.destDir,self.installDir)) 405 return 406 407 def runsetup(self): 408 self.setup() 409 self.setupDirectories() 410 self.checkPrefix() 411 self.checkDestdir() 412 return 413 414 def runcopy(self): 415 if self.destDir == self.installDir: 416 print('*** Installing PETSc at prefix location:',self.destDir, ' ***') 417 else: 418 print('*** Copying PETSc to DESTDIR location:',self.destDir, ' ***') 419 if not os.path.exists(self.destDir): 420 try: 421 os.makedirs(self.destDir) 422 except: 423 print('********************************************************************') 424 print('Unable to create', self.destDir, 'Perhaps you need to do "sudo make install"') 425 print('********************************************************************') 426 sys.exit(1) 427 self.installIncludes() 428 self.installConf() 429 self.installBin() 430 self.installLib() 431 self.installShare() 432 return 433 434 def runfix(self): 435 self.fixConf() 436 return 437 438 def rundone(self): 439 self.createUninstaller() 440 if self.destDir == self.installDir: 441 self.outputInstallDone() 442 else: 443 self.outputDestDirDone() 444 return 445 446 def run(self): 447 self.runsetup() 448 self.runcopy() 449 self.runfix() 450 self.rundone() 451 return 452 453if __name__ == '__main__': 454 Installer(sys.argv[1:]).run() 455 # temporary hack - delete log files created by BuildSystem - when 'sudo make install' is invoked 456 delfiles=['RDict.db','RDict.log','buildsystem.log','default.log','buildsystem.log.bkp','default.log.bkp'] 457 for delfile in delfiles: 458 if os.path.exists(delfile) and (os.stat(delfile).st_uid==0): 459 os.remove(delfile) 460