xref: /petsc/config/configure.py (revision e5a36eccef3d6b83a2c625c30d0dfd5adb4001f2)
1#!/usr/bin/env python
2from __future__ import print_function
3import os, sys
4
5extraLogs = []
6petsc_arch = ''
7
8# Use en_US as language so that BuildSystem parses compiler messages in english
9if 'LC_LOCAL' in os.environ and os.environ['LC_LOCAL'] != '' and os.environ['LC_LOCAL'] != 'en_US' and os.environ['LC_LOCAL']!= 'en_US.UTF-8': os.environ['LC_LOCAL'] = 'en_US.UTF-8'
10if 'LANG' in os.environ and os.environ['LANG'] != '' and os.environ['LANG'] != 'en_US' and os.environ['LANG'] != 'en_US.UTF-8': os.environ['LANG'] = 'en_US.UTF-8'
11
12if sys.version_info < (2,6):
13  print('************************************************************************')
14  print('*      Python version 2.6+ or 3.4+ is required to run ./configure      *')
15  print('*         Try: "python2.7 ./configure" or "python3 ./configure"        *')
16  print('************************************************************************')
17  sys.exit(4)
18
19def check_for_option_mistakes(opts):
20  for opt in opts[1:]:
21    name = opt.split('=')[0]
22    if name.find('_') >= 0:
23      exception = False
24      for exc in ['mkl_sparse', 'mkl_sparse_optimize', 'mkl_cpardiso', 'mkl_pardiso', 'superlu_dist', 'PETSC_ARCH', 'PETSC_DIR', 'CXX_CXXFLAGS', 'LD_SHARED', 'CC_LINKER_FLAGS', 'CXX_LINKER_FLAGS', 'FC_LINKER_FLAGS', 'AR_FLAGS', 'C_VERSION', 'CXX_VERSION', 'FC_VERSION', 'size_t', 'MPI_Comm','MPI_Fint','int64_t']:
25        if name.find(exc) >= 0:
26          exception = True
27      if not exception:
28        raise ValueError('The option '+name+' should probably be '+name.replace('_', '-'));
29    if opt.find('=') >=0:
30      optval = opt.split('=')[1]
31      if optval == 'ifneeded':
32        raise ValueError('The option '+opt+' should probably be '+opt.replace('ifneeded', '1'));
33  return
34
35def check_for_unsupported_combinations(opts):
36  if '--with-precision=single' in opts and '--with-clanguage=cxx' in opts and '--with-scalar-type=complex' in opts:
37    sys.exit(ValueError('PETSc does not support single precision complex with C++ clanguage, run with --with-clanguage=c'))
38
39def check_for_option_changed(opts):
40# Document changes in command line options here.
41  optMap = [('with-64bit-indices','with-64-bit-indices'),
42            ('with-mpi-exec','with-mpiexec'),
43            ('c-blas-lapack','f2cblaslapack'),
44            ('cholmod','suitesparse'),
45            ('umfpack','suitesparse'),
46            ('f-blas-lapack','fblaslapack'),
47            ('with-cuda-arch',
48             'CUDAFLAGS=-arch'),
49            ('with-packages-dir','with-packages-download-dir'),
50            ('with-external-packages-dir','with-packages-build-dir'),
51            ('package-dirs','with-packages-search-path'),
52            ('download-petsc4py-python','with-python-exec'),
53            ('search-dirs','with-executables-search-path')]
54  for opt in opts[1:]:
55    optname = opt.split('=')[0].strip('-')
56    for oldname,newname in optMap:
57      if optname.find(oldname) >=0:
58        raise ValueError('The option '+opt+' should probably be '+opt.replace(oldname,newname))
59  return
60
61def check_petsc_arch(opts):
62  # If PETSC_ARCH not specified - use script name (if not configure.py)
63  global petsc_arch
64  found = 0
65  for name in opts:
66    if name.find('PETSC_ARCH=') >= 0:
67      petsc_arch=name.split('=')[1]
68      found = 1
69      break
70  # If not yet specified - use the filename of script
71  if not found:
72      filename = os.path.basename(sys.argv[0])
73      if not filename.startswith('configure') and not filename.startswith('reconfigure') and not filename.startswith('setup'):
74        petsc_arch=os.path.splitext(os.path.basename(sys.argv[0]))[0]
75        useName = 'PETSC_ARCH='+petsc_arch
76        opts.append(useName)
77  return 0
78
79def chkenable():
80  #Replace all 'enable-'/'disable-' with 'with-'=0/1/tail
81  #enable-fortran is a special case, the resulting --with-fortran is ambiguous.
82  #Would it mean --with-fc=
83  en_dash = u'\N{EN DASH}'
84  if sys.version_info < (3, 0):
85    en_dash = en_dash.encode('utf-8')
86  for l in range(0,len(sys.argv)):
87    name = sys.argv[l]
88
89    if name.find(en_dash)  >= 0:
90      sys.argv[l] = name.replace(en_dash,'-')
91    if name.find('enable-cxx') >= 0:
92      if name.find('=') == -1:
93        sys.argv[l] = name.replace('enable-cxx','with-clanguage=C++')
94      else:
95        head, tail = name.split('=', 1)
96        if tail=='0':
97          sys.argv[l] = head.replace('enable-cxx','with-clanguage=C')
98        else:
99          sys.argv[l] = head.replace('enable-cxx','with-clanguage=C++')
100      continue
101    if name.find('disable-cxx') >= 0:
102      if name.find('=') == -1:
103        sys.argv[l] = name.replace('disable-cxx','with-clanguage=C')
104      else:
105        head, tail = name.split('=', 1)
106        if tail == '0':
107          sys.argv[l] = head.replace('disable-cxx','with-clanguage=C++')
108        else:
109          sys.argv[l] = head.replace('disable-cxx','with-clanguage=C')
110      continue
111
112
113    if name.find('enable-') >= 0:
114      if name.find('=') == -1:
115        sys.argv[l] = name.replace('enable-','with-')+'=1'
116      else:
117        head, tail = name.split('=', 1)
118        sys.argv[l] = head.replace('enable-','with-')+'='+tail
119    if name.find('disable-') >= 0:
120      if name.find('=') == -1:
121        sys.argv[l] = name.replace('disable-','with-')+'=0'
122      else:
123        head, tail = name.split('=', 1)
124        if tail == '1': tail = '0'
125        sys.argv[l] = head.replace('disable-','with-')+'='+tail
126    if name.find('without-') >= 0:
127      if name.find('=') == -1:
128        sys.argv[l] = name.replace('without-','with-')+'=0'
129      else:
130        head, tail = name.split('=', 1)
131        if tail == '1': tail = '0'
132        sys.argv[l] = head.replace('without-','with-')+'='+tail
133
134def argsAddDownload(value,deps = [],options = []):
135  # Adds --download-value to args if the command line DOES NOT already has --with-value or --download-value in it
136  # this is to prevent introducing conflicting arguments to ones that already exist
137  for opt in sys.argv[1:]:
138    optname = opt.split('=')[0].strip('-')
139    if optname in ['download-'+value,'with-'+value,'with-'+value+'-dir','with-'+value+'-include','with-'+value+'-lib']: return
140  sys.argv.append('--download-'+value)
141  for i in deps:
142    argsAddDownload(i)
143  for i in options:
144    sys.argv.append(i)
145
146def chksynonyms():
147  #replace common configure options with ones that PETSc BuildSystem recognizes
148  downloadxsdk = 0
149  downloadideas = 0
150  for l in range(0,len(sys.argv)):
151    name = sys.argv[l]
152
153    if name.find('download-xsdk=') >= 0 or name.endswith('download-xsdk'):
154      downloadxsdk = 1
155
156    if name.find('download-ideas=') >= 0 or name.endswith('download-ideas'):
157      downloadideas = 1
158
159    if name.find('with-blas-lapack') >= 0:
160      sys.argv[l] = name.replace('with-blas-lapack','with-blaslapack')
161
162    if name.find('with-debug=') >= 0 or name.endswith('with-debug'):
163      if name.find('=') == -1:
164        sys.argv[l] = name.replace('with-debug','with-debugging')+'=1'
165      else:
166        head, tail = name.split('=', 1)
167        sys.argv[l] = head.replace('with-debug','with-debugging')+'='+tail
168
169    if name.find('with-shared=') >= 0 or name.endswith('with-shared'):
170      if name.find('=') == -1:
171        sys.argv[l] = name.replace('with-shared','with-shared-libraries')+'=1'
172      else:
173        head, tail = name.split('=', 1)
174        sys.argv[l] = head.replace('with-shared','with-shared-libraries')+'='+tail
175
176    if name.find('with-index-size=') >=0:
177      head,tail = name.split('=',1)
178      if int(tail)==32:
179        sys.argv[l] = '--with-64-bit-indices=0'
180      elif int(tail)==64:
181        sys.argv[l] = '--with-64-bit-indices=1'
182      else:
183        raise RuntimeError('--with-index-size= must be 32 or 64')
184
185    if name.find('with-precision=') >=0:
186      head,tail = name.split('=',1)
187      if tail.find('quad')>=0:
188        sys.argv[l]='--with-precision=__float128'
189
190  if downloadideas:
191    downloadxsdk = 1
192    argsAddDownload('alquimia')
193    # mstk currently cannot build a shared library
194    argsAddDownload('mstk',[],['--download-mstk-shared=0'])
195    argsAddDownload('ascem-io')
196    argsAddDownload('unittestcpp')
197
198  if downloadxsdk:
199    # Common external libraries
200    argsAddDownload('pflotran')
201    argsAddDownload('hdf5',['zlib'])
202    argsAddDownload('netcdf')
203    argsAddDownload('metis')
204
205    argsAddDownload('superlu_dist',['parmetis'])
206
207    argsAddDownload('hypre')
208
209    argsAddDownload('trilinos',['boost','xsdktrilinos'],['--with-cxx-dialect=C++11'])
210
211
212def chkwinf90():
213  for arg in sys.argv:
214    if (arg.find('win32fe') >= 0 and (arg.find('f90') >=0 or arg.find('ifort') >=0)):
215      return 1
216  return 0
217
218def chkdosfiles():
219  # cygwin - but not a hg clone - so check one of files in bin dir
220  if b"\r\n" in open(os.path.join('lib','petsc','bin','petscmpiexec'),"rb").read():
221    print('===============================================================================')
222    print(' *** Scripts are in DOS mode. Was winzip used to extract petsc sources?    ****')
223    print(' *** Please restart with a fresh tarball and use "tar -xzf petsc.tar.gz"   ****')
224    print('===============================================================================')
225    sys.exit(3)
226  return
227
228def chkcygwinlink():
229  if os.path.exists('/usr/bin/cygcheck.exe') and os.path.exists('/usr/bin/link.exe') and chkwinf90():
230      if '--ignore-cygwin-link' in sys.argv: return 0
231      print('===============================================================================')
232      print(' *** Cygwin /usr/bin/link detected! Compiles with CVF/Intel f90 can break!  **')
233      print(' *** To workarround do: "mv /usr/bin/link.exe /usr/bin/link-cygwin.exe"     **')
234      print(' *** Or to ignore this check, use configure option: --ignore-cygwin-link    **')
235      print('===============================================================================')
236      sys.exit(3)
237  return 0
238
239def chkbrokencygwin():
240  if os.path.exists('/usr/bin/cygcheck.exe'):
241    buf = os.popen('/usr/bin/cygcheck.exe -c cygwin').read()
242    if buf.find('1.5.11-1') > -1:
243      print('===============================================================================')
244      print(' *** cygwin-1.5.11-1 detected. ./configure fails with this version ***')
245      print(' *** Please upgrade to cygwin-1.5.12-1 or newer version. This can  ***')
246      print(' *** be done by running cygwin-setup, selecting "next" all the way.***')
247      print('===============================================================================')
248      sys.exit(3)
249  return 0
250
251def chkusingwindowspython():
252  if sys.platform == 'win32':
253    print('===============================================================================')
254    print(' *** Windows python detected. Please rerun ./configure with cygwin-python. ***')
255    print('===============================================================================')
256    sys.exit(3)
257  return 0
258
259def chkcygwinpython():
260  if sys.platform == 'cygwin' :
261    import platform
262    import re
263    r=re.compile("([0-9]+).([0-9]+).([0-9]+)")
264    m=r.match(platform.release())
265    major=int(m.group(1))
266    minor=int(m.group(2))
267    subminor=int(m.group(3))
268    if ((major < 1) or (major == 1 and minor < 7) or (major == 1 and minor == 7 and subminor < 34)):
269      sys.argv.append('--useThreads=0')
270      extraLogs.append('''\
271===============================================================================
272** Cygwin version is older than 1.7.34. Python threads do not work correctly. ***
273** Disabling thread usage for this run of ./configure *******
274===============================================================================''')
275  return 0
276
277def chkrhl9():
278  if os.path.exists('/etc/redhat-release'):
279    try:
280      file = open('/etc/redhat-release','r')
281      buf = file.read()
282      file.close()
283    except:
284      # can't read file - assume dangerous RHL9
285      buf = 'Shrike'
286    if buf.find('Shrike') > -1:
287      sys.argv.append('--useThreads=0')
288      extraLogs.append('''\
289==============================================================================
290   *** RHL9 detected. Threads do not work correctly with this distribution ***
291   ****** Disabling thread usage for this run of ./configure *********
292===============================================================================''')
293  return 0
294
295def check_broken_configure_log_links():
296  '''Sometime symlinks can get broken if the original files are deleted. Delete such broken links'''
297  import os
298  for logfile in ['configure.log','configure.log.bkp']:
299    if os.path.islink(logfile) and not os.path.isfile(logfile): os.remove(logfile)
300  return
301
302def move_configure_log(framework):
303  '''Move configure.log to PETSC_ARCH/lib/petsc/conf - and update configure.log.bkp in both locations appropriately'''
304  global petsc_arch
305
306  if hasattr(framework,'arch'): petsc_arch = framework.arch
307  if hasattr(framework,'logName'): curr_file = framework.logName
308  else: curr_file = 'configure.log'
309
310  if petsc_arch:
311    import shutil
312    import os
313
314    # Just in case - confdir is not created
315    lib_dir = os.path.join(petsc_arch,'lib')
316    conf_dir = os.path.join(petsc_arch,'lib','petsc','conf')
317    if not os.path.isdir(petsc_arch): os.mkdir(petsc_arch)
318    if not os.path.isdir(lib_dir): os.mkdir(lib_dir)
319    if not os.path.isdir(conf_dir): os.mkdir(conf_dir)
320
321    curr_bkp  = curr_file + '.bkp'
322    new_file  = os.path.join(conf_dir,curr_file)
323    new_bkp   = new_file + '.bkp'
324
325    # Keep backup in $PETSC_ARCH/lib/petsc/conf location
326    if os.path.isfile(new_bkp): os.remove(new_bkp)
327    if os.path.isfile(new_file): os.rename(new_file,new_bkp)
328    if os.path.isfile(curr_file):
329      shutil.copyfile(curr_file,new_file)
330      os.remove(curr_file)
331    if os.path.isfile(new_file): os.symlink(new_file,curr_file)
332    # If the old bkp is using the same PETSC_ARCH/lib/petsc/conf - then update bkp link
333    if os.path.realpath(curr_bkp) == os.path.realpath(new_file):
334      if os.path.isfile(curr_bkp): os.remove(curr_bkp)
335      if os.path.isfile(new_bkp): os.symlink(new_bkp,curr_bkp)
336  return
337
338def print_final_timestamp(framework):
339  import time
340  framework.log.write(('='*80)+'\n')
341  framework.log.write('Finishing configure run at '+time.strftime('%a, %d %b %Y %H:%M:%S %z')+'\n')
342  framework.log.write(('='*80)+'\n')
343  return
344
345def petsc_configure(configure_options):
346  if 'PETSC_DIR' in os.environ:
347    petscdir = os.environ['PETSC_DIR']
348    if petscdir.find(' ') > -1:
349      raise RuntimeError('Your PETSC_DIR '+petscdir+' has spaces in it; this is not allowed.\n Change the directory with PETSc to not have spaces in it')
350    try:
351      sys.path.append(os.path.join(petscdir,'lib','petsc','bin'))
352      import petscnagupgrade
353      file     = os.path.join(petscdir,'.nagged')
354      if not petscnagupgrade.naggedtoday(file):
355        petscnagupgrade.currentversion(petscdir)
356    except:
357      pass
358  print('===============================================================================')
359  print('             Configuring PETSc to compile on your system                       ')
360  print('===============================================================================')
361
362  try:
363    # Command line arguments take precedence (but don't destroy argv[0])
364    sys.argv = sys.argv[:1] + configure_options + sys.argv[1:]
365    check_for_option_mistakes(sys.argv)
366    check_for_option_changed(sys.argv)
367  except (TypeError, ValueError) as e:
368    emsg = str(e)
369    if not emsg.endswith('\n'): emsg = emsg+'\n'
370    msg ='*******************************************************************************\n'\
371    +'                ERROR in COMMAND LINE ARGUMENT to ./configure \n' \
372    +'-------------------------------------------------------------------------------\n'  \
373    +emsg+'*******************************************************************************\n'
374    sys.exit(msg)
375  # check PETSC_ARCH
376  check_for_unsupported_combinations(sys.argv)
377  check_petsc_arch(sys.argv)
378  check_broken_configure_log_links()
379
380  #rename '--enable-' to '--with-'
381  chkenable()
382  # support a few standard configure option types
383  chksynonyms()
384  # Check for broken cygwin
385  chkbrokencygwin()
386  # Disable threads on RHL9
387  chkrhl9()
388  # Make sure cygwin-python is used on windows
389  chkusingwindowspython()
390  # Threads don't work for cygwin & python...
391  chkcygwinpython()
392  chkcygwinlink()
393  chkdosfiles()
394
395  # Should be run from the toplevel
396  configDir = os.path.abspath('config')
397  bsDir     = os.path.join(configDir, 'BuildSystem')
398  if not os.path.isdir(configDir):
399    raise RuntimeError('Run configure from $PETSC_DIR, not '+os.path.abspath('.'))
400  sys.path.insert(0, bsDir)
401  sys.path.insert(0, configDir)
402  import config.base
403  import config.framework
404  import pickle
405
406  framework = None
407  try:
408    framework = config.framework.Framework(['--configModules=PETSc.Configure','--optionsModule=config.compilerOptions']+sys.argv[1:], loadArgDB = 0)
409    framework.setup()
410    framework.logPrint('\n'.join(extraLogs))
411    framework.configure(out = sys.stdout)
412    framework.storeSubstitutions(framework.argDB)
413    framework.argDB['configureCache'] = pickle.dumps(framework)
414    framework.printSummary()
415    framework.argDB.save(force = True)
416    framework.logClear()
417    print_final_timestamp(framework)
418    framework.closeLog()
419    try:
420      move_configure_log(framework)
421    except:
422      # perhaps print an error about unable to shuffle logs?
423      pass
424    return 0
425  except (RuntimeError, config.base.ConfigureSetupError) as e:
426    emsg = str(e)
427    if not emsg.endswith('\n'): emsg = emsg+'\n'
428    msg ='*******************************************************************************\n'\
429    +'         UNABLE to CONFIGURE with GIVEN OPTIONS    (see configure.log for details):\n' \
430    +'-------------------------------------------------------------------------------\n'  \
431    +emsg+'*******************************************************************************\n'
432    se = ''
433  except (TypeError, ValueError) as e:
434    emsg = str(e)
435    if not emsg.endswith('\n'): emsg = emsg+'\n'
436    msg ='*******************************************************************************\n'\
437    +'                ERROR in COMMAND LINE ARGUMENT to ./configure \n' \
438    +'-------------------------------------------------------------------------------\n'  \
439    +emsg+'*******************************************************************************\n'
440    se = ''
441  except ImportError as e :
442    emsg = str(e)
443    if not emsg.endswith('\n'): emsg = emsg+'\n'
444    msg ='*******************************************************************************\n'\
445    +'                     UNABLE to FIND MODULE for ./configure \n' \
446    +'-------------------------------------------------------------------------------\n'  \
447    +emsg+'*******************************************************************************\n'
448    se = ''
449  except OSError as e :
450    emsg = str(e)
451    if not emsg.endswith('\n'): emsg = emsg+'\n'
452    msg ='*******************************************************************************\n'\
453    +'                    UNABLE to EXECUTE BINARIES for ./configure \n' \
454    +'-------------------------------------------------------------------------------\n'  \
455    +emsg+'*******************************************************************************\n'
456    se = ''
457  except SystemExit as e:
458    if e.code is None or e.code == 0:
459      return
460    if e.code is 10:
461      sys.exit(10)
462    msg ='*******************************************************************************\n'\
463    +'         CONFIGURATION FAILURE  (Please send configure.log to petsc-maint@mcs.anl.gov)\n' \
464    +'*******************************************************************************\n'
465    se  = str(e)
466  except Exception as e:
467    msg ='*******************************************************************************\n'\
468    +'        CONFIGURATION CRASH  (Please send configure.log to petsc-maint@mcs.anl.gov)\n' \
469    +'*******************************************************************************\n'
470    se  = str(e)
471
472  print(msg)
473  if not framework is None:
474    framework.logClear()
475    if hasattr(framework, 'log'):
476      try:
477        if hasattr(framework,'compilerDefines'):
478          framework.log.write('**** Configure header '+framework.compilerDefines+' ****\n')
479          framework.outputHeader(framework.log)
480        if hasattr(framework,'compilerFixes'):
481          framework.log.write('**** C specific Configure header '+framework.compilerFixes+' ****\n')
482          framework.outputCHeader(framework.log)
483      except Exception as e:
484        framework.log.write('Problem writing headers to log: '+str(e))
485      import traceback
486      try:
487        framework.log.write(msg+se)
488        traceback.print_tb(sys.exc_info()[2], file = framework.log)
489        print_final_timestamp(framework)
490        if hasattr(framework,'log'): framework.log.close()
491        move_configure_log(framework)
492      except:
493        pass
494      sys.exit(1)
495  else:
496    print(se)
497    import traceback
498    traceback.print_tb(sys.exc_info()[2])
499  if hasattr(framework,'log'): framework.log.close()
500
501if __name__ == '__main__':
502  petsc_configure([])
503
504