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