From bf5c70973f5c7bdd1e52b20c8d423b03f69418e4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:31:56 +0000 Subject: Latest version from Rene Liebscher; major changes: - added big comment describing possible problems - look for and react to versions of gcc, ld, and dlltool; mainly this is done by the 'get_versions()' function and the CygwinCCompiler and Mingw32CCompiler constructors - move 'check_config_h()' to end of file and defer calling it until we need to (ie. in the CygwinCCompiler constructor) - lots of changes in 'link_shared_object()' -- mostly seems to be library and DLL stuff, but I don't follow it entirely --- Lib/distutils/cygwinccompiler.py | 337 ++++++++++++++++++++++++++------------- 1 file changed, 230 insertions(+), 107 deletions(-) diff --git a/Lib/distutils/cygwinccompiler.py b/Lib/distutils/cygwinccompiler.py index 650627f..3f9a5bd 100644 --- a/Lib/distutils/cygwinccompiler.py +++ b/Lib/distutils/cygwinccompiler.py @@ -6,53 +6,54 @@ the Mingw32CCompiler class which handles the mingw32 port of GCC (same as cygwin in no-cygwin mode). """ +# problems: +# +# * if you use a msvc compiled python version (1.5.2) +# 1. you have to insert a __GNUC__ section in its config.h +# 2. you have to generate a import library for its dll +# - create a def-file for python??.dll +# - create a import library using +# dlltool --dllname python15.dll --def python15.def \ +# --output-lib libpython15.a +# +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# +# * We use put export_symbols in a def-file, and don't use +# --export-all-symbols because it doesn't worked reliable in some +# tested configurations. And because other windows compilers also +# need their symbols specified this no serious problem. +# +# tested configurations: +# +# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works +# (after patching python's config.h and for C++ some other include files) +# see also http://starship.python.net/crew/kernr/mingw32/Notes.html +# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works +# (ld doesn't support -shared, so we use dllwrap) +# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now +# - its dllwrap doesn't work, there is a bug in binutils 2.10.90 +# see also ..... +# - using gcc -mdll instead dllwrap doesn't work without -static because +# it tries to link against dlls instead their import libraries. (If +# it finds the dll first.) +# By specifying -static we force ld to link against the import libraries, +# this is windows standard and there are normally not the necessary symbols +# in the dlls. + # created 2000/05/05, Rene Liebscher __revision__ = "$Id$" -import os,sys,string -from distutils import sysconfig +import os,sys from distutils.unixccompiler import UnixCCompiler - -# Because these compilers aren't configured in Python's config.h file by -# default we should at least warn the user if he is using a unmodified -# version. - -def check_config_h(): - """Checks if the GCC compiler is mentioned in config.h. If it is not, - compiling probably doesn't work, so print a warning to stderr. - """ - - # XXX the result of the check should be returned! - - from distutils import sysconfig - import string,sys - try: - # It would probably better to read single lines to search. - # But we do this only once, and it is fast enough - f=open(sysconfig.get_config_h_filename()) - s=f.read() - f.close() - try: - # is somewhere a #ifdef __GNUC__ or something similar - string.index(s,"__GNUC__") - except ValueError: - sys.stderr.write ("warning: "+ - "Python's config.h doesn't seem to support your compiler.\n") - except IOError: - # if we can't read this file, we cannot say it is wrong - # the compiler will complain later about this file as missing - pass - - -# This is called when the module is imported, so we make this check only once -# XXX why not make it only when the compiler is needed? -check_config_h() - +from distutils.file_util import write_file class CygwinCCompiler (UnixCCompiler): compiler_type = 'cygwin' + gcc_version = None + dllwrap_version = None + ld_version = None def __init__ (self, verbose=0, @@ -61,22 +62,45 @@ class CygwinCCompiler (UnixCCompiler): UnixCCompiler.__init__ (self, verbose, dry_run, force) + if check_config_h()<=0: + self.warn( + "Python's config.h doesn't seem to support your compiler. " + "Compiling may fail because of undefined preprocessor macros.") + + (self.gcc_version, self.ld_version, self.dllwrap_version) = \ + get_versions() + sys.stderr.write(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % + (self.gcc_version, + self.ld_version, + self.dllwrap_version) ) + + # ld_version >= "2.10.90" should also be able to use + # gcc -mdll instead of dllwrap + # Older dllwraps had own version numbers, newer ones use the + # same as the rest of binutils ( also ld ) + # dllwrap 2.10.90 is buggy + if self.ld_version >= "2.10.90": + self.linker = "gcc" + else: + self.linker = "dllwrap" + # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. - self.set_executables(compiler='gcc -O -Wall', - compiler_so='gcc -O -Wall', - linker_exe='gcc', - linker_so='dllwrap --target=i386-cygwin32') + self.set_executables(compiler='gcc -mcygwin -O -Wall', + compiler_so='gcc -mcygwin -mdll -O -Wall', + linker_exe='gcc -mcygwin', + linker_so=('%s -mcygwin -mdll -static' % + self.linker)) # cygwin and mingw32 need different sets of libraries - self.dll_libraries=[ - # cygwin shouldn't need msvcrt, - # but without the dll's will crash - # ( gcc version 2.91.57 ) - # perhaps something about initialization - # mingw32 needs it in all cases - "msvcrt" - ] + if self.gcc_version == "2.91.57": + # cygwin shouldn't need msvcrt, but without the dlls will crash + # (gcc version 2.91.57) -- perhaps something about initialization + self.dll_libraries=["msvcrt"] + self.warn( + "Consider upgrading to a newer version of gcc") + else: + self.dll_libraries=[] # __init__ () @@ -93,64 +117,67 @@ class CygwinCCompiler (UnixCCompiler): extra_postargs=None, build_temp=None): - if libraries == None: - libraries = [] + # use separate copies, so can modify the lists + extra_preargs = list(extra_preargs or []) + libraries = list(libraries or []) - # Additional libraries: the python library is always needed on - # Windows we need the python version without the dot, eg. '15' - - pythonlib = ("python%d%d" % - (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) - libraries.append(pythonlib) + # Additional libraries libraries.extend(self.dll_libraries) + + # we want to put some files in the same directory as the + # object files are, build_temp doesn't help much - # name of extension + # where are the object files + temp_dir = os.path.dirname(objects[0]) - # XXX WRONG WRONG WRONG - # this is NOT the place to make guesses about Python namespaces; - # that MUST be done in build_ext.py + # name of dll to give the helper files (def, lib, exp) the same name + (dll_name, dll_extension) = os.path.splitext( + os.path.basename(output_filename)) - if not debug: - ext_name = os.path.basename(output_filename)[:-len(".pyd")] - else: - ext_name = os.path.basename(output_filename)[:-len("_d.pyd")] + # generate the filenames for these files + def_file = None # this will be done later, if necessary + exp_file = os.path.join(temp_dir, dll_name + ".exp") + lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") - def_file = os.path.join(build_temp, ext_name + ".def") - #exp_file = os.path.join(build_temp, ext_name + ".exp") - #lib_file = os.path.join(build_temp, 'lib' + ext_name + ".a") - - # Make .def file - # (It would probably better to check if we really need this, - # but for this we had to insert some unchanged parts of - # UnixCCompiler, and this is not what we want.) - f = open(def_file,"w") - f.write("EXPORTS\n") # intro - if export_symbols == None: - # export a function "init" + ext_name - f.write("init" + ext_name + "\n") + #extra_preargs.append("--verbose") + if self.linker == "dllwrap": + extra_preargs.extend([#"--output-exp",exp_file, + "--output-lib",lib_file, + ]) else: - # if there are more symbols to export write them into f + # doesn't work: bfd_close build\...\libfoo.a: Invalid operation + extra_preargs.extend([#"-Wl,--out-implib,%s" % lib_file, + ]) + + # check what we got in export_symbols + if export_symbols is not None: + # Make .def file + # (It would probably better to check if we really need this, + # but for this we had to insert some unchanged parts of + # UnixCCompiler, and this is not what we want.) + def_file = os.path.join(temp_dir, dll_name + ".def") + contents = [ + "LIBRARY %s" % os.path.basename(output_filename), + "EXPORTS"] for sym in export_symbols: - f.write(sym+"\n") - f.close() - - if extra_preargs == None: - extra_preargs = [] - - extra_preargs = extra_preargs + [ - #"--verbose", - #"--output-exp",exp_file, - #"--output-lib",lib_file, - "--def",def_file - ] - + contents.append(sym) + self.execute(write_file, (def_file, contents), + "writing %s" % def_file) + + if def_file: + if self.linker == "dllwrap": + # for dllwrap we have to use a special option + extra_preargs.append("--def") + # for gcc/ld it is specified as any other object file + extra_preargs.append(def_file) + # who wants symbols and a many times larger output file # should explicitly switch the debug mode on - # otherwise we let dllwrap strip the output file + # otherwise we let dllwrap/ld strip the output file # (On my machine unstripped_file = stripped_file + 254KB # 10KB < stripped_file < ??100KB ) if not debug: - extra_preargs = extra_preargs + ["-s"] + extra_preargs.append("-s") UnixCCompiler.link_shared_object(self, objects, @@ -159,7 +186,7 @@ class CygwinCCompiler (UnixCCompiler): libraries, library_dirs, runtime_library_dirs, - None, # export_symbols, we do this with our def-file + None, # export_symbols, we do this in our def-file debug, extra_preargs, extra_postargs, @@ -181,19 +208,115 @@ class Mingw32CCompiler (CygwinCCompiler): force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) + + # A real mingw32 doesn't need to specify a different entry point, + # but cygwin 2.91.57 in no-cygwin-mode needs it. + if self.gcc_version <= "2.91.57": + entry_point = '--entry _DllMain@12' + else: + entry_point = '' self.set_executables(compiler='gcc -mno-cygwin -O -Wall', - compiler_so='gcc -mno-cygwin -O -Wall', + compiler_so='gcc -mno-cygwin -mdll -O -Wall', linker_exe='gcc -mno-cygwin', - linker_so='dllwrap' - + ' --target=i386-mingw32' - + ' --entry _DllMain@12') - # mingw32 doesn't really need 'target' and cygwin too (it seems, - # it is enough to specify a different entry point) - - # no additional libraries need - # (only msvcrt, which is already added by CygwinCCompiler) - + linker_so='%s -mno-cygwin -mdll -static %s' + % (self.linker, entry_point)) + # Maybe we should also append -mthreads, but then the finished + # dlls need another dll (mingwm10.dll see Mingw32 docs) + # (-mthreads: Support thread-safe exception handling on `Mingw32') + + # no additional libraries needed + self.dll_libraries=[] + # __init__ () - + # class Mingw32CCompiler + +# Because these compilers aren't configured in Python's config.h file by +# default, we should at least warn the user if he is using a unmodified +# version. + +def check_config_h(): + """Checks if the GCC compiler is mentioned in config.h. If it is not, + compiling probably doesn't work. + """ + # return values + # 2: OK, python was compiled with GCC + # 1: OK, python's config.h mentions __GCC__ + # 0: uncertain, because we couldn't check it + # -1: probably not OK, because we didn't found it in config.h + # You could check check_config_h()>0 => OK + + from distutils import sysconfig + import string,sys + # if sys.version contains GCC then python was compiled with + # GCC, and the config.h file should be OK + if -1 == string.find(sys.version,"GCC"): + pass # go to the next test + else: + return 2 + + try: + # It would probably better to read single lines to search. + # But we do this only once, and it is fast enough + f=open(sysconfig.get_config_h_filename()) + s=f.read() + f.close() + + # is somewhere a #ifdef __GNUC__ or something similar + if -1 == string.find(s,"__GNUC__"): + return -1 + else: + return 1 + except IOError: + # if we can't read this file, we cannot say it is wrong + # the compiler will complain later about this file as missing + pass + return 0 + +def get_versions(): + """ Try to find out the versions of gcc, ld and dllwrap. + If not possible it returns None for it. + """ + from distutils.version import StrictVersion + from distutils.spawn import find_executable + import re + + gcc_exe = find_executable('gcc') + if gcc_exe: + out = os.popen(gcc_exe + ' -dumpversion','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + gcc_version = StrictVersion(result.group(1)) + else: + gcc_version = None + else: + gcc_version = None + ld_exe = find_executable('ld') + if ld_exe: + out = os.popen(ld_exe + ' -v','r') + out_string = out.read() + out.close() + result = re.search('(\d+\.\d+\.\d+)',out_string) + if result: + ld_version = StrictVersion(result.group(1)) + else: + ld_version = None + else: + ld_version = None + dllwrap_exe = find_executable('dllwrap') + if dllwrap_exe: + out = os.popen(dllwrap_exe + ' --version','r') + out_string = out.read() + out.close() + result = re.search(' (\d+\.\d+\.\d+)',out_string) + if result: + dllwrap_version = StrictVersion(result.group(1)) + else: + dllwrap_version = None + else: + dllwrap_version = None + return (gcc_version, ld_version, dllwrap_version) + -- cgit v0.12