xref: /petsc/config/PETSc/options/arch.py (revision 7f296bb328fcd4c99f2da7bfe8ba7ed8a4ebceee)
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    '''Forms the arch as GNU's configure would form it'''
31    import sys
32    arch = 'arch-' + sys.platform.replace('cygwin','mswin')
33    # use opt/debug, c/c++ tags.s
34    arch+= '-'+self.framework.argDB['with-clanguage'].lower().replace('+','x')
35    if self.framework.argDB['with-debugging']:
36      arch += '-debug'
37    else:
38      arch += '-opt'
39    self.nativeArch = arch
40    return
41
42  def configureArchitecture(self):
43    '''Checks if PETSC_ARCH is set and sets it if not set'''
44    # Warn if PETSC_ARCH doesn't match env variable
45    if 'PETSC_ARCH' in self.framework.argDB and 'PETSC_ARCH' in os.environ and self.framework.argDB['PETSC_ARCH'] != os.environ['PETSC_ARCH']:
46      self.logPrintWarning('''\
47PETSC_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'])))
48      os.environ['PETSC_ARCH'] = self.framework.argDB['PETSC_ARCH']
49    if 'with-petsc-arch' in self.framework.argDB:
50      self.arch = self.framework.argDB['with-petsc-arch']
51      msg = 'option -with-petsc-arch='+str(self.arch)
52    elif 'PETSC_ARCH' in self.framework.argDB:
53      self.arch = self.framework.argDB['PETSC_ARCH']
54      msg = 'option PETSC_ARCH='+str(self.arch)
55    elif 'PETSC_ARCH' in os.environ:
56      self.arch = os.environ['PETSC_ARCH']
57      msg = 'environment variable PETSC_ARCH='+str(self.arch)
58    else:
59      self.arch = self.nativeArch
60    if self.arch.find('/') >= 0 or self.arch.find('\\') >= 0:
61      raise RuntimeError('PETSC_ARCH should not contain path characters, but you have specified with '+msg)
62    if self.arch.startswith('-'):
63      raise RuntimeError('PETSC_ARCH should not start with "-", 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 not len(self.arch):
67      raise RuntimeError('PETSC_ARCH cannot be empty string. Use a valid string or do not set one. Currently set with '+msg)
68    self.archBase = re.sub(r'^(\w+)[-_]?.*$', r'\1', self.arch)
69    return
70
71  def makeDependency(self,hash,hashfile,hashfilepackages):
72    '''Deletes the current hashfile and saves the hashfile names and its value in framework so that'''
73    '''framework.Configure can create the file upon success of configure'''
74    import os
75    if hash:
76      self.framework.hash = hash
77      self.framework.hashfile = hashfile
78      self.logPrint('Setting hashfile: '+hashfile)
79      if hashfilepackages: self.framework.hashfilepackages = hashfilepackages
80    try:
81      self.logPrint('Deleting configure hash file: '+hashfile)
82      os.remove(hashfile)
83      self.logPrint('Deleted configure hash file: '+hashfile)
84    except:
85      self.logPrint('Unable to delete configure hash file: '+hashfile)
86
87
88  def checkDependency(self):
89    '''Checks if files in config have changed, the command line options have changed or the PATH has changed'''
90    '''  By default - checks if configure needs to be run'''
91    '''  If --arch-hash it manages the same information but it:'''
92    '''     * computes a short hash for the configuration <hashvalue>'''
93    '''     * sets self.arch and PETSC_ARCH to arch-<hashvalue>'''
94    '''       This results in the downloaded packages being installed once to the arch-<hasvalue> directory'''
95    '''       and a new directory with a different hash is created if the configuration changes.'''
96    '''     This mode is intended mostly for testing to reduce reconfigure and recompile times (not currently used)'''
97    '''  If --package-prefix-hash=directory is provided'''
98    '''     * computes a short hash for the configuration <hashvalue>'''
99    '''     * puts the downloaded external packages into location directory/hash'''
100    '''       This results in the downloaded packages being installed once'''
101    '''       and a new directory with a different hash is created if the configuration changes.'''
102    '''     This mode is intended mostly for testing to reduce time of reinstalling external packages'''
103    import os
104    import sys
105    import hashlib
106    import platform
107    import nargs
108    hash = 'Uname: '+platform.uname().system+' '+platform.uname().processor+'\n'
109    hash += 'PATH=' + os.environ.get('PATH', '') + '\n'
110    args = dict([(nargs.Arg.parseArgument(arg)[0], arg) for arg in self.framework.clArgs])
111    hash += 'args:\n' + '\n'.join('    '+a for a in sorted(args.values())) + '\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      self.logPrint('Computed hash to be used with --package-prefix-hash option: '+hprefix)
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.logPrint('Specified package-prefix-hash location %s not found! Attempting 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 prefix directory '+self.argDB['package-prefix-hash'])
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              self.logPrint('Found existing '+hashfilepackages+' reusing packages built in '+hashdirpackages)
163              status = True
164              break
165            else:
166              self.logPrint('Found existing '+hashdirpackages+' but it is incomplete so trying a longer directory name based on the hash')
167              continue # perhaps an incomplete build? use a longer hash
168          else:
169            if self.argDB['force']:
170              # since the previous hash associated with --package-prefix-hash
171              # (and hence its directory of built packages) is not available
172              # all the packages associated with that hash cannot be reused
173              raise RuntimeError('You cannot use --force with --package-prefix-hash=directory; you need to delete the $PETSC_ARCH directory and run configure again')
174            self.logPrint('Creating package-prefix-hash subdirectory '+hashdirpackages)
175            try:
176              os.mkdir(hashdirpackages)
177            except Exception as e:
178              raise RuntimeError('You must have write permission on --package-prefix-hash='+self.argDB['package-prefix-hash']+' directory')
179            status = True
180            break
181        if not status:
182          raise RuntimeError('Unable to create package-prefix-hash dir! Suggest cleaning up %s* !' % os.path.join(self.argDB['package-prefix-hash'],hprefix[0:6]) )
183        self.argDB['prefix'] = hashdirpackages
184
185    hashfile = os.path.join(self.arch,'lib','petsc','conf','configure-hash')
186
187    if self.argDB['force']:
188      self.logPrint('Forcing a new configuration requested by use')
189      self.makeDependency(hash,hashfile,hashfilepackages)
190      return
191    a = ''
192    try:
193      with open(hashfile, 'r') as f:
194        a = f.read()
195    except:
196      self.logPrint('No previous hashfile found')
197      self.makeDependency(hash,hashfile,hashfilepackages)
198      return
199    if a == hash:
200      try:
201        self.logPrint('Attempting to save lib/petsc/conf/petscvariables file')
202        with open(os.path.join('lib','petsc','conf','petscvariables'), 'w') as g:
203          g.write('PETSC_ARCH='+self.arch+'\n')
204          g.write('PETSC_DIR='+self.petscdir.dir+'\n')
205          g.write('include $(PETSC_DIR)/$(PETSC_ARCH)/lib/petsc/conf/petscvariables\n')
206          self.logPrint('Saved lib/petsc/conf/petscvariables file')
207      except:
208        self.logPrint('Unable to save lib/petsc/conf/petscvariables file')
209      self.logPrint('configure hash file: '+hashfile+' matches; no need to run configure.')
210      self.logPrintBox('Your configure options and state has not changed; no need to run configure\nHowever you can force a configure run using the option: --force')
211
212      import logger
213      from config.packages.make import getMakeUserPath
214      banner_ends   = 'xxx'
215      banner_middle = '=' * (logger.get_global_divider_length() - 2 * len(banner_ends))
216      banner_line   = banner_middle.join((banner_ends, banner_ends))
217      print(banner_line)
218      print(' Build PETSc libraries with:')
219      print('   %s PETSC_DIR=%s PETSC_ARCH=%s all' % (getMakeUserPath(self.arch), self.petscdir.dir, self.arch))
220      print(banner_line)
221      sys.exit()
222    self.logPrint('configure hash file: '+hashfile+' does not match, need to run configure')
223    self.makeDependency(hash,hashfile,hashfilepackages)
224
225  def configure(self):
226    self.executeTest(self.setNativeArchitecture)
227    self.executeTest(self.configureArchitecture)
228    # required by top-level configure.py
229    self.framework.arch = self.arch
230    self.checkDependency()
231    return
232