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