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