source: trunk/anuga_core/source/anuga/utilities/compile.py @ 8124

Last change on this file since 8124 was 8124, checked in by wilsonr, 14 years ago

Made changes described in ticket 359.

File size: 11.7 KB
Line 
1"""compile.py - compile Python C-extension
2
3   Commandline usage:
4     python compile.py <filename>
5
6   Usage from within Python:
7     import compile
8     compile.compile(<filename>,..)
9
10   Ole Nielsen, Duncan Gray Oct 2001     
11"""     
12
13#NumPy ------------------------------------
14# Something like these lines recommended in "Converting from NUMARRAY to NUMPY"
15import numpy
16I_dirs = '-I"%s" ' % numpy.get_include()
17#NumPy ------------------------------------
18
19# FIXME (Ole): Although this script says it works with a range of compilers,
20# it has only really been used with gcc.
21
22import os, string, sys, types
23
24separation_line = '---------------------------------------'     
25 
26def compile(FNs=None, CC=None, LD = None, SFLAG = None, verbose = 1):
27  """compile(FNs=None, CC=None, LD = None, SFLAG = None):
28 
29     Compile FN(s) using compiler CC (e.g. mpicc),
30     Loader LD and shared flag SFLAG.
31     If CC is absent use default compiler dependent on platform
32     if LD is absent CC is used.
33     if SFLAG is absent platform default is used
34     FNs can be either one filename or a list of filenames
35     In the latter case, the first will be used to name so file.
36  """
37 
38  # Input check
39  #
40  assert not FNs is None, 'No filename provided'
41
42  if not type(FNs) == types.ListType:
43    FNs = [FNs]
44
45
46  libext = 'so' #Default extension (Unix)
47  libs = ''
48  version = sys.version[:3]
49 
50  # Determine platform and compiler
51  #
52  if sys.platform == 'sunos5':  #Solaris
53    if CC:
54      compiler = CC
55    else: 
56      compiler = 'gcc'
57    if LD:
58      loader = LD
59    else: 
60      loader = compiler
61    if SFLAG:
62      sharedflag = SFLAG
63    else: 
64      sharedflag = 'G'
65     
66  elif sys.platform == 'osf1V5':  #Compaq AlphaServer
67    if CC:
68      compiler = CC
69    else: 
70      compiler = 'cc'
71    if LD:
72      loader = LD
73    else: 
74      loader = compiler
75    if SFLAG:
76      sharedflag = SFLAG
77    else: 
78      sharedflag = 'shared'   
79     
80  elif sys.platform == 'linux2':  #Linux
81    if CC:
82      compiler = CC
83    else: 
84      compiler = 'gcc'
85    if LD:
86      loader = LD
87    else: 
88      loader = compiler
89    if SFLAG:
90      sharedflag = SFLAG
91    else: 
92      sharedflag = 'shared'   
93     
94  elif sys.platform == 'darwin':  #Mac OS X:
95    if CC:
96      compiler = CC
97    else: 
98      compiler = 'cc'
99    if LD:
100      loader = LD
101    else: 
102      loader = compiler
103    if SFLAG:
104      sharedflag = SFLAG
105    else: 
106      sharedflag = 'bundle -flat_namespace -undefined suppress'
107
108  elif sys.platform == 'cygwin':  #Cygwin (compilation same as linux)
109    if CC:
110      compiler = CC
111    else: 
112      compiler = 'gcc'
113    if LD:
114      loader = LD
115    else: 
116      loader = compiler
117    if SFLAG:
118      sharedflag = SFLAG
119    else: 
120      sharedflag = 'shared'
121    libext = 'dll'
122    libs = '/lib/python%s/config/libpython%s.dll.a' %(version,version)
123     
124  elif sys.platform == 'win32':  #Windows
125    if CC:
126      compiler = CC
127    else: 
128      compiler = 'gcc.exe' #Some systems require this (a security measure?)
129    if LD:
130      loader = LD
131    else: 
132      loader = compiler
133    if SFLAG:
134      sharedflag = SFLAG
135    else: 
136      sharedflag = 'shared'
137     
138    # As of python2.5, .pyd is the extension for python extension
139    # modules.
140    if sys.version_info[0:2] >= (2, 5):
141      libext = 'pyd'
142    else:
143      libext = 'dll'
144
145    libs, is_found = set_python_dll_path()
146     
147  else:
148    if verbose: print "Unrecognised platform %s - revert to default"\
149                %sys.platform
150   
151    if CC:
152      compiler = CC
153    else: 
154      compiler = 'cc'
155    if LD:
156      loader = LD
157    else: 
158      loader = 'ld'
159    if SFLAG:
160      sharedflag = SFLAG
161    else: 
162      sharedflag = 'G'
163
164
165  # Verify that compiler can be executed
166  print 'Compiler: %s, version ' %compiler,
167  sys.stdout.flush()
168  s = '%s -dumpversion' %(compiler)
169  err = os.system(s)
170  print
171 
172  if err != 0:
173      msg = 'Unable to execute compiler: %s. ' %compiler
174      msg += 'Make sure it is available on the system path.\n'
175      msg += 'One way to check this is to run %s on ' %compiler
176      msg += 'the commandline.'
177      raise Exception(msg)
178
179 
180  # Find location of include files
181  #
182  if sys.platform == 'win32':  #Windows
183    python_include = os.path.join(sys.exec_prefix, 'include')   
184  else: 
185    python_include = os.path.join(os.path.join(sys.exec_prefix, 'include'),
186                                  'python' + version)
187
188  # Check existence of Python.h
189  #
190  headerfile = python_include + os.sep + 'Python.h'
191  try:
192    open(headerfile, 'r')
193  except:
194    raise Exception("""Did not find Python header file %s.
195    Make sure files for Python C-extensions are installed.
196    In debian linux, for example, you need to install a
197    package called something like python2.3-dev""" %headerfile)
198
199
200
201  # Add Python path + utilities to includelist (see ticket:31)
202  # Assume there is only one 'utilities' dir under path dirs
203 
204  utilities_include_dir = None
205  for pathdir in sys.path:
206
207    utilities_include_dir = pathdir + os.sep + 'utilities'
208    #print pathdir
209    #print utilities_include_dir
210    try:
211      os.stat(utilities_include_dir)
212    except OSError:
213      pass
214    else:
215      #print 'Found %s to be used as include dir' %utilities_include_dir
216      break
217
218  # This is hacky since it
219  # assumes the location of the compile_all that determines buildroot
220  try:
221    utilities_include_dir = buildroot + os.sep + "source" + os.sep + "anuga" \
222                            + os.sep + 'utilities'
223  except:
224    # This will make compile work locally
225    utilities_include_dir = '.'
226    utilities_include_dir = '../utilities'
227
228
229   
230  try:
231    os.stat(utilities_include_dir)
232  except OSError: 
233    utilities_include_dir = buildroot + os.sep + 'utilities'
234 
235 
236 
237  # Check filename(s)
238  #
239  object_files = ''
240  for FN in FNs:       
241    root, ext = os.path.splitext(FN)
242    if ext == '':
243      FN = FN + '.c'
244    elif ext.lower() != '.c':
245      raise Exception("Unrecognised extension: " + FN)
246   
247    try:
248      open(FN, 'r')
249    except:
250      raise Exception("Could not open: " + FN)
251
252    if not object_files: root1 = root  #Remember first filename       
253    object_files += root + '.o ' 
254 
255 
256    # Compile
257    #
258    if utilities_include_dir is None:   
259      s = '%s -c %s -I"%s" -o "%s.o" -Wall -O3'\
260          %(compiler, FN, python_include, root)
261    else:
262      if FN == "triangle.c" or FN == "mesh_engine_c_layer.c":
263        s = '%s -c %s %s -I"%s" -I"%s" -o "%s.o" -O3 -DTRILIBRARY=1 -DNO_TIMER=1'\
264            % (compiler, FN, I_dirs, python_include, utilities_include_dir, root)
265      elif FN == "polygon_ext.c":
266        # gcc 4.3.x is problematic with this file if '-O3' is used
267        s = '%s -c %s %s -I"%s" -I"%s" -o "%s.o" -Wall'\
268            %(compiler, FN, I_dirs, python_include, utilities_include_dir, root)
269      else:
270        s = '%s -c %s %s -I"%s" -I"%s" -o "%s.o" -Wall -O3'\
271            %(compiler, FN, I_dirs, python_include, utilities_include_dir, root)
272
273    if os.name == 'posix' and os.uname()[4] in ['x86_64', 'ia64']:
274      # Extra flags for 64 bit architectures.
275      #   AMD Opteron will give x86_64
276      #   Itanium will give ia64
277      #
278      # Second clause will always fail on Win32 because uname is UNIX specific
279      # but won't get past first clause
280
281      s += ' -fPIC' # Use position independent code for 64 bit archs
282      #s += ' -m64' # Used to be necessary for AMD Opteron
283     
284     
285    if verbose:
286      print s
287
288    # Doesn't work on Windows anyway 
289    #else:
290    #  s = s + ' 2> /dev/null' #Suppress errors
291   
292    try:
293      err = os.system(s)
294      if err != 0:
295          raise Exception('Attempting to compile %s failed - please try manually' %FN)
296    except:
297      raise Exception('Could not compile %s - please try manually' %FN)
298
299 
300  # Make shared library (*.so or *.dll)
301  if libs is "":
302    s = '%s -%s %s -o %s.%s -lm' %(loader, sharedflag, object_files, root1, libext)
303  else:
304    s = '%s -%s %s -o %s.%s "%s" -lm' %(loader, sharedflag, object_files, root1, libext, libs)
305   
306  if verbose:
307    print s
308
309  # Doesn't work on Windows anyway     
310  #else:
311  #  s = s + ' 2> /dev/null' #Suppress warnings
312 
313  try: 
314    err=os.system(s)
315    if err != 0:       
316        raise Exception('Attempting to link %s failed - please try manually'
317                        % root1)
318  except:
319    raise Exception('Could not link %s - please try manually' % root1)
320   
321
322def can_use_C_extension(filename):
323    """Determine whether specified C-extension
324    can and should be used.
325    """
326
327    from os.path import splitext
328
329    root, ext = splitext(filename)
330   
331    C=False
332    try:
333        s = 'import %s' %root
334        exec(s)
335    except:
336        try:
337            open(filename)
338        except:
339            msg = 'C extension %s cannot be opened' %filename
340            print msg               
341        else:   
342            print '------- Trying to compile c-extension %s' %filename
343       
344            try:
345                compile(filename)
346            except:
347                print 'WARNING: Could not compile C-extension %s'\
348                      %filename
349            else:
350                try:
351                    exec('import %s' %root)
352                except:
353                    msg = 'C extension %s seems to compile OK, '
354                    msg += 'but it can still not be imported.'
355                    raise Exception(msg)
356                else:
357                    C=True
358    else:
359        C=True
360           
361    if not C:
362        pass
363        print 'NOTICE: C-extension %s not used' % filename
364
365    return C
366
367
368def set_python_dll_path():
369  """ On windows, find which of the two usual hiding places the python
370  dll is located.
371
372  If the file can't be found, return None.
373  """
374  import sys
375  from os import access, F_OK
376 
377  version = sys.version[:3]
378  v = version.replace('.','')
379  dllfilename = 'python%s.dll' %(v)
380  libs = os.path.join(sys.exec_prefix,dllfilename)
381
382  is_found = True   
383  if access(libs,F_OK) == 0 :
384    # Sometimes python dll is hidden in %WINDIR%/system32
385    libs = os.path.join(os.environ.get('WINDIR', 'C:\WINNT'), 'system32',
386                        dllfilename)
387    if access(libs,F_OK) == 0 :
388      # could not find the dll
389      libs = os.path.join(sys.exec_prefix,dllfilename)
390      is_found = False
391  return libs, is_found
392
393def check_python_dll():
394  libs, is_found = set_python_dll_path()
395  if not is_found:
396    print "%s not found.\nPlease install.\nIt is available on the web." \
397              %(libs)
398    import sys; sys.exit()
399   
400     
401if __name__ == '__main__':
402  from os.path import splitext
403 
404  if sys.platform == 'win32':
405    check_python_dll()
406  if len(sys.argv) > 1:
407      files = sys.argv[1:]
408      for filename in files:
409          root, ext = splitext(filename)
410
411          if ext <> '.c':
412              print 'WARNING (compile.py): Skipping %s. I only compile C-files.' %filename
413     
414  else: 
415      #path = os.path.split(sys.argv[0])[0] or os.getcwd()
416      path = '.'
417      files = os.listdir(path)
418     
419     
420
421  for filename in files:
422      root, ext = splitext(filename)
423
424      if ext == '.c':
425          #for x in ['.dll', '.so']:
426          #    try:
427          #        os.remove(root + x)
428          #    except:
429          #        pass
430
431          print separation_line
432          print 'Trying to compile c-extension %s in %s'\
433                %(filename, os.getcwd())
434          try:
435            if filename == 'triang.c': 
436              compile(['triang.c','triangle.c'])
437            elif  filename == 'mesh_engine_c_layer.c': 
438              compile(['mesh_engine_c_layer.c','triangle.c'])
439            else:
440              compile(filename)
441          except Exception, e:
442              msg = 'Could not compile C extension %s\n' %filename
443              msg += str(e)
444              raise Exception(msg)
445          else:
446              print 'C extension %s OK' %filename
447          print   
448       
449
Note: See TracBrowser for help on using the repository browser.