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