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