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

Last change on this file since 7940 was 7940, checked in by habili, 14 years ago

dll and so files are no longer deleted if compiled in different OS.

File size: 11.6 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 """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 'Attempting to compile %s failed - please try manually' %FN
296    except:
297      raise '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 'Attempting to link %s failed - please try manually' %root1     
317  except:
318    raise 'Could not link %s - please try manually' %root1
319   
320
321def can_use_C_extension(filename):
322    """Determine whether specified C-extension
323    can and should be used.
324    """
325
326    from os.path import splitext
327
328    root, ext = splitext(filename)
329   
330    C=False
331    try:
332        s = 'import %s' %root
333        exec(s)
334    except:
335        try:
336            open(filename)
337        except:
338            msg = 'C extension %s cannot be opened' %filename
339            print msg               
340        else:   
341            print '------- Trying to compile c-extension %s' %filename
342       
343            try:
344                compile(filename)
345            except:
346                print 'WARNING: Could not compile C-extension %s'\
347                      %filename
348            else:
349                try:
350                    exec('import %s' %root)
351                except:
352                    msg = 'C extension %s seems to compile OK, '
353                    msg += 'but it can still not be imported.'
354                    raise msg
355                else:
356                    C=True
357    else:
358        C=True
359           
360    if not C:
361        pass
362        print 'NOTICE: C-extension %s not used' % filename
363
364    return C
365
366
367def set_python_dll_path():
368  """ On windows, find which of the two usual hiding places the python
369  dll is located.
370
371  If the file can't be found, return None.
372  """
373  import sys
374  from os import access, F_OK
375 
376  version = sys.version[:3]
377  v = version.replace('.','')
378  dllfilename = 'python%s.dll' %(v)
379  libs = os.path.join(sys.exec_prefix,dllfilename)
380
381  is_found = True   
382  if access(libs,F_OK) == 0 :
383    # Sometimes python dll is hidden in %WINDIR%/system32
384    libs = os.path.join(os.environ.get('WINDIR', 'C:\WINNT'), 'system32',
385                        dllfilename)
386    if access(libs,F_OK) == 0 :
387      # could not find the dll
388      libs = os.path.join(sys.exec_prefix,dllfilename)
389      is_found = False
390  return libs, is_found
391
392def check_python_dll():
393  libs, is_found = set_python_dll_path()
394  if not is_found:
395    print "%s not found.\nPlease install.\nIt is available on the web." \
396              %(libs)
397    import sys; sys.exit()
398   
399     
400if __name__ == '__main__':
401  from os.path import splitext
402 
403  if sys.platform == 'win32':
404    check_python_dll()
405  if len(sys.argv) > 1:
406      files = sys.argv[1:]
407      for filename in files:
408          root, ext = splitext(filename)
409
410          if ext <> '.c':
411              print 'WARNING (compile.py): Skipping %s. I only compile C-files.' %filename
412     
413  else: 
414      #path = os.path.split(sys.argv[0])[0] or os.getcwd()
415      path = '.'
416      files = os.listdir(path)
417     
418     
419
420  for filename in files:
421      root, ext = splitext(filename)
422
423      if ext == '.c':
424          #for x in ['.dll', '.so']:
425          #    try:
426          #        os.remove(root + x)
427          #    except:
428          #        pass
429
430          print separation_line
431          print 'Trying to compile c-extension %s in %s'\
432                %(filename, os.getcwd())
433          try:
434            if filename == 'triang.c': 
435              compile(['triang.c','triangle.c'])
436            elif  filename == 'mesh_engine_c_layer.c': 
437              compile(['mesh_engine_c_layer.c','triangle.c'])
438            else:
439              compile(filename)
440          except Exception, e:
441              msg = 'Could not compile C extension %s\n' %filename
442              msg += str(e)
443              raise Exception, msg
444          else:
445              print 'C extension %s OK' %filename
446          print   
447       
448
Note: See TracBrowser for help on using the repository browser.