Line 0
Link Here
|
|
|
1 |
"""distutils.cygwinccompiler |
2 |
|
3 |
Provides the CygwinCCompiler class, a subclass of UnixCCompiler that |
4 |
handles the Cygwin port of the GNU C compiler to Windows. It also contains |
5 |
the Mingw32CCompiler class which handles the mingw32 port of GCC (same as |
6 |
cygwin in no-cygwin mode). |
7 |
""" |
8 |
|
9 |
# problems: |
10 |
# |
11 |
# * if you use a msvc compiled python version (1.5.2) |
12 |
# 1. you have to insert a __GNUC__ section in its config.h |
13 |
# 2. you have to generate an import library for its dll |
14 |
# - create a def-file for python??.dll |
15 |
# - create an import library using |
16 |
# dlltool --dllname python15.dll --def python15.def \ |
17 |
# --output-lib libpython15.a |
18 |
# |
19 |
# see also http://starship.python.net/crew/kernr/mingw32/Notes.html |
20 |
# |
21 |
# * We put export_symbols in a def-file, and don't use |
22 |
# --export-all-symbols because it doesn't worked reliable in some |
23 |
# tested configurations. And because other windows compilers also |
24 |
# need their symbols specified this no serious problem. |
25 |
# |
26 |
# tested configurations: |
27 |
# |
28 |
# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works |
29 |
# (after patching python's config.h and for C++ some other include files) |
30 |
# see also http://starship.python.net/crew/kernr/mingw32/Notes.html |
31 |
# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works |
32 |
# (ld doesn't support -shared, so we use dllwrap) |
33 |
# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now |
34 |
# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 |
35 |
# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html |
36 |
# - using gcc -mdll instead dllwrap doesn't work without -static because |
37 |
# it tries to link against dlls instead their import libraries. (If |
38 |
# it finds the dll first.) |
39 |
# By specifying -static we force ld to link against the import libraries, |
40 |
# this is windows standard and there are normally not the necessary symbols |
41 |
# in the dlls. |
42 |
# *** only the version of June 2000 shows these problems |
43 |
# * cygwin gcc 3.2/ld 2.13.90 works |
44 |
# (ld supports -shared) |
45 |
# * mingw gcc 3.2/ld 2.13 works |
46 |
# (ld supports -shared) |
47 |
|
48 |
# This module should be kept compatible with Python 2.1. |
49 |
|
50 |
__revision__ = "$Id$" |
51 |
|
52 |
import os,sys,copy |
53 |
from distutils.ccompiler import gen_preprocess_options, gen_lib_options |
54 |
from distutils.unixccompiler import UnixCCompiler |
55 |
from distutils.file_util import write_file |
56 |
from distutils.errors import DistutilsExecError, CompileError, UnknownFileError |
57 |
from distutils import log |
58 |
|
59 |
def get_msvcr(): |
60 |
"""Include the appropriate MSVC runtime library if Python was built |
61 |
with MSVC 7.0 or later. |
62 |
""" |
63 |
msc_pos = sys.version.find('MSC v.') |
64 |
if msc_pos != -1: |
65 |
msc_ver = sys.version[msc_pos+6:msc_pos+10] |
66 |
if msc_ver == '1300': |
67 |
# MSVC 7.0 |
68 |
return ['msvcr70'] |
69 |
elif msc_ver == '1310': |
70 |
# MSVC 7.1 |
71 |
return ['msvcr71'] |
72 |
elif msc_ver == '1400': |
73 |
# VS2005 / MSVC 8.0 |
74 |
return ['msvcr80'] |
75 |
elif msc_ver == '1500': |
76 |
# VS2008 / MSVC 9.0 |
77 |
return ['msvcr90'] |
78 |
elif msc_ver == '1600': |
79 |
# VS2010 / MSVC 10.0 |
80 |
return ['msvcr100'] |
81 |
else: |
82 |
raise ValueError("Unknown MS Compiler version %s " % msc_ver) |
83 |
|
84 |
|
85 |
class CygwinCCompiler (UnixCCompiler): |
86 |
|
87 |
compiler_type = 'cygwin' |
88 |
obj_extension = ".o" |
89 |
static_lib_extension = ".a" |
90 |
shared_lib_extension = ".dll" |
91 |
static_lib_format = "lib%s%s" |
92 |
shared_lib_format = "%s%s" |
93 |
exe_extension = ".exe" |
94 |
|
95 |
def __init__ (self, verbose=0, dry_run=0, force=0): |
96 |
|
97 |
UnixCCompiler.__init__ (self, verbose, dry_run, force) |
98 |
|
99 |
(status, details) = check_config_h() |
100 |
self.debug_print("Python's GCC status: %s (details: %s)" % |
101 |
(status, details)) |
102 |
if status is not CONFIG_H_OK: |
103 |
self.warn( |
104 |
"Python's pyconfig.h doesn't seem to support your compiler. " |
105 |
"Reason: %s. " |
106 |
"Compiling may fail because of undefined preprocessor macros." |
107 |
% details) |
108 |
|
109 |
self.gcc_version, self.ld_version, self.dllwrap_version = \ |
110 |
get_versions() |
111 |
self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % |
112 |
(self.gcc_version, |
113 |
self.ld_version, |
114 |
self.dllwrap_version) ) |
115 |
|
116 |
# ld_version >= "2.10.90" and < "2.13" should also be able to use |
117 |
# gcc -mdll instead of dllwrap |
118 |
# Older dllwraps had own version numbers, newer ones use the |
119 |
# same as the rest of binutils ( also ld ) |
120 |
# dllwrap 2.10.90 is buggy |
121 |
if self.ld_version >= "2.10.90": |
122 |
self.linker_dll = "gcc" |
123 |
else: |
124 |
self.linker_dll = "dllwrap" |
125 |
|
126 |
# ld_version >= "2.13" support -shared so use it instead of |
127 |
# -mdll -static |
128 |
if self.ld_version >= "2.13": |
129 |
shared_option = "-shared" |
130 |
else: |
131 |
shared_option = "-mdll -static" |
132 |
|
133 |
# Hard-code GCC because that's what this is all about. |
134 |
# XXX optimization, warnings etc. should be customizable. |
135 |
self.set_executables(compiler='gcc -mcygwin -O -Wall', |
136 |
compiler_so='gcc -mcygwin -mdll -O -Wall', |
137 |
compiler_cxx='g++ -mcygwin -O -Wall', |
138 |
linker_exe='gcc -mcygwin', |
139 |
linker_so=('%s -mcygwin %s' % |
140 |
(self.linker_dll, shared_option))) |
141 |
|
142 |
# cygwin and mingw32 need different sets of libraries |
143 |
if self.gcc_version == "2.91.57": |
144 |
# cygwin shouldn't need msvcrt, but without the dlls will crash |
145 |
# (gcc version 2.91.57) -- perhaps something about initialization |
146 |
self.dll_libraries=["msvcrt"] |
147 |
self.warn( |
148 |
"Consider upgrading to a newer version of gcc") |
149 |
else: |
150 |
# Include the appropriate MSVC runtime library if Python was built |
151 |
# with MSVC 7.0 or later. |
152 |
self.dll_libraries = get_msvcr() |
153 |
|
154 |
# __init__ () |
155 |
|
156 |
|
157 |
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): |
158 |
if ext == '.rc' or ext == '.res': |
159 |
# gcc needs '.res' and '.rc' compiled to object files !!! |
160 |
try: |
161 |
self.spawn(["windres", "-i", src, "-o", obj]) |
162 |
except DistutilsExecError, msg: |
163 |
raise CompileError, msg |
164 |
else: # for other files use the C-compiler |
165 |
try: |
166 |
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + |
167 |
extra_postargs) |
168 |
except DistutilsExecError, msg: |
169 |
raise CompileError, msg |
170 |
|
171 |
def link (self, |
172 |
target_desc, |
173 |
objects, |
174 |
output_filename, |
175 |
output_dir=None, |
176 |
libraries=None, |
177 |
library_dirs=None, |
178 |
runtime_library_dirs=None, |
179 |
export_symbols=None, |
180 |
debug=0, |
181 |
extra_preargs=None, |
182 |
extra_postargs=None, |
183 |
build_temp=None, |
184 |
target_lang=None): |
185 |
|
186 |
# use separate copies, so we can modify the lists |
187 |
extra_preargs = copy.copy(extra_preargs or []) |
188 |
libraries = copy.copy(libraries or []) |
189 |
objects = copy.copy(objects or []) |
190 |
|
191 |
# Additional libraries |
192 |
libraries.extend(self.dll_libraries) |
193 |
|
194 |
# handle export symbols by creating a def-file |
195 |
# with executables this only works with gcc/ld as linker |
196 |
if ((export_symbols is not None) and |
197 |
(target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): |
198 |
# (The linker doesn't do anything if output is up-to-date. |
199 |
# So it would probably better to check if we really need this, |
200 |
# but for this we had to insert some unchanged parts of |
201 |
# UnixCCompiler, and this is not what we want.) |
202 |
|
203 |
# we want to put some files in the same directory as the |
204 |
# object files are, build_temp doesn't help much |
205 |
# where are the object files |
206 |
temp_dir = os.path.dirname(objects[0]) |
207 |
# name of dll to give the helper files the same base name |
208 |
(dll_name, dll_extension) = os.path.splitext( |
209 |
os.path.basename(output_filename)) |
210 |
|
211 |
# generate the filenames for these files |
212 |
def_file = os.path.join(temp_dir, dll_name + ".def") |
213 |
lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") |
214 |
|
215 |
# Generate .def file |
216 |
contents = [ |
217 |
"LIBRARY %s" % os.path.basename(output_filename), |
218 |
"EXPORTS"] |
219 |
for sym in export_symbols: |
220 |
contents.append(sym) |
221 |
self.execute(write_file, (def_file, contents), |
222 |
"writing %s" % def_file) |
223 |
|
224 |
# next add options for def-file and to creating import libraries |
225 |
|
226 |
# dllwrap uses different options than gcc/ld |
227 |
if self.linker_dll == "dllwrap": |
228 |
extra_preargs.extend(["--output-lib", lib_file]) |
229 |
# for dllwrap we have to use a special option |
230 |
extra_preargs.extend(["--def", def_file]) |
231 |
# we use gcc/ld here and can be sure ld is >= 2.9.10 |
232 |
else: |
233 |
# doesn't work: bfd_close build\...\libfoo.a: Invalid operation |
234 |
#extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) |
235 |
# for gcc/ld the def-file is specified as any object files |
236 |
objects.append(def_file) |
237 |
|
238 |
#end: if ((export_symbols is not None) and |
239 |
# (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): |
240 |
|
241 |
# who wants symbols and a many times larger output file |
242 |
# should explicitly switch the debug mode on |
243 |
# otherwise we let dllwrap/ld strip the output file |
244 |
# (On my machine: 10KB < stripped_file < ??100KB |
245 |
# unstripped_file = stripped_file + XXX KB |
246 |
# ( XXX=254 for a typical python extension)) |
247 |
if not debug: |
248 |
extra_preargs.append("-s") |
249 |
|
250 |
UnixCCompiler.link(self, |
251 |
target_desc, |
252 |
objects, |
253 |
output_filename, |
254 |
output_dir, |
255 |
libraries, |
256 |
library_dirs, |
257 |
runtime_library_dirs, |
258 |
None, # export_symbols, we do this in our def-file |
259 |
debug, |
260 |
extra_preargs, |
261 |
extra_postargs, |
262 |
build_temp, |
263 |
target_lang) |
264 |
|
265 |
# link () |
266 |
|
267 |
# -- Miscellaneous methods ----------------------------------------- |
268 |
|
269 |
# overwrite the one from CCompiler to support rc and res-files |
270 |
def object_filenames (self, |
271 |
source_filenames, |
272 |
strip_dir=0, |
273 |
output_dir=''): |
274 |
if output_dir is None: output_dir = '' |
275 |
obj_names = [] |
276 |
for src_name in source_filenames: |
277 |
# use normcase to make sure '.rc' is really '.rc' and not '.RC' |
278 |
(base, ext) = os.path.splitext (os.path.normcase(src_name)) |
279 |
if ext not in (self.src_extensions + ['.rc','.res']): |
280 |
raise UnknownFileError, \ |
281 |
"unknown file type '%s' (from '%s')" % \ |
282 |
(ext, src_name) |
283 |
if strip_dir: |
284 |
base = os.path.basename (base) |
285 |
if ext == '.res' or ext == '.rc': |
286 |
# these need to be compiled to object files |
287 |
obj_names.append (os.path.join (output_dir, |
288 |
base + ext + self.obj_extension)) |
289 |
else: |
290 |
obj_names.append (os.path.join (output_dir, |
291 |
base + self.obj_extension)) |
292 |
return obj_names |
293 |
|
294 |
# object_filenames () |
295 |
|
296 |
# class CygwinCCompiler |
297 |
|
298 |
|
299 |
# the same as cygwin plus some additional parameters |
300 |
class Mingw32CCompiler (CygwinCCompiler): |
301 |
|
302 |
compiler_type = 'mingw32' |
303 |
|
304 |
def __init__ (self, |
305 |
verbose=0, |
306 |
dry_run=0, |
307 |
force=0): |
308 |
|
309 |
CygwinCCompiler.__init__ (self, verbose, dry_run, force) |
310 |
|
311 |
# ld_version >= "2.13" support -shared so use it instead of |
312 |
# -mdll -static |
313 |
if self.ld_version >= "2.13": |
314 |
shared_option = "-shared" |
315 |
else: |
316 |
shared_option = "-mdll -static" |
317 |
|
318 |
# A real mingw32 doesn't need to specify a different entry point, |
319 |
# but cygwin 2.91.57 in no-cygwin-mode needs it. |
320 |
if self.gcc_version <= "2.91.57": |
321 |
entry_point = '--entry _DllMain@12' |
322 |
else: |
323 |
entry_point = '' |
324 |
|
325 |
if self.gcc_version < '4' or is_cygwingcc(): |
326 |
no_cygwin = ' -mno-cygwin' |
327 |
else: |
328 |
no_cygwin = '' |
329 |
|
330 |
self.set_executables(compiler='gcc%s -O -Wall' % no_cygwin, |
331 |
compiler_so='gcc%s -mdll -O -Wall' % no_cygwin, |
332 |
compiler_cxx='g++%s -O -Wall' % no_cygwin, |
333 |
linker_exe='gcc%s' % no_cygwin, |
334 |
linker_so='%s%s %s %s' |
335 |
% (self.linker_dll, no_cygwin, |
336 |
shared_option, entry_point)) |
337 |
# Maybe we should also append -mthreads, but then the finished |
338 |
# dlls need another dll (mingwm10.dll see Mingw32 docs) |
339 |
# (-mthreads: Support thread-safe exception handling on `Mingw32') |
340 |
|
341 |
# no additional libraries needed |
342 |
self.dll_libraries=[] |
343 |
|
344 |
# Include the appropriate MSVC runtime library if Python was built |
345 |
# with MSVC 7.0 or later. |
346 |
self.dll_libraries = get_msvcr() |
347 |
|
348 |
# __init__ () |
349 |
|
350 |
# class Mingw32CCompiler |
351 |
|
352 |
# Because these compilers aren't configured in Python's pyconfig.h file by |
353 |
# default, we should at least warn the user if he is using a unmodified |
354 |
# version. |
355 |
|
356 |
CONFIG_H_OK = "ok" |
357 |
CONFIG_H_NOTOK = "not ok" |
358 |
CONFIG_H_UNCERTAIN = "uncertain" |
359 |
|
360 |
def check_config_h(): |
361 |
|
362 |
"""Check if the current Python installation (specifically, pyconfig.h) |
363 |
appears amenable to building extensions with GCC. Returns a tuple |
364 |
(status, details), where 'status' is one of the following constants: |
365 |
CONFIG_H_OK |
366 |
all is well, go ahead and compile |
367 |
CONFIG_H_NOTOK |
368 |
doesn't look good |
369 |
CONFIG_H_UNCERTAIN |
370 |
not sure -- unable to read pyconfig.h |
371 |
'details' is a human-readable string explaining the situation. |
372 |
|
373 |
Note there are two ways to conclude "OK": either 'sys.version' contains |
374 |
the string "GCC" (implying that this Python was built with GCC), or the |
375 |
installed "pyconfig.h" contains the string "__GNUC__". |
376 |
""" |
377 |
|
378 |
# XXX since this function also checks sys.version, it's not strictly a |
379 |
# "pyconfig.h" check -- should probably be renamed... |
380 |
|
381 |
from distutils import sysconfig |
382 |
import string |
383 |
# if sys.version contains GCC then python was compiled with |
384 |
# GCC, and the pyconfig.h file should be OK |
385 |
if string.find(sys.version,"GCC") >= 0: |
386 |
return (CONFIG_H_OK, "sys.version mentions 'GCC'") |
387 |
|
388 |
fn = sysconfig.get_config_h_filename() |
389 |
try: |
390 |
# It would probably better to read single lines to search. |
391 |
# But we do this only once, and it is fast enough |
392 |
f = open(fn) |
393 |
try: |
394 |
s = f.read() |
395 |
finally: |
396 |
f.close() |
397 |
|
398 |
except IOError, exc: |
399 |
# if we can't read this file, we cannot say it is wrong |
400 |
# the compiler will complain later about this file as missing |
401 |
return (CONFIG_H_UNCERTAIN, |
402 |
"couldn't read '%s': %s" % (fn, exc.strerror)) |
403 |
|
404 |
else: |
405 |
# "pyconfig.h" contains an "#ifdef __GNUC__" or something similar |
406 |
if string.find(s,"__GNUC__") >= 0: |
407 |
return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn) |
408 |
else: |
409 |
return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn) |
410 |
|
411 |
|
412 |
|
413 |
def get_versions(): |
414 |
""" Try to find out the versions of gcc, ld and dllwrap. |
415 |
If not possible it returns None for it. |
416 |
""" |
417 |
from distutils.version import LooseVersion |
418 |
from distutils.spawn import find_executable |
419 |
import re |
420 |
|
421 |
gcc_exe = find_executable('gcc') |
422 |
if gcc_exe: |
423 |
out = os.popen(gcc_exe + ' -dumpversion','r') |
424 |
out_string = out.read() |
425 |
out.close() |
426 |
result = re.search('(\d+\.\d+(\.\d+)*)',out_string) |
427 |
if result: |
428 |
gcc_version = LooseVersion(result.group(1)) |
429 |
else: |
430 |
gcc_version = None |
431 |
else: |
432 |
gcc_version = None |
433 |
ld_exe = find_executable('ld') |
434 |
if ld_exe: |
435 |
out = os.popen(ld_exe + ' -v','r') |
436 |
out_string = out.read() |
437 |
out.close() |
438 |
result = re.search('(\d+\.\d+(\.\d+)*)',out_string) |
439 |
if result: |
440 |
ld_version = LooseVersion(result.group(1)) |
441 |
else: |
442 |
ld_version = None |
443 |
else: |
444 |
ld_version = None |
445 |
dllwrap_exe = find_executable('dllwrap') |
446 |
if dllwrap_exe: |
447 |
out = os.popen(dllwrap_exe + ' --version','r') |
448 |
out_string = out.read() |
449 |
out.close() |
450 |
result = re.search(' (\d+\.\d+(\.\d+)*)',out_string) |
451 |
if result: |
452 |
dllwrap_version = LooseVersion(result.group(1)) |
453 |
else: |
454 |
dllwrap_version = None |
455 |
else: |
456 |
dllwrap_version = None |
457 |
return (gcc_version, ld_version, dllwrap_version) |
458 |
|
459 |
def is_cygwingcc(): |
460 |
'''Try to determine if the gcc that would be used is from cygwin.''' |
461 |
out = os.popen('gcc -dumpmachine', 'r') |
462 |
out_string = out.read() |
463 |
out.close() |
464 |
# out_string is the target triplet cpu-vendor-os |
465 |
# Cygwin's gcc sets the os to 'cygwin' |
466 |
return out_string.strip().endswith('cygwin') |