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