PageRenderTime 68ms CodeModel.GetById 50ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 1ms

/Lib/distutils/cygwinccompiler.py

http://unladen-swallow.googlecode.com/
Python | 447 lines | 271 code | 51 blank | 125 comment | 32 complexity | 02f2ce828ec3b7ba4ddb4cdf68b6fe1f MD5 | raw file
  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: cygwinccompiler.py 73349 2009-06-11 09:17:19Z tarek.ziade $"
 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        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
348CONFIG_H_OK = "ok"
349CONFIG_H_NOTOK = "not ok"
350CONFIG_H_UNCERTAIN = "uncertain"
351
352def 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        s = f.read()
386        f.close()
387
388    except IOError, exc:
389        # if we can't read this file, we cannot say it is wrong
390        # the compiler will complain later about this file as missing
391        return (CONFIG_H_UNCERTAIN,
392                "couldn't read '%s': %s" % (fn, exc.strerror))
393
394    else:
395        # "pyconfig.h" contains an "#ifdef __GNUC__" or something similar
396        if string.find(s,"__GNUC__") >= 0:
397            return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
398        else:
399            return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
400
401
402
403def get_versions():
404    """ Try to find out the versions of gcc, ld and dllwrap.
405        If not possible it returns None for it.
406    """
407    from distutils.version import LooseVersion
408    from distutils.spawn import find_executable
409    import re
410
411    gcc_exe = find_executable('gcc')
412    if gcc_exe:
413        out = os.popen(gcc_exe + ' -dumpversion','r')
414        out_string = out.read()
415        out.close()
416        result = re.search('(\d+\.\d+(\.\d+)*)',out_string)
417        if result:
418            gcc_version = LooseVersion(result.group(1))
419        else:
420            gcc_version = None
421    else:
422        gcc_version = None
423    ld_exe = find_executable('ld')
424    if ld_exe:
425        out = os.popen(ld_exe + ' -v','r')
426        out_string = out.read()
427        out.close()
428        result = re.search('(\d+\.\d+(\.\d+)*)',out_string)
429        if result:
430            ld_version = LooseVersion(result.group(1))
431        else:
432            ld_version = None
433    else:
434        ld_version = None
435    dllwrap_exe = find_executable('dllwrap')
436    if dllwrap_exe:
437        out = os.popen(dllwrap_exe + ' --version','r')
438        out_string = out.read()
439        out.close()
440        result = re.search(' (\d+\.\d+(\.\d+)*)',out_string)
441        if result:
442            dllwrap_version = LooseVersion(result.group(1))
443        else:
444            dllwrap_version = None
445    else:
446        dllwrap_version = None
447    return (gcc_version, ld_version, dllwrap_version)