xref: /petsc/config/PETSc/options/arch.py (revision ccb4e88a40f0b86eaeca07ff64c64e4de2fae686)
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    if sys.version_info < (3,):
110      hash = 'Uname: '+platform.uname()[0]+' '+platform.uname()[4]+'\n'
111    else:
112      hash = 'Uname: '+platform.uname().system+' '+platform.uname().processor+'\n'
113    hash += 'PATH=' + os.environ.get('PATH', '') + '\n'
114    args = sorted(set(filter(lambda x: not (x.startswith('PETSC_ARCH') or x == '--force'),sys.argv[1:])))
115    hash += 'args:\n' + '\n'.join('    '+a for a in args) + '\n'
116    chash=''
117    try:
118      for root, dirs, files in os.walk('config'):
119        if root == 'config':
120          dirs.remove('examples')
121        for f in files:
122          if not f.endswith('.py') or f.startswith('.') or f.startswith('#'):
123            continue
124          fname = os.path.join(root, f)
125          with open(fname,'rb') as f:
126            chash += hashlib.sha256(f.read()).hexdigest() + '  ' + fname + '\n'
127    except:
128      self.logPrint('Error generating file list/hash from config directory for configure hash, forcing new configuration')
129      return
130    hash += '\n'.join(sorted(chash.splitlines()))
131    hashfilepackages = None
132    # Generate short hash to use for the arch so the same arch can be reused if the configuration files don't change
133    if 'arch-hash' in self.argDB:
134      if self.argDB['prefix']:
135        raise RuntimeError('Cannot provide --prefix and --arch-hash')
136      if hasattr(self.argDB,'PETSC_ARCH'):
137        raise RuntimeError('Cannot provide PETSC_ARCH and --arch-hash')
138      if 'package-prefix-hash' in self.argDB:
139        raise RuntimeError('Cannot provide --arch-hash and --package-prefix-hash')
140      if os.getenv('PETSC_ARCH'):
141        raise RuntimeError('Do not set the environmental variable PETSC_ARCH and use --arch-hash')
142    if 'arch-hash' in self.argDB or 'package-prefix-hash' in self.argDB:
143      import hashlib
144      m = hashlib.md5()
145      m.update(hash.encode('utf-8'))
146      hprefix = m.hexdigest()
147      if 'arch-hash' in self.argDB:
148        self.argDB['PETSC_ARCH'] = 'arch-'+hprefix[0:6]
149        self.arch = 'arch-'+hprefix[0:6]
150      else:
151        if not os.path.isdir(self.argDB['package-prefix-hash']):
152          self.logPrintBox('Specified package-prefix-hash location %s not found! Attemping to create this dir!' % self.argDB['package-prefix-hash'])
153          try:
154            os.makedirs(self.argDB['package-prefix-hash'])
155          except Exception as e:
156            self.logPrint('Error creating package-prefix-hash directory '+self.argDB['package-prefix-hash']+': '+str(e))
157            raise RuntimeError('You must have write permission to create this directory!')
158        status = False
159        for idx in range(6,len(hprefix)):
160          hashdirpackages = os.path.join(self.argDB['package-prefix-hash'],hprefix[0:idx])
161          hashfilepackages = os.path.join(hashdirpackages,'configure-hash')
162          if os.path.isdir(hashdirpackages):
163            if os.path.exists(hashfilepackages):
164              self.argDB['package-prefix-hash'] = 'reuse' # indicates prefix libraries already built, no need to rebuild
165              status = True
166              break
167            else: continue # perhaps an incomplete build? use a longer hash
168          else:
169            try:
170              os.mkdir(hashdirpackages)
171            except Exception as e:
172              self.logPrint('Error creating package-prefix-hash directory '+hashdirpackages+': '+str(e))
173              raise RuntimeError('You must have write permission on --package-prefix-hash='+self.argDB['package-prefix-hash']+' directory')
174            status = True
175            break
176        if not status:
177          raise RuntimeError('Unable to create package-prefix-hash dir! Suggest cleaning up %s* !' % os.path.join(self.argDB['package-prefix-hash'],hprefix[0:6]) )
178        self.argDB['prefix'] = hashdirpackages
179
180    hashfile = os.path.join(self.arch,'lib','petsc','conf','configure-hash')
181
182    if self.argDB['force']:
183      self.logPrint('Forcing a new configuration requested by use')
184      self.makeDependency(hash,hashfile,hashfilepackages)
185      return
186    a = ''
187    try:
188      with open(hashfile, 'r') as f:
189        a = f.read()
190    except:
191      self.logPrint('No previous hashfile found')
192      self.makeDependency(hash,hashfile,hashfilepackages)
193      return
194    if a == hash:
195      try:
196        self.logPrint('Attempting to save lib/petsc/conf/petscvariables file')
197        with open(os.path.join('lib','petsc','conf','petscvariables'), 'w') as g:
198          g.write('PETSC_ARCH='+self.arch+'\n')
199          g.write('PETSC_DIR='+self.petscdir.dir+'\n')
200          g.write('include $(PETSC_DIR)/$(PETSC_ARCH)/lib/petsc/conf/petscvariables\n')
201          self.logPrint('Saved lib/petsc/conf/petscvariables file')
202      except:
203        self.logPrint('Unable to save lib/petsc/conf/petscvariables file')
204      self.logPrint('configure hash file: '+hashfile+' matches; no need to run configure.')
205      print('Your configure options and state has not changed; no need to run configure')
206      print('However you can force a configure run using the option: --force')
207
208      from config.packages.make import getMakeUserPath
209      print('xxx=========================================================================xxx')
210      print(' Build PETSc libraries with:')
211      print('   %s PETSC_DIR=%s PETSC_ARCH=%s all' % (getMakeUserPath(self.arch), self.petscdir.dir, self.arch))
212      print('xxx=========================================================================xxx')
213      sys.exit()
214    self.logPrint('configure hash file: '+hashfile+' does not match, need to run configure')
215    self.makeDependency(hash,hashfile,hashfilepackages)
216
217  def configure(self):
218    self.executeTest(self.setNativeArchitecture)
219    self.executeTest(self.configureArchitecture)
220    # required by top-level configure.py
221    self.framework.arch = self.arch
222    self.checkDependency()
223    return
224