xref: /petsc/config/PETSc/options/arch.py (revision 54c656ecd1eed152dd1f31c4d3dfa2dca30fef2d)
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.logPrintWarning('''\
48PETSC_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'])))
49      os.environ['PETSC_ARCH'] = self.framework.argDB['PETSC_ARCH']
50    if 'with-petsc-arch' in self.framework.argDB:
51      self.arch = self.framework.argDB['with-petsc-arch']
52      msg = 'option -with-petsc-arch='+str(self.arch)
53    elif 'PETSC_ARCH' in self.framework.argDB:
54      self.arch = self.framework.argDB['PETSC_ARCH']
55      msg = 'option PETSC_ARCH='+str(self.arch)
56    elif 'PETSC_ARCH' in os.environ:
57      self.arch = os.environ['PETSC_ARCH']
58      msg = 'environment variable PETSC_ARCH='+str(self.arch)
59    else:
60      self.arch = self.nativeArch
61    if self.arch.find('/') >= 0 or self.arch.find('\\') >= 0:
62      raise RuntimeError('PETSC_ARCH should not contain path characters, but you have specified with '+msg)
63    if self.arch.startswith('-'):
64      raise RuntimeError('PETSC_ARCH should not start with "-", but you have specified with '+msg)
65    if self.arch.startswith('.'):
66      raise RuntimeError('PETSC_ARCH should not start with ".", but you have specified with '+msg)
67    if not len(self.arch):
68      raise RuntimeError('PETSC_ARCH cannot be empty string. Use a valid string or do not set one. Currently set with '+msg)
69    self.archBase = re.sub(r'^(\w+)[-_]?.*$', r'\1', self.arch)
70    return
71
72  def makeDependency(self,hash,hashfile,hashfilepackages):
73    '''Deletes the current hashfile and saves the hashfile names and its value in framework so that'''
74    '''framework.Configure can create the file upon success of configure'''
75    import os
76    if hash:
77      self.framework.hash = hash
78      self.framework.hashfile = hashfile
79      self.logPrint('Setting hashfile: '+hashfile)
80      if hashfilepackages: self.framework.hashfilepackages = hashfilepackages
81    try:
82      self.logPrint('Deleting configure hash file: '+hashfile)
83      os.remove(hashfile)
84      self.logPrint('Deleted configure hash file: '+hashfile)
85    except:
86      self.logPrint('Unable to delete configure hash file: '+hashfile)
87
88
89  def checkDependency(self):
90    '''Checks if files in config have changed, the command line options have changed or the PATH has changed'''
91    '''  By default - checks if configure needs to be run'''
92    '''  If --arch-hash it manages the same information but it:'''
93    '''     * computes a short hash for the configuration <hashvalue>'''
94    '''     * sets self.arch and PETSC_ARCH to arch-<hashvalue>'''
95    '''       This results in the downloaded packages being installed once to the arch-<hasvalue> directory'''
96    '''       and a new directory with a different hash is created if the configuration changes.'''
97    '''     This mode is intended mostly for testing to reduce reconfigure and recompile times (not currently used)'''
98    '''  If --package-prefix-hash=directory is provided'''
99    '''     * computes a short hash for the configuration <hashvalue>'''
100    '''     * puts the downloaded external packages into location directory/hash'''
101    '''       This results in the downloaded packages being installed once'''
102    '''       and a new directory with a different hash is created if the configuration changes.'''
103    '''     This mode is intended mostly for testing to reduce time of reinstalling external packages'''
104    import os
105    import sys
106    import hashlib
107    import platform
108    hash = 'Uname: '+platform.uname().system+' '+platform.uname().processor+'\n'
109    hash += 'PATH=' + os.environ.get('PATH', '') + '\n'
110    args = sorted(set(filter(lambda x: not (x.startswith('PETSC_ARCH') or x == '--force'),sys.argv[1:])))
111    hash += 'args:\n' + '\n'.join('    '+a for a in args) + '\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      if 'arch-hash' in self.argDB:
144        self.argDB['PETSC_ARCH'] = 'arch-'+hprefix[0:6]
145        self.arch = 'arch-'+hprefix[0:6]
146      else:
147        if not os.path.isdir(self.argDB['package-prefix-hash']):
148          self.logPrintBox('Specified package-prefix-hash location %s not found! Attemping to create this dir!' % self.argDB['package-prefix-hash'])
149          try:
150            os.makedirs(self.argDB['package-prefix-hash'])
151          except Exception as e:
152            self.logPrint('Error creating package-prefix-hash directory '+self.argDB['package-prefix-hash']+': '+str(e))
153            raise RuntimeError('You must have write permission to create this directory!')
154        status = False
155        for idx in range(6,len(hprefix)):
156          hashdirpackages = os.path.join(self.argDB['package-prefix-hash'],hprefix[0:idx])
157          hashfilepackages = os.path.join(hashdirpackages,'configure-hash')
158          if os.path.isdir(hashdirpackages):
159            if os.path.exists(hashfilepackages):
160              self.argDB['package-prefix-hash'] = 'reuse' # indicates prefix libraries already built, no need to rebuild
161              status = True
162              break
163            else: continue # perhaps an incomplete build? use a longer hash
164          else:
165            try:
166              os.mkdir(hashdirpackages)
167            except Exception as e:
168              self.logPrint('Error creating package-prefix-hash directory '+hashdirpackages+': '+str(e))
169              raise RuntimeError('You must have write permission on --package-prefix-hash='+self.argDB['package-prefix-hash']+' directory')
170            status = True
171            break
172        if not status:
173          raise RuntimeError('Unable to create package-prefix-hash dir! Suggest cleaning up %s* !' % os.path.join(self.argDB['package-prefix-hash'],hprefix[0:6]) )
174        self.argDB['prefix'] = hashdirpackages
175
176    hashfile = os.path.join(self.arch,'lib','petsc','conf','configure-hash')
177
178    if self.argDB['force']:
179      self.logPrint('Forcing a new configuration requested by use')
180      self.makeDependency(hash,hashfile,hashfilepackages)
181      return
182    a = ''
183    try:
184      with open(hashfile, 'r') as f:
185        a = f.read()
186    except:
187      self.logPrint('No previous hashfile found')
188      self.makeDependency(hash,hashfile,hashfilepackages)
189      return
190    if a == hash:
191      try:
192        self.logPrint('Attempting to save lib/petsc/conf/petscvariables file')
193        with open(os.path.join('lib','petsc','conf','petscvariables'), 'w') as g:
194          g.write('PETSC_ARCH='+self.arch+'\n')
195          g.write('PETSC_DIR='+self.petscdir.dir+'\n')
196          g.write('include $(PETSC_DIR)/$(PETSC_ARCH)/lib/petsc/conf/petscvariables\n')
197          self.logPrint('Saved lib/petsc/conf/petscvariables file')
198      except:
199        self.logPrint('Unable to save lib/petsc/conf/petscvariables file')
200      self.logPrint('configure hash file: '+hashfile+' matches; no need to run configure.')
201      print('Your configure options and state has not changed; no need to run configure')
202      print('However you can force a configure run using the option: --force')
203
204      from config.packages.make import getMakeUserPath
205      print('xxx=========================================================================xxx')
206      print(' Build PETSc libraries with:')
207      print('   %s PETSC_DIR=%s PETSC_ARCH=%s all' % (getMakeUserPath(self.arch), self.petscdir.dir, self.arch))
208      print('xxx=========================================================================xxx')
209      sys.exit()
210    self.logPrint('configure hash file: '+hashfile+' does not match, need to run configure')
211    self.makeDependency(hash,hashfile,hashfilepackages)
212
213  def configure(self):
214    self.executeTest(self.setNativeArchitecture)
215    self.executeTest(self.configureArchitecture)
216    # required by top-level configure.py
217    self.framework.arch = self.arch
218    self.checkDependency()
219    return
220