1import config.base 2import os 3import re 4 5class Configure(config.base.Configure): 6 def __init__(self, framework): 7 config.base.Configure.__init__(self, framework) 8 self.headerPrefix = 'PETSC' 9 self.substPrefix = 'PETSC' 10 return 11 12 def __str1__(self): 13 if not hasattr(self, 'arch'): 14 return '' 15 return ' PETSC_ARCH: '+str(self.arch)+'\n' 16 17 def setupHelp(self, help): 18 import nargs 19 help.addArgument('PETSc', '-PETSC_ARCH=<string>', nargs.Arg(None, None, 'The configuration name')) 20 help.addArgument('PETSc', '-with-petsc-arch=<string>',nargs.Arg(None, None, 'The configuration name')) 21 help.addArgument('PETSc', '-force=<bool>', nargs.ArgBool(None, 0, 'Bypass configure hash caching, and run to completion')) 22 return 23 24 def setupDependencies(self, framework): 25 self.sourceControl = framework.require('config.sourceControl',self) 26 self.petscdir = framework.require('PETSc.options.petscdir', self) 27 return 28 29 def setNativeArchitecture(self): 30 '''Forms the arch as GNU's configure would form it''' 31 import sys 32 arch = 'arch-' + sys.platform.replace('cygwin','mswin') 33 # use opt/debug, c/c++ tags.s 34 arch+= '-'+self.framework.argDB['with-clanguage'].lower().replace('+','x') 35 if self.framework.argDB['with-debugging']: 36 arch += '-debug' 37 else: 38 arch += '-opt' 39 self.nativeArch = arch 40 return 41 42 def configureArchitecture(self): 43 '''Checks if PETSC_ARCH is set and sets it if not set''' 44 # Warn if PETSC_ARCH doesn't match env variable 45 if 'PETSC_ARCH' in self.framework.argDB and 'PETSC_ARCH' in os.environ and self.framework.argDB['PETSC_ARCH'] != os.environ['PETSC_ARCH']: 46 self.logPrintWarning('''\ 47PETSC_ARCH from environment does not match command-line or name of script. Using from command-line or name of script: %s, ignoring environment: %s''' % (str(self.framework.argDB['PETSC_ARCH']), str(os.environ['PETSC_ARCH']))) 48 os.environ['PETSC_ARCH'] = self.framework.argDB['PETSC_ARCH'] 49 if 'with-petsc-arch' in self.framework.argDB: 50 self.arch = self.framework.argDB['with-petsc-arch'] 51 msg = 'option -with-petsc-arch='+str(self.arch) 52 elif 'PETSC_ARCH' in self.framework.argDB: 53 self.arch = self.framework.argDB['PETSC_ARCH'] 54 msg = 'option PETSC_ARCH='+str(self.arch) 55 elif 'PETSC_ARCH' in os.environ: 56 self.arch = os.environ['PETSC_ARCH'] 57 msg = 'environment variable PETSC_ARCH='+str(self.arch) 58 else: 59 self.arch = self.nativeArch 60 if self.arch.find('/') >= 0 or self.arch.find('\\') >= 0: 61 raise RuntimeError('PETSC_ARCH should not contain path characters, but you have specified with '+msg) 62 if self.arch.startswith('-'): 63 raise RuntimeError('PETSC_ARCH should not start with "-", but you have specified with '+msg) 64 if self.arch.startswith('.'): 65 raise RuntimeError('PETSC_ARCH should not start with ".", but you have specified with '+msg) 66 if not len(self.arch): 67 raise RuntimeError('PETSC_ARCH cannot be empty string. Use a valid string or do not set one. Currently set with '+msg) 68 self.archBase = re.sub(r'^(\w+)[-_]?.*$', r'\1', self.arch) 69 return 70 71 def makeDependency(self,hash,hashfile,hashfilepackages): 72 '''Deletes the current hashfile and saves the hashfile names and its value in framework so that''' 73 '''framework.Configure can create the file upon success of configure''' 74 import os 75 if hash: 76 self.framework.hash = hash 77 self.framework.hashfile = hashfile 78 self.logPrint('Setting hashfile: '+hashfile) 79 if hashfilepackages: self.framework.hashfilepackages = hashfilepackages 80 try: 81 self.logPrint('Deleting configure hash file: '+hashfile) 82 os.remove(hashfile) 83 self.logPrint('Deleted configure hash file: '+hashfile) 84 except: 85 self.logPrint('Unable to delete configure hash file: '+hashfile) 86 87 88 def checkDependency(self): 89 '''Checks if files in config have changed, the command line options have changed or the PATH has changed''' 90 ''' By default - checks if configure needs to be run''' 91 ''' If --arch-hash it manages the same information but it:''' 92 ''' * computes a short hash for the configuration <hashvalue>''' 93 ''' * sets self.arch and PETSC_ARCH to arch-<hashvalue>''' 94 ''' This results in the downloaded packages being installed once to the arch-<hasvalue> directory''' 95 ''' and a new directory with a different hash is created if the configuration changes.''' 96 ''' This mode is intended mostly for testing to reduce reconfigure and recompile times (not currently used)''' 97 ''' If --package-prefix-hash=directory is provided''' 98 ''' * computes a short hash for the configuration <hashvalue>''' 99 ''' * puts the downloaded external packages into location directory/hash''' 100 ''' This results in the downloaded packages being installed once''' 101 ''' and a new directory with a different hash is created if the configuration changes.''' 102 ''' This mode is intended mostly for testing to reduce time of reinstalling external packages''' 103 import os 104 import sys 105 import hashlib 106 import platform 107 import nargs 108 hash = 'Uname: '+platform.uname().system+' '+platform.uname().processor+'\n' 109 hash += 'PATH=' + os.environ.get('PATH', '') + '\n' 110 args = dict([(nargs.Arg.parseArgument(arg)[0], arg) for arg in self.framework.clArgs]) 111 hash += 'args:\n' + '\n'.join(' '+a for a in sorted(args.values())) + '\n' 112 chash='' 113 try: 114 for root, dirs, files in os.walk('config'): 115 if root == 'config': 116 dirs.remove('examples') 117 for f in files: 118 if not f.endswith('.py') or f.startswith('.') or f.startswith('#'): 119 continue 120 fname = os.path.join(root, f) 121 with open(fname,'rb') as f: 122 chash += hashlib.sha256(f.read()).hexdigest() + ' ' + fname + '\n' 123 except: 124 self.logPrint('Error generating file list/hash from config directory for configure hash, forcing new configuration') 125 return 126 hash += '\n'.join(sorted(chash.splitlines())) 127 hashfilepackages = None 128 # Generate short hash to use for the arch so the same arch can be reused if the configuration files don't change 129 if 'arch-hash' in self.argDB: 130 if self.argDB['prefix']: 131 raise RuntimeError('Cannot provide --prefix and --arch-hash') 132 if hasattr(self.argDB,'PETSC_ARCH'): 133 raise RuntimeError('Cannot provide PETSC_ARCH and --arch-hash') 134 if 'package-prefix-hash' in self.argDB: 135 raise RuntimeError('Cannot provide --arch-hash and --package-prefix-hash') 136 if os.getenv('PETSC_ARCH'): 137 raise RuntimeError('Do not set the environmental variable PETSC_ARCH and use --arch-hash') 138 if 'arch-hash' in self.argDB or 'package-prefix-hash' in self.argDB: 139 import hashlib 140 m = hashlib.md5() 141 m.update(hash.encode('utf-8')) 142 hprefix = m.hexdigest() 143 self.logPrint('Computed hash to be used with --package-prefix-hash option: '+hprefix) 144 if 'arch-hash' in self.argDB: 145 self.argDB['PETSC_ARCH'] = 'arch-'+hprefix[0:6] 146 self.arch = 'arch-'+hprefix[0:6] 147 else: 148 if not os.path.isdir(self.argDB['package-prefix-hash']): 149 self.logPrint('Specified package-prefix-hash location %s not found! Attempting to create this dir!' % self.argDB['package-prefix-hash']) 150 try: 151 os.makedirs(self.argDB['package-prefix-hash']) 152 except Exception as e: 153 self.logPrint('Error creating package-prefix-hash directory '+self.argDB['package-prefix-hash']+': '+str(e)) 154 raise RuntimeError('You must have write permission to create prefix directory '+self.argDB['package-prefix-hash']) 155 status = False 156 for idx in range(6,len(hprefix)): 157 hashdirpackages = os.path.join(self.argDB['package-prefix-hash'],hprefix[0:idx]) 158 hashfilepackages = os.path.join(hashdirpackages,'configure-hash') 159 if os.path.isdir(hashdirpackages): 160 if os.path.exists(hashfilepackages): 161 self.argDB['package-prefix-hash'] = 'reuse' # indicates prefix libraries already built, no need to rebuild 162 self.logPrint('Found existing '+hashfilepackages+' reusing packages built in '+hashdirpackages) 163 status = True 164 break 165 else: 166 self.logPrint('Found existing '+hashdirpackages+' but it is incomplete so trying a longer directory name based on the hash') 167 continue # perhaps an incomplete build? use a longer hash 168 else: 169 if self.argDB['force']: 170 # since the previous hash associated with --package-prefix-hash 171 # (and hence its directory of built packages) is not available 172 # all the packages associated with that hash cannot be reused 173 raise RuntimeError('You cannot use --force with --package-prefix-hash=directory; you need to delete the $PETSC_ARCH directory and run configure again') 174 self.logPrint('Creating package-prefix-hash subdirectory '+hashdirpackages) 175 try: 176 os.mkdir(hashdirpackages) 177 except Exception as e: 178 raise RuntimeError('You must have write permission on --package-prefix-hash='+self.argDB['package-prefix-hash']+' directory') 179 status = True 180 break 181 if not status: 182 raise RuntimeError('Unable to create package-prefix-hash dir! Suggest cleaning up %s* !' % os.path.join(self.argDB['package-prefix-hash'],hprefix[0:6]) ) 183 self.argDB['prefix'] = hashdirpackages 184 185 hashfile = os.path.join(self.arch,'lib','petsc','conf','configure-hash') 186 187 if self.argDB['force']: 188 self.logPrint('Forcing a new configuration requested by use') 189 self.makeDependency(hash,hashfile,hashfilepackages) 190 return 191 a = '' 192 try: 193 with open(hashfile, 'r') as f: 194 a = f.read() 195 except: 196 self.logPrint('No previous hashfile found') 197 self.makeDependency(hash,hashfile,hashfilepackages) 198 return 199 if a == hash: 200 try: 201 self.logPrint('Attempting to save lib/petsc/conf/petscvariables file') 202 with open(os.path.join('lib','petsc','conf','petscvariables'), 'w') as g: 203 g.write('PETSC_ARCH='+self.arch+'\n') 204 g.write('PETSC_DIR='+self.petscdir.dir+'\n') 205 g.write('include $(PETSC_DIR)/$(PETSC_ARCH)/lib/petsc/conf/petscvariables\n') 206 self.logPrint('Saved lib/petsc/conf/petscvariables file') 207 except: 208 self.logPrint('Unable to save lib/petsc/conf/petscvariables file') 209 self.logPrint('configure hash file: '+hashfile+' matches; no need to run configure.') 210 print('Your configure options and state has not changed; no need to run configure') 211 print('However you can force a configure run using the option: --force') 212 213 import logger 214 from config.packages.make import getMakeUserPath 215 banner_ends = 'xxx' 216 banner_middle = '=' * (logger.get_global_divider_length() - 2 * len(banner_ends)) 217 banner_line = banner_middle.join((banner_ends, banner_ends)) 218 print(banner_line) 219 print(' Build PETSc libraries with:') 220 print(' %s PETSC_DIR=%s PETSC_ARCH=%s all' % (getMakeUserPath(self.arch), self.petscdir.dir, self.arch)) 221 print(banner_line) 222 sys.exit() 223 self.logPrint('configure hash file: '+hashfile+' does not match, need to run configure') 224 self.makeDependency(hash,hashfile,hashfilepackages) 225 226 def configure(self): 227 self.executeTest(self.setNativeArchitecture) 228 self.executeTest(self.configureArchitecture) 229 # required by top-level configure.py 230 self.framework.arch = self.arch 231 self.checkDependency() 232 return 233