[Distutils] Re: CygwinCCompiler, msvc hack, BCPPCompiler

Rene Liebscher R.Liebscher@gmx.de
Thu, 06 Jul 2000 12:57:00 +0200


This is a multi-part message in MIME format.
--------------C2F9671324B463815CAE57DD
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Greg Ward wrote:
> 
> On 30 June 2000, Rene Liebscher said:
> > But if you compile it with MSVC or use the downloaded binary
> > version, then you have this special PC config.h file. And in
> > this file you need a special section for GNU C. (probably
> > using #ifdef __GNUC__ somewhere.)
> >
> > So I will change my code, if it finds GCC in sys.version, then
> > it doesn't need to check the config.h file.
> 
> This is all to avoid the "Python's config.h doesn't seem to support your
> compiler." warning, right?  Or is there something more to it?
Yes, on a GCC compiled python you would get this warning. 
 
> Have a patch yet, BTW?
And here is the patch. (It removes also the msvc_hack from build_ext.)

It fixes my compiler class to avoid this warning on a GCC
compiled python and it removes all the python specific stuff 
(except the above).
It inserts this in build_ext (specifying the export symbol
init{module} if no export symbols given and link the library python??)
This means also I had to adapt the msvc compiler class and the Extension
class (It should be possible to specify None as export symbols,
this would mean standard python module with one export symbol, 
specifying a list would mean export what is in the list.)

The def-file will go through the export_symbols parameter.
(as string or if given export_symbols as list)

I didn't changed bcppcompiler.py because I can't test it.


kind regards

Rene Liebscher
--------------C2F9671324B463815CAE57DD
Content-Type: text/plain; charset=iso-8859-1; name="mscv_hack.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: inline; filename="mscv_hack.patch"

diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/command/build_ext.py dp/distutils/command/build_ext.py
--- distutils.orig/distutils/command/build_ext.py	Fri Jun 30 17:18:01 2000
+++ dp/distutils/command/build_ext.py	Wed Jul  5 11:27:48 2000
@@ -420,16 +420,40 @@
                 objects.extend (ext.extra_objects)
             extra_args = ext.extra_link_args or []
 
-            # Bunch of fixing-up we have to do for Microsoft's linker.
-            if self.compiler.compiler_type == 'msvc':
-                self.msvc_prelink_hack(sources, ext, extra_args)
+            # For all platforms and compiler doesn't export all symbols
+            # by default, we have to specify the module's init function
+            # if the use doesn't provide any export information.
+            # All other platforms and compilers should ignore such
+            # export information.
+            if ext.export_symbol_file == None and ext.export_symbols == None:
+                # if no exports are specified we can't build a working
+                # python module.
+                # so let us export the module's init function
+                export_symbols = ["init" + string.split(ext.name,'.')[-1]]
+            else:
+            	# export_symbols will be a list, a string or None if 
+            	# neither export_symbols nor export_symbol_file was set.                                
+		export_symbols = ext.export_symbols or ext.export_symbol_file
+
+            # The python library is always needed on
+            # Windows we need the python version without the dot, eg. '15'
+            # MSVC doesn't really need this, because the pragma in config.h 
+            if sys.platform == "win32": 
+                pythonlib = ("python%d%d" %
+                     (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
+                # don't extend ext.libraries, it may be shared with other
+                # extensions, it is a reference to the original list
+                libraries = ext.libraries + [pythonlib]
+            else:
+                libraries = ext.libraries
 
             self.compiler.link_shared_object (
                 objects, ext_filename, 
-                libraries=ext.libraries,
+                libraries=libraries,
                 library_dirs=ext.library_dirs,
                 runtime_library_dirs=ext.runtime_library_dirs,
                 extra_postargs=extra_args,
+                export_symbols=export_symbols, 
                 debug=self.debug,
                 build_temp=self.build_temp)
 
@@ -511,44 +535,6 @@
 
     # find_swig ()
     
-
-    # -- Hooks 'n hacks ------------------------------------------------
-
-    def msvc_prelink_hack (self, sources, ext, extra_args):
-
-        # XXX this is a kludge!  Knowledge of specific compilers or
-        # platforms really doesn't belong here; in an ideal world, the
-        # CCompiler interface would provide access to everything in a
-        # compiler/linker system needs to build Python extensions, and
-        # we would just do everything nicely and cleanly through that
-        # interface.  However, this is a not an ideal world and the
-        # CCompiler interface doesn't handle absolutely everything.
-        # Thus, kludges like this slip in occasionally.  (This is no
-        # excuse for committing more platform- and compiler-specific
-        # kludges; they are to be avoided if possible!)
-
-        def_file = ext.export_symbol_file
-
-        if def_file is not None:
-            extra_args.append ('/DEF:' + def_file)
-        else:
-            modname = string.split (ext.name, '.')[-1]
-            extra_args.append('/export:init%s' % modname)
-
-        # The MSVC linker generates .lib and .exp files, which cannot be
-        # suppressed by any linker switches. The .lib files may even be
-        # needed! Make sure they are generated in the temporary build
-        # directory. Since they have different names for debug and release
-        # builds, they can go into the same directory.
-        implib_file = os.path.join (
-            self.implib_dir,
-            self.get_ext_libname (ext.name))
-        extra_args.append ('/IMPLIB:' + implib_file)
-        self.mkpath (os.path.dirname (implib_file))
-
-    # msvc_prelink_hack ()
-
-
     # -- Name generators -----------------------------------------------
     # (extension names, filenames, whatever)
 
diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/cygwinccompiler.py dp/distutils/cygwinccompiler.py
--- distutils.orig/distutils/cygwinccompiler.py	Fri Jun 30 17:17:55 2000
+++ dp/distutils/cygwinccompiler.py	Wed Jul  5 12:29:57 2000
@@ -6,11 +6,34 @@
 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
+#   
+# * cygwin gcc 2.91 works (after patching some include files)
+# * mingw32 gcc 2.95.2 works (it doesn't support gcc -shared)
+# * cygwin gcc 2.95.2 doesn't work now
+#   - its dllwrap doesn't work at my machine
+#   - target had to be changed to something like i686-cygwin32
+#   - mingw32-mode is specified by -mno-cygwin
+#   - using gcc -shared instead dllwrap didn't work because some problems with 
+#     the import library, maybe it needs some additional parameters 
+
+
 # created 2000/05/05, Rene Liebscher
 
 __revision__ = "$Id: cygwinccompiler.py,v 1.2 2000/06/29 22:57:55 gward Exp $"
 
 import os,sys,string
+from types import *
 from distutils import sysconfig
 from distutils.unixccompiler import UnixCCompiler
 
@@ -20,13 +43,25 @@
 
 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.
+    compiling probably doesn't work.
     """
-
-    # XXX the result of the check should be returned!
+    # 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
+    try:
+        string.index(sys.version,"GCC")
+        return 2
+    except ValueError:
+        pass # go to the next test
+    
     try:
         # It would probably better to read single lines to search.
         # But we do this only once, and it is fast enough 
@@ -35,19 +70,15 @@
         f.close()
         try:
             # is somewhere a #ifdef __GNUC__ or something similar
-            string.index(s,"__GNUC__") 
+            string.index(s,"__GNUC__")
+            return 1  
         except ValueError:
-            sys.stderr.write ("warning: "+
-                "Python's config.h doesn't seem to support your compiler.\n")
+            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
-
-
-# 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()
+    return 0
 
 
 class CygwinCCompiler (UnixCCompiler):
@@ -59,8 +90,13 @@
                   dry_run=0,
                   force=0):
 
+
         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.")
+
         # 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',
@@ -96,53 +132,55 @@
         if libraries == None:
             libraries = []
         
-        # 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)
-        libraries.extend(self.dll_libraries)
-
-        # name of extension
-
-        # XXX WRONG WRONG WRONG
-        # this is NOT the place to make guesses about Python namespaces;
-        # that MUST be done in build_ext.py
+        # Additional libraries
+        # libraries.extend(...) would affect the list we got as parameter
+        # and this can come from anywhere  
+        libraries = libraries + 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 dll
         if not debug:
-            ext_name = os.path.basename(output_filename)[:-len(".pyd")]
+            dll_name = os.path.basename(output_filename)[:-len(".pyd")]
         else:
-            ext_name = os.path.basename(output_filename)[:-len("_d.pyd")]
+            dll_name = os.path.basename(output_filename)[:-len("_d.pyd")]
 
-        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")
+        # where are the object files
+        temp_dir=os.path.dirname(objects[0])
+
+        # 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")
         
-        # 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")    
+        #  check what we got in export_symbols
+        if export_symbols != None and type(export_symbols) == StringType:
+            def_file = export_symbol_file
         else:
-            # if there are more symbols to export write them into f
-            for sym in export_symbols:
-                f.write(sym+"\n")                    
-        f.close()
-        
+            # 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.) 
+            if type(export_symbols) == ListType :
+                def_file = os.path.join(temp_dir, dll_name + ".def")
+                f = open(def_file,"w")
+                f.write("EXPORTS\n") # intro
+                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
+                        "--output-exp",exp_file,
+                        "--output-lib",lib_file,
                         ]
+        if def_file:
+            extra_preargs.extend(["--def",def_file])
         
         # who wants symbols and a many times larger output file
         # should explicitely switch the debug mode on 
diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/extension.py dp/distutils/extension.py
--- distutils.orig/distutils/extension.py	Mon Jun 26 09:34:35 2000
+++ dp/distutils/extension.py	Tue Jul  4 17:00:00 2000
@@ -111,7 +111,7 @@
         self.extra_objects = extra_objects or []
         self.extra_compile_args = extra_compile_args or []
         self.extra_link_args = extra_link_args or []
-        self.export_symbols = export_symbols or []
+        self.export_symbols = export_symbols
         self.export_symbol_file = export_symbol_file
 
 # class Extension
diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/msvccompiler.py dp/distutils/msvccompiler.py
--- distutils.orig/distutils/msvccompiler.py	Mon Jul  3 12:33:44 2000
+++ dp/distutils/msvccompiler.py	Tue Jul  4 18:06:50 2000
@@ -374,11 +374,25 @@
                 ldflags = self.ldflags_shared
 
             export_opts = []
-            for sym in (export_symbols or []):
-                export_opts.append("/EXPORT:" + sym)
+            if export_symbols != None and type(export_symbols) == StringType:
+                export_opts.append('/DEF:' + export_symbols)
+            else:
+                for sym in (export_symbols or []):
+                    export_opts.append("/EXPORT:" + sym)
 
             ld_args = (ldflags + lib_opts + export_opts + 
                        objects + ['/OUT:' + output_filename])
+
+            # The MSVC linker generates .lib and .exp files, which cannot be
+            # suppressed by any linker switches. The .lib files may even be
+            # needed! Make sure they are generated in the temporary build
+            # directory. Since they have different names for debug and release
+            # builds, they can go into the same directory.
+            dll_name, dll_ext = os.path.splitext(os.path.basename(output_filename))
+            implib_file = os.path.join (
+                os.path.dirname(objects[0]),
+                self.static_lib_format % (dll_name, self.static_lib_extension))
+            ld_args.append ('/IMPLIB:' + implib_file)
 
             if extra_preargs:
                 ld_args[:0] = extra_preargs

--------------C2F9671324B463815CAE57DD--