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 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 
     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         self.set_executables(compiler='gcc -mno-cygwin -O -Wall',
    323                              compiler_so='gcc -mno-cygwin -mdll -O -Wall',
    324                              compiler_cxx='g++ -mno-cygwin -O -Wall',
    325                              linker_exe='gcc -mno-cygwin',
    326                              linker_so='%s -mno-cygwin %s %s'
    327                                         % (self.linker_dll, shared_option,
    328                                            entry_point))
    329         # Maybe we should also append -mthreads, but then the finished

    330         # dlls need another dll (mingwm10.dll see Mingw32 docs)

    331         # (-mthreads: Support thread-safe exception handling on `Mingw32')

    332 
    333         # no additional libraries needed

    334         self.dll_libraries=[]
    335 
    336         # Include the appropriate MSVC runtime library if Python was built

    337         # with MSVC 7.0 or later.

    338         self.dll_libraries = get_msvcr()
    339 
    340     # __init__ ()

    341 
    342 # class Mingw32CCompiler

    343 
    344 # Because these compilers aren't configured in Python's pyconfig.h file by

    345 # default, we should at least warn the user if he is using a unmodified

    346 # version.

    347 
    348 CONFIG_H_OK = "ok"
    349 CONFIG_H_NOTOK = "not ok"
    350 CONFIG_H_UNCERTAIN = "uncertain"
    351 
    352 def check_config_h():
    353 
    354     """Check if the current Python installation (specifically, pyconfig.h)
    355     appears amenable to building extensions with GCC.  Returns a tuple
    356     (status, details), where 'status' is one of the following constants:
    357       CONFIG_H_OK
    358         all is well, go ahead and compile
    359       CONFIG_H_NOTOK
    360         doesn't look good
    361       CONFIG_H_UNCERTAIN
    362         not sure -- unable to read pyconfig.h
    363     'details' is a human-readable string explaining the situation.
    364 
    365     Note there are two ways to conclude "OK": either 'sys.version' contains
    366     the string "GCC" (implying that this Python was built with GCC), or the
    367     installed "pyconfig.h" contains the string "__GNUC__".
    368     """
    369 
    370     # XXX since this function also checks sys.version, it's not strictly a

    371     # "pyconfig.h" check -- should probably be renamed...

    372 
    373     from distutils import sysconfig
    374     import string
    375     # if sys.version contains GCC then python was compiled with

    376     # GCC, and the pyconfig.h file should be OK

    377     if string.find(sys.version,"GCC") >= 0:
    378         return (CONFIG_H_OK, "sys.version mentions 'GCC'")
    379 
    380     fn = sysconfig.get_config_h_filename()
    381     try:
    382         # It would probably better to read single lines to search.

    383         # But we do this only once, and it is fast enough

    384         f = open(fn)
    385         try:
    386             s = f.read()
    387         finally:
    388             f.close()
    389 
    390     except IOError, exc:
    391         # if we can't read this file, we cannot say it is wrong

    392         # the compiler will complain later about this file as missing

    393         return (CONFIG_H_UNCERTAIN,
    394                 "couldn't read '%s': %s" % (fn, exc.strerror))
    395 
    396     else:
    397         # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar

    398         if string.find(s,"__GNUC__") >= 0:
    399             return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
    400         else:
    401             return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
    402 
    403 
    404 
    405 def get_versions():
    406     """ Try to find out the versions of gcc, ld and dllwrap.
    407         If not possible it returns None for it.
    408     """
    409     from distutils.version import LooseVersion
    410     from distutils.spawn import find_executable
    411     import re
    412 
    413     gcc_exe = find_executable('gcc')
    414     if gcc_exe:
    415         out = os.popen(gcc_exe + ' -dumpversion','r')
    416         out_string = out.read()
    417         out.close()
    418         result = re.search('(\d+\.\d+(\.\d+)*)',out_string)
    419         if result:
    420             gcc_version = LooseVersion(result.group(1))
    421         else:
    422             gcc_version = None
    423     else:
    424         gcc_version = None
    425     ld_exe = find_executable('ld')
    426     if ld_exe:
    427         out = os.popen(ld_exe + ' -v','r')
    428         out_string = out.read()
    429         out.close()
    430         result = re.search('(\d+\.\d+(\.\d+)*)',out_string)
    431         if result:
    432             ld_version = LooseVersion(result.group(1))
    433         else:
    434             ld_version = None
    435     else:
    436         ld_version = None
    437     dllwrap_exe = find_executable('dllwrap')
    438     if dllwrap_exe:
    439         out = os.popen(dllwrap_exe + ' --version','r')
    440         out_string = out.read()
    441         out.close()
    442         result = re.search(' (\d+\.\d+(\.\d+)*)',out_string)
    443         if result:
    444             dllwrap_version = LooseVersion(result.group(1))
    445         else:
    446             dllwrap_version = None
    447     else:
    448         dllwrap_version = None
    449     return (gcc_version, ld_version, dllwrap_version)
    450