xref: /petsc/config/PETSc/options/arch.py (revision 4e4bbfaa3814cc83b5851d85be69070845f5653e)
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    args = sorted(set(filter(lambda x: not (x.startswith('PETSC_ARCH') or x == '--force'),sys.argv[1:])))
109    hash = 'args:\n' + '\n'.join('    '+a for a in args) + '\n'
110    hash += 'PATH=' + os.environ.get('PATH', '') + '\n'
111    chash=''
112    try:
113      for root, dirs, files in os.walk('config'):
114        if root == 'config':
115          dirs.remove('examples')
116        for f in files:
117          if not f.endswith('.py') or f.startswith('.') or f.startswith('#'):
118            continue
119          fname = os.path.join(root, f)
120          with open(fname,'rb') as f:
121            chash += hashlib.sha256(f.read()).hexdigest() + '  ' + fname + '\n'
122    except:
123      self.logPrint('Error generating file list/hash from config directory for configure hash, forcing new configuration')
124      return
125    hash += '\n'.join(sorted(chash.splitlines()))
126    hashfilepackages = None
127    # Generate short hash to use for the arch so the same arch can be reused if the configuration files don't change
128    if 'arch-hash' in self.argDB:
129      if self.argDB['prefix']:
130        raise RuntimeError('Cannot provide --prefix and --arch-hash')
131      if hasattr(self.argDB,'PETSC_ARCH'):
132        raise RuntimeError('Cannot provide PETSC_ARCH and --arch-hash')
133      if 'package-prefix-hash' in self.argDB:
134        raise RuntimeError('Cannot provide --arch-hash and --package-prefix-hash')
135      if os.getenv('PETSC_ARCH'):
136        raise RuntimeError('Do not set the environmental variable PETSC_ARCH and use --arch-hash')
137    if 'arch-hash' in self.argDB or 'package-prefix-hash' in self.argDB:
138      import hashlib
139      m = hashlib.md5()
140      m.update(hash.encode('utf-8'))
141      hprefix = m.hexdigest()
142      if 'arch-hash' in self.argDB:
143        self.argDB['PETSC_ARCH'] = 'arch-'+hprefix[0:6]
144        self.arch = 'arch-'+hprefix[0:6]
145      else:
146        if not os.path.isdir(self.argDB['package-prefix-hash']):
147          raise RuntimeError('--package-prefix-hash '+self.argDB['package-prefix-hash']+' directory does not exist\n')
148        self.argDB['prefix'] = os.path.join(self.argDB['package-prefix-hash'],hprefix[0:6])
149        if not os.path.isdir(self.argDB['prefix']):
150          os.mkdir(self.argDB['prefix'])
151          hashfilepackages = os.path.join(self.argDB['prefix'],'configure-hash')
152        else:
153          try:
154            with open(os.path.join(self.argDB['prefix'],'configure-hash'), 'r') as f:
155              a = f.read()
156          except:
157            self.logPrint('No previous hashfilepackages found')
158            a = ''
159          if a == hash:
160            self.logPrint('Reusing download packages in '+self.argDB['prefix'])
161            self.argDB['package-prefix-hash'] = 'reuse' # indicates prefix libraries already built, no need to rebuild
162
163    hashfile = os.path.join(self.arch,'lib','petsc','conf','configure-hash')
164
165    if self.argDB['force']:
166      self.logPrint('Forcing a new configuration requested by use')
167      self.makeDependency(hash,hashfile,hashfilepackages)
168      return
169    a = ''
170    try:
171      with open(hashfile, 'r') as f:
172        a = f.read()
173    except:
174      self.logPrint('No previous hashfile found')
175      self.makeDependency(hash,hashfile,hashfilepackages)
176      return
177    if a == hash:
178      try:
179        self.logPrint('Attempting to save lib/petsc/conf/petscvariables file')
180        with open(os.path.join('lib','petsc','conf','petscvariables'), 'w') as g:
181          g.write('PETSC_ARCH='+self.arch+'\n')
182          g.write('PETSC_DIR='+self.petscdir.dir+'\n')
183          g.write('include $(PETSC_DIR)/$(PETSC_ARCH)/lib/petsc/conf/petscvariables\n')
184          self.logPrint('Saved lib/petsc/conf/petscvariables file')
185      except:
186        self.logPrint('Unable to save lib/petsc/conf/petscvariables file')
187      self.logPrint('configure hash file: '+hashfile+' matches; no need to run configure.')
188      print('Your configure options and state has not changed; no need to run configure')
189      print('However you can force a configure run using the option: --force')
190      sys.exit()
191    self.logPrint('configure hash file: '+hashfile+' does not match\n'+a+'\n---\n'+hash+'\n need to run configure')
192    self.makeDependency(hash,hashfile,hashfilepackages)
193
194  def configure(self):
195    self.executeTest(self.setNativeArchitecture)
196    self.executeTest(self.configureArchitecture)
197    # required by top-level configure.py
198    self.framework.arch = self.arch
199    self.checkDependency()
200    return
201