Home | History | Annotate | Download | only in distutils
      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         else:
     79             raise ValueError("Unknown MS Compiler version %s " % msc_ver)
     80 
     81 
     82 class 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
    297 class 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 
    353 CONFIG_H_OK = "ok"
    354 CONFIG_H_NOTOK = "not ok"
    355 CONFIG_H_UNCERTAIN = "uncertain"
    356 
    357 def 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 
    410 def 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 
    456 def 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