xref: /petsc/config/configure.py (revision 49229798da634fe4ab5e4bc1ce52a64b97a8a11f)
1#!/usr/bin/env python
2import os, sys
3import commands
4# to load ~/.pythonrc.py before inserting correct BuildSystem to path
5import user
6extraLogs = []
7petsc_arch = ''
8
9import urllib
10import tarfile
11
12def untar(tar, path = '.', leading = ''):
13  if leading:
14    entries = [t.name for t in tar.getmembers()]
15    prefix = os.path.commonprefix(entries)
16    if prefix:
17      for tarinfo in tar.getmembers():
18        tail = tarinfo.name.split(prefix, 1)[1]
19        tarinfo.name = os.path.join(leading, tail)
20  for tarinfo in tar.getmembers():
21    tar.extract(tarinfo, path)
22  return
23
24def downloadPackage(url, filename, targetDirname):
25  '''Download the tarball for a package at url, save it as filename, and untar it into targetDirname'''
26  filename, headers = urllib.urlretrieve(url, filename)
27  tar = tarfile.open(filename, 'r:gz')
28  untar(tar, targetDirname, leading = filename.split('.')[0])
29  return
30
31def getBuildSystem(configDir,bsDir):
32  print '==============================================================================='
33  print '''++ Could not locate BuildSystem in %s.''' % configDir
34  (status,output) = commands.getstatusoutput('hg showconfig paths.default')
35  if status or not output:
36    print '++ Mercurial clone not found. Downloading it from http://petsc.cs.iit.edu/petsc/BuildSystem/archive/tip.tar.gz'
37    downloadPackage('http://petsc.cs.iit.edu/petsc/BuildSystem/archive/tip.tar.gz', 'BuildSystem.tar.gz', configDir)
38  else:
39    print '++ Mercurial clone found. URL : ' + output
40    if output.find("petsc.cs.iit.edu") >=0:
41      bsurl = output.replace('petsc-dev','BuildSystem').replace('releases/petsc-','releases/BuildSystem-')
42      print '++ Using: hg clone '+ bsurl +' '+ bsDir
43      (status,output) = commands.getstatusoutput('hg clone '+ bsurl +' '+ bsDir)
44      if status:
45        print '++ Unable to clone BuildSystem. Please clone manually'
46        print '==============================================================================='
47        sys.exit(3)
48    else:
49      print '++ Nonstandard parent URL. Cannot determine appropriate BuildSystem URL. Please clone appropriate BuildSystem'
50      print '==============================================================================='
51      sys.exit(3)
52  print '==============================================================================='
53  return
54
55
56# Use en_US as language so that BuildSystem parses compiler messages in english
57if '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'
58if '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'
59
60if not hasattr(sys, 'version_info') or not sys.version_info[0] == 2 or not sys.version_info[1] >= 3:
61  print '*** You must have Python2 version 2.3 or higher to run ./configure        *****'
62  print '*          Python is easy to install for end users or sys-admin.              *'
63  print '*                  http://www.python.org/download/                            *'
64  print '*                                                                             *'
65  print '*           You CANNOT configure PETSc without Python                         *'
66  print '*   http://www.mcs.anl.gov/petsc/petsc-as/documentation/installation.html     *'
67  print '*******************************************************************************'
68  sys.exit(4)
69
70if sys.platform == 'win32':
71  print '**** Windows python detected. ****'
72  print sys.version,'on',sys.platform
73  print ''
74  print '** You must use cygwin python, but not windows python with PETSc configure. ***'
75  sys.exit(4)
76
77def check_for_option_mistakes(opts):
78  for opt in opts[1:]:
79    name = opt.split('=')[0]
80    if name.find('_') >= 0:
81      exception = False
82      for exc in ['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']:
83        if name.find(exc) >= 0:
84          exception = True
85      if not exception:
86        raise ValueError('The option '+name+' should probably be '+name.replace('_', '-'));
87    if opt.find('=') >=0:
88      optval = opt.split('=')[1]
89      if optval == 'ifneeded':
90        raise ValueError('The option '+opt+' should probably be '+opt.replace('ifneeded', '1'));
91  return
92
93def check_petsc_arch(opts):
94  # If PETSC_ARCH not specified - use script name (if not configure.py)
95  global petsc_arch
96  found = 0
97  for name in opts:
98    if name.find('PETSC_ARCH=') >= 0:
99      petsc_arch=name.split('=')[1]
100      found = 1
101      break
102  # If not yet specified - use the filename of script
103  if not found:
104      filename = os.path.basename(sys.argv[0])
105      if not filename.startswith('configure') and not filename.startswith('reconfigure') and not filename.startswith('setup'):
106        petsc_arch=os.path.splitext(os.path.basename(sys.argv[0]))[0]
107        useName = 'PETSC_ARCH='+petsc_arch
108        opts.append(useName)
109  return 0
110
111def chkwinf90():
112  for arg in sys.argv:
113    if (arg.find('win32fe') >= 0 and (arg.find('f90') >=0 or arg.find('ifort') >=0)):
114      return 1
115  return 0
116
117def chkcygwinlink():
118  if os.path.exists('/usr/bin/cygcheck.exe') and os.path.exists('/usr/bin/link.exe') and chkwinf90():
119      if '--ignore-cygwin-link' in sys.argv: return 0
120      print '==============================================================================='
121      print ' *** Cygwin /usr/bin/link detected! Compiles with CVF/Intel f90 can break!  **'
122      print ' *** To workarround do: "mv /usr/bin/link.exe /usr/bin/link-cygwin.exe"     **'
123      print ' *** Or to ignore this check, use configure option: --ignore-cygwin-link    **'
124      print '==============================================================================='
125      sys.exit(3)
126  return 0
127
128def chkbrokencygwin():
129  if os.path.exists('/usr/bin/cygcheck.exe'):
130    buf = os.popen('/usr/bin/cygcheck.exe -c cygwin').read()
131    if buf.find('1.5.11-1') > -1:
132      print '==============================================================================='
133      print ' *** cygwin-1.5.11-1 detected. ./configure fails with this version ***'
134      print ' *** Please upgrade to cygwin-1.5.12-1 or newer version. This can  ***'
135      print ' *** be done by running cygwin-setup, selecting "next" all the way.***'
136      print '==============================================================================='
137      sys.exit(3)
138  return 0
139
140def chkcygwinpython():
141  if os.path.exists('/usr/bin/cygcheck.exe') and sys.platform == 'cygwin' :
142    sys.argv.append('--useThreads=0')
143    extraLogs.append('''\
144===============================================================================
145** Cygwin-python detected. Threads do not work correctly. ***
146** Disabling thread usage for this run of ./configure *******
147===============================================================================''')
148  return 0
149
150def chkrhl9():
151  if os.path.exists('/etc/redhat-release'):
152    try:
153      file = open('/etc/redhat-release','r')
154      buf = file.read()
155      file.close()
156    except:
157      # can't read file - assume dangerous RHL9
158      buf = 'Shrike'
159    if buf.find('Shrike') > -1:
160      sys.argv.append('--useThreads=0')
161      extraLogs.append('''\
162==============================================================================
163   *** RHL9 detected. Threads do not work correctly with this distribution ***
164   ****** Disabling thread usage for this run of ./configure *********
165===============================================================================''')
166  return 0
167
168def check_broken_configure_log_links():
169  '''Sometime symlinks can get broken if the original files are deleted. Delete such broken links'''
170  import os
171  for logfile in ['configure.log','configure.log.bkp']:
172    if os.path.islink(logfile) and not os.path.isfile(logfile): os.remove(logfile)
173  return
174
175def move_configure_log(framework):
176  '''Move configure.log to PETSC_ARCH/conf - and update configure.log.bkp in both locations appropriately'''
177  global petsc_arch
178
179  if hasattr(framework,'arch'): petsc_arch = framework.arch
180  if hasattr(framework,'logName'): curr_file = framework.logName
181  else: curr_file = 'configure.log'
182
183  if petsc_arch:
184    import shutil
185    import os
186
187    # Just in case - confdir is not created
188    conf_dir = os.path.join(petsc_arch,'conf')
189    if not os.path.isdir(petsc_arch): os.mkdir(petsc_arch)
190    if not os.path.isdir(conf_dir): os.mkdir(conf_dir)
191
192    curr_bkp  = curr_file + '.bkp'
193    new_file  = os.path.join(conf_dir,curr_file)
194    new_bkp   = new_file + '.bkp'
195
196    # Keep backup in $PETSC_ARCH/conf location
197    if os.path.isfile(new_bkp): os.remove(new_bkp)
198    if os.path.isfile(new_file): os.rename(new_file,new_bkp)
199    if os.path.isfile(curr_file):
200      shutil.copyfile(curr_file,new_file)
201      os.remove(curr_file)
202    if os.path.isfile(new_file): os.symlink(new_file,curr_file)
203    # If the old bkp is using the same PETSC_ARCH/conf - then update bkp link
204    if os.path.realpath(curr_bkp) == os.path.realpath(new_file):
205      if os.path.isfile(curr_bkp): os.remove(curr_bkp)
206      if os.path.isfile(new_bkp): os.symlink(new_bkp,curr_bkp)
207  return
208
209def petsc_configure(configure_options):
210  print '==============================================================================='
211  print '             Configuring PETSc to compile on your system                       '
212  print '==============================================================================='
213
214  try:
215    # Command line arguments take precedence (but don't destroy argv[0])
216    sys.argv = sys.argv[:1] + configure_options + sys.argv[1:]
217    check_for_option_mistakes(sys.argv)
218  except (TypeError, ValueError), e:
219    emsg = str(e)
220    if not emsg.endswith('\n'): emsg = emsg+'\n'
221    msg ='*******************************************************************************\n'\
222    +'                ERROR in COMMAND LINE ARGUMENT to ./configure \n' \
223    +'-------------------------------------------------------------------------------\n'  \
224    +emsg+'*******************************************************************************\n'
225    sys.exit(msg)
226  # check PETSC_ARCH
227  check_petsc_arch(sys.argv)
228  check_broken_configure_log_links()
229
230  # support a few standard configure option types
231  for l in range(0,len(sys.argv)):
232    name = sys.argv[l]
233    if name.find('enable-') >= 0:
234      if name.find('=') == -1:
235        sys.argv[l] = name.replace('enable-','with-')+'=1'
236      else:
237        head, tail = name.split('=', 1)
238        sys.argv[l] = head.replace('enable-','with-')+'='+tail
239    if name.find('disable-') >= 0:
240      if name.find('=') == -1:
241        sys.argv[l] = name.replace('disable-','with-')+'=0'
242      else:
243        head, tail = name.split('=', 1)
244        if tail == '1': tail = '0'
245        sys.argv[l] = head.replace('disable-','with-')+'='+tail
246    if name.find('without-') >= 0:
247      if name.find('=') == -1:
248        sys.argv[l] = name.replace('without-','with-')+'=0'
249      else:
250        head, tail = name.split('=', 1)
251        if tail == '1': tail = '0'
252        sys.argv[l] = head.replace('without-','with-')+'='+tail
253
254  # Check for broken cygwin
255  chkbrokencygwin()
256  # Disable threads on RHL9
257  chkrhl9()
258  # Threads don't work for cygwin & python...
259  chkcygwinpython()
260  chkcygwinlink()
261
262  # Should be run from the toplevel
263  configDir = os.path.abspath('config')
264  bsDir     = os.path.join(configDir, 'BuildSystem')
265  if not os.path.isdir(configDir):
266    raise RuntimeError('Run configure from $PETSC_DIR, not '+os.path.abspath('.'))
267  if not os.path.isdir(bsDir): getBuildSystem(configDir,bsDir)
268  sys.path.insert(0, bsDir)
269  sys.path.insert(0, configDir)
270  import config.base
271  import config.framework
272  import cPickle
273
274  framework = None
275  try:
276    framework = config.framework.Framework(['--configModules=PETSc.Configure','--optionsModule=PETSc.compilerOptions']+sys.argv[1:], loadArgDB = 0)
277    framework.setup()
278    framework.logPrint('\n'.join(extraLogs))
279    framework.configure(out = sys.stdout)
280    framework.storeSubstitutions(framework.argDB)
281    framework.argDB['configureCache'] = cPickle.dumps(framework)
282    import PETSc.packages
283    for i in framework.packages:
284      if hasattr(i,'postProcess'):
285        i.postProcess()
286    framework.printSummary()
287    framework.argDB.save(force = True)
288    framework.logClear()
289    framework.closeLog()
290    try:
291      move_configure_log(framework)
292    except:
293      # perhaps print an error about unable to shuffle logs?
294      pass
295    return 0
296  except (RuntimeError, config.base.ConfigureSetupError), e:
297    emsg = str(e)
298    if not emsg.endswith('\n'): emsg = emsg+'\n'
299    msg ='*******************************************************************************\n'\
300    +'         UNABLE to CONFIGURE with GIVEN OPTIONS    (see configure.log for details):\n' \
301    +'-------------------------------------------------------------------------------\n'  \
302    +emsg+'*******************************************************************************\n'
303    se = ''
304  except (TypeError, ValueError), e:
305    emsg = str(e)
306    if not emsg.endswith('\n'): emsg = emsg+'\n'
307    msg ='*******************************************************************************\n'\
308    +'                ERROR in COMMAND LINE ARGUMENT to ./configure \n' \
309    +'-------------------------------------------------------------------------------\n'  \
310    +emsg+'*******************************************************************************\n'
311    se = ''
312  except ImportError, e :
313    emsg = str(e)
314    if not emsg.endswith('\n'): emsg = emsg+'\n'
315    msg ='*******************************************************************************\n'\
316    +'                     UNABLE to FIND MODULE for ./configure \n' \
317    +'-------------------------------------------------------------------------------\n'  \
318    +emsg+'*******************************************************************************\n'
319    se = ''
320  except OSError, e :
321    emsg = str(e)
322    if not emsg.endswith('\n'): emsg = emsg+'\n'
323    msg ='*******************************************************************************\n'\
324    +'                    UNABLE to EXECUTE BINARIES for ./configure \n' \
325    +'-------------------------------------------------------------------------------\n'  \
326    +emsg+'*******************************************************************************\n'
327    se = ''
328  except SystemExit, e:
329    if e.code is None or e.code == 0:
330      return
331    msg ='*******************************************************************************\n'\
332    +'         CONFIGURATION FAILURE  (Please send configure.log to petsc-maint@mcs.anl.gov)\n' \
333    +'*******************************************************************************\n'
334    se  = str(e)
335  except Exception, e:
336    msg ='*******************************************************************************\n'\
337    +'        CONFIGURATION CRASH  (Please send configure.log to petsc-maint@mcs.anl.gov)\n' \
338    +'*******************************************************************************\n'
339    se  = str(e)
340
341  print msg
342  if not framework is None:
343    framework.logClear()
344    if hasattr(framework, 'log'):
345      import traceback
346      try:
347        framework.log.write(msg+se)
348        traceback.print_tb(sys.exc_info()[2], file = framework.log)
349        if hasattr(framework,'log'): framework.log.close()
350        move_configure_log(framework)
351      except:
352        pass
353      sys.exit(1)
354  else:
355    print se
356    import traceback
357    traceback.print_tb(sys.exc_info()[2])
358  if hasattr(framework,'log'): framework.log.close()
359
360if __name__ == '__main__':
361  petsc_configure([])
362
363