[Distutils] Factoring out WindowsCCompiler?

Rene Liebscher R.Liebscher@gmx.de
Thu Oct 19 09:37:03 2000


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

Greg Ward wrote:
> 
> Rene --
> 
> now that the fuss over Distutils 1.0/Python 2.0 has died down, where
> does your work on factoring out WindowsCCompiler stand?  Now would be a
> good time to get that checked in, so it can be well tested before
> Distutils 1.1.
> 
I'm still working on it. But you can have a first version.

file: windowsccompiler.py

It collects some common initialization and helper functions
in the module. 
The WindowsCCompiler class is currently not more than 
an additional layer between CCompiler and the existing 
compiler classes, which means it doesn't contain any 
implementations for compile and link methods.

The main problem is that the link method for Borland C++
needs this strict order.
Maybe one should think about some kind of generic building
the command line.
For example: (Borland C++, linking of a dll)

cmdline_link_dll = [
		(LINKER_EXECUTABLE,),
		(EXTRA_PREARGS,),
		"/Tpd","/Gn","/q","/x",
                (LIBRARY_DIRS,"/L%s"),
                (EXTRA_POSTARGS,)
                "c0d32", (OBJECTS,"%s"),
                ",",
                (OUTPUT_FILENAME,"%s"),
                ",,",
		(LIBRARIES,"%s"), "import32", "cw32mt",
		",",
		(DEF_FILE,"%s"),			
		",",
		(RESOURCES."%s")                
]

This would need a kind of "parser" in WindowsCCompiler and 
for a new compiler you would only specify how its command
line has to be built.
(It would change some existing code to data structures,
which means also to a higher level of abstraction.)


--------------------------------------
And another problem which was reported to me.

Mike Fletcher wrote:
> The tar format
> command for bdist uses fully-specified windows path names, which might make
> tar fail:
> 
> d:\bin\lang\cygwin\cygwin-b20\H-i586-cygwin32\bin\tar.exe -cf
> S:\PyOpenGL\dist\PyOpenGL-1.5.6a1.win32.tar .
> tar: Cannot execute remote shell: No such file or directory
> tar: Cannot open S:\PyOpenGL\dist\PyOpenGL-1.5.6a1.win32.tar: I/O error
> tar: Error is not recoverable: exiting now
> error: command 'tar' failed with exit status 2
> 
> Using /PyOpenGL/dist/yada (or even just "yada", since you've changed
> directories) should work I think. Strangely, the sdist command uses a
> similar format and tar works there... curiouser and curiouser said Alice. 

The problem comes from tar. The cygwin tar program doesn't like absolute
pathnames on windows. (sdist works in the current directory.)
(S:\PyOpenGL\dist\PyOpenGL-1.5.6a1.win32.tar fails, but
 //S/PyOpenGL/... had probably worked.)

If we still want let the users work with tar (on windows), we had to 
change either the path so it is accepted (this is windows cygwin-tar 
specific) or we could use relative paths instead of absolute ones 
(this is not platform specific).
I attached a patch for archive_util.py which tries to use a relative
path
whenever possible.

file: archive_util.py


Kind regards

Rene Liebscher
--------------731E085DA5F2F5144B5381BE
Content-Type: text/plain; charset=us-ascii;
 name="windowsccompiler.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="windowsccompiler.patch"

diff -BurN --exclude=*.pyc --minimal distutils.orig/distutils/bcppcompiler.py distutils.patched/distutils/bcppcompiler.py
--- distutils.orig/distutils/bcppcompiler.py	Wed Sep 27 15:37:16 2000
+++ distutils.patched/distutils/bcppcompiler.py	Thu Oct 19 14:42:32 2000
@@ -18,12 +18,12 @@
 from distutils.errors import \
      DistutilsExecError, DistutilsPlatformError, \
      CompileError, LibError, LinkError
-from distutils.ccompiler import \
-     CCompiler, gen_preprocess_options, gen_lib_options
+from distutils.windowsccompiler import WindowsCCompiler
+from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 from distutils.file_util import write_file
 
 
-class BCPPCompiler(CCompiler) :
+class BCPPCompiler(WindowsCCompiler) :
     """Concrete class that implements an interface to the Borland C/C++
     compiler, as defined by the CCompiler abstract class.
     """
@@ -27,36 +27,15 @@
     """Concrete class that implements an interface to the Borland C/C++
     compiler, as defined by the CCompiler abstract class.
     """
-
     compiler_type = 'bcpp'
 
-    # Just set this so CCompiler's constructor doesn't barf.  We currently
-    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
-    # as it really isn't necessary for this sort of single-compiler class.
-    # Would be nice to have a consistent interface with UnixCCompiler,
-    # though, so it's worth thinking about.
-    executables = {}
-
-    # Private class data (need to distinguish C from C++ source for compiler)
-    _c_extensions = ['.c']
-    _cpp_extensions = ['.cc', '.cpp', '.cxx']
-
-    # Needed for the filename generation methods provided by the
-    # base class, CCompiler.
-    src_extensions = _c_extensions + _cpp_extensions
-    obj_extension = '.obj'
-    static_lib_extension = '.lib'
-    shared_lib_extension = '.dll'
-    static_lib_format = shared_lib_format = '%s%s'
-    exe_extension = '.exe'
-
 
     def __init__ (self,
                   verbose=0,
                   dry_run=0,
                   force=0):
 
-        CCompiler.__init__ (self, verbose, dry_run, force)
+        WindowsCCompiler.__init__ (self, verbose, dry_run, force)
 
         # These executables are assumed to all be in the path.
         # Borland doesn't seem to use any special registry settings to
@@ -65,6 +44,7 @@
         self.cc = "bcc32.exe"
         self.linker = "ilink32.exe"
         self.lib = "tlib.exe"
+        self.rc = "brcc32.exe"
 
         self.preprocess_options = None
         self.compile_options = ['/tWM', '/O2', '/q', '/g0']
@@ -120,7 +100,7 @@
                 if ext == '.rc':
                     # This needs to be compiled to a .res file -- do it now.
                     try:
-                        self.spawn (["brcc32", "-fo", obj, src])
+                        self.spawn ([self.rc, "-fo", obj, src])
                     except DistutilsExecError, msg:
                         raise CompileError, msg
                     continue # the 'for' loop
@@ -214,7 +194,7 @@
         if self._need_link (objects, output_filename):
 
             # Figure out linker args based on type of target.
-            if target_desc == CCompiler.EXECUTABLE:
+            if target_desc == self.EXECUTABLE:
                 startup_obj = 'c0w32'
                 if debug:
                     ld_args = self.ldflags_exe_debug[:]
@@ -316,60 +296,3 @@
             self.announce ("skipping %s (up-to-date)" % output_filename)
 
     # link ()
-
-    # -- Miscellaneous methods -----------------------------------------
-
-
-    def find_library_file (self, dirs, lib, debug=0):
-        # List of effective library names to try, in order of preference:
-        # xxx_bcpp.lib is better than xxx.lib
-        # and xxx_d.lib is better than xxx.lib if debug is set
-        #
-        # The "_bcpp" suffix is to handle a Python installation for people
-        # with multiple compilers (primarily Distutils hackers, I suspect
-        # ;-).  The idea is they'd have one static library for each
-        # compiler they care about, since (almost?) every Windows compiler
-        # seems to have a different format for static libraries.
-        if debug:
-            dlib = (lib + "_d")
-            try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
-        else:
-            try_names = (lib + "_bcpp", lib)
-
-        for dir in dirs:
-            for name in try_names:
-                libfile = os.path.join(dir, self.library_filename(name))
-                if os.path.exists(libfile):
-                    return libfile
-        else:
-            # Oops, didn't find it in *any* of 'dirs'
-            return None
-
-    # overwrite the one from CCompiler to support rc and res-files
-    def object_filenames (self,
-                          source_filenames,
-                          strip_dir=0,
-                          output_dir=''):
-        if output_dir is None: output_dir = ''
-        obj_names = []
-        for src_name in source_filenames:
-            # use normcase to make sure '.rc' is really '.rc' and not '.RC'
-            (base, ext) = os.path.splitext (os.path.normcase(src_name))
-            if ext not in (self.src_extensions + ['.rc','.res']):
-                raise UnknownFileError, \
-                      "unknown file type '%s' (from '%s')" % \
-                      (ext, src_name)
-            if strip_dir:
-                base = os.path.basename (base)
-            if ext == '.res':
-                # these can go unchanged
-                obj_names.append (os.path.join (output_dir, base + ext))
-            elif ext == '.rc':
-                # these need to be compiled to .res-files
-                obj_names.append (os.path.join (output_dir, base + '.res'))
-            else:
-                obj_names.append (os.path.join (output_dir,
-                                            base + self.obj_extension))
-        return obj_names
-
-    # object_filenames ()
diff -BurN --exclude=*.pyc --minimal distutils.orig/distutils/ccompiler.py distutils.patched/distutils/ccompiler.py
--- distutils.orig/distutils/ccompiler.py	Wed Sep 27 11:26:08 2000
+++ distutils.patched/distutils/ccompiler.py	Thu Oct 19 14:42:32 2000
@@ -695,7 +695,7 @@
         """
         raise NotImplementedError
 
-    def library_option (self, lib):
+    def library_option (self, lib, debug):
         """Return the compiler option to add 'dir' to the list of libraries
         linked into the shared library or executable.
         """
@@ -969,7 +969,7 @@
 # gen_preprocess_options ()
 
 
-def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries):
+def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries,debug=0):
     """Generate linker options for searching library directories and
     linking with specific libraries.  'libraries' and 'library_dirs' are,
     respectively, lists of library names (not filenames!) and search
diff -BurN --exclude=*.pyc --minimal distutils.orig/distutils/cygwinccompiler.py distutils.patched/distutils/cygwinccompiler.py
--- distutils.orig/distutils/cygwinccompiler.py	Wed Sep 27 11:26:09 2000
+++ distutils.patched/distutils/cygwinccompiler.py	Thu Oct 19 14:45:12 2000
@@ -46,6 +46,7 @@
 __revision__ = "$Id: cygwinccompiler.py,v 1.8 2000/09/27 02:08:14 gward Exp $"
 
 import os,sys,copy
+import windowsccompiler
 from distutils.ccompiler import gen_preprocess_options, gen_lib_options
 from distutils.unixccompiler import UnixCCompiler
 from distutils.file_util import write_file
@@ -60,6 +61,35 @@
     static_lib_format = "lib%s%s"
     shared_lib_format = "%s%s"
     exe_extension = ".exe"
+
+    # Private class data (need to distinguish C from C++ source for compiler)
+    _c_extensions = ['.c']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx']
+    _rc_extensions = ['.rc']
+    _rc_obj_extension = '.rc.o'
+    _res_extensions = ['.res']
+    _res_obj_extension = '.res.o'
+    _mc_extensions = [] # not supported by cygwin ['.mc']
+    #_mc_obj_extension = '.mc.o'
+
+    # res-files go for most compilers to the linker, but gcc compiles them
+    # to a real object file in the compile() method
+    # so you have to support res-files as source files
+    _passthrough_extensions = []
+
+    # Needed for the filename generation methods provided by the
+    # base class, CCompiler.
+    src_extensions = ( _c_extensions + _cpp_extensions
+                     + _rc_extensions + _mc_extensions
+                     + _passthrough_extensions )
+
+    obj_extension = '.obj'
+    static_lib_extension = '.lib'
+    shared_lib_extension = '.dll'
+    static_lib_format = shared_lib_format = '%s%s'
+    exe_extension = '.exe'
+
+
    
     def __init__ (self,
                   verbose=0,
@@ -68,10 +98,10 @@
 
         UnixCCompiler.__init__ (self, verbose, dry_run, force)
 
-        (status, details) = check_config_h()
+        (status, details) = windowsccompiler.check_compatibility("GCC","__GNUC__")
         self.debug_print("Python's GCC status: %s (details: %s)" %
                          (status, details))
-        if status is not CONFIG_H_OK:
+        if status is not windowsccompiler.COMPATIBILITY_OK:
             self.warn(
                 "Python's config.h doesn't seem to support your compiler.  " +
                 ("Reason: %s." % details) +
@@ -275,25 +305,7 @@
                           source_filenames,
                           strip_dir=0,
                           output_dir=''):
-        if output_dir is None: output_dir = ''
-        obj_names = []
-        for src_name in source_filenames:
-            # use normcase to make sure '.rc' is really '.rc' and not '.RC'
-            (base, ext) = os.path.splitext (os.path.normcase(src_name))
-            if ext not in (self.src_extensions + ['.rc','.res']):
-                raise UnknownFileError, \
-                      "unknown file type '%s' (from '%s')" % \
-                      (ext, src_name)
-            if strip_dir:
-                base = os.path.basename (base)
-            if ext == '.res' or ext == '.rc':
-                # these need to be compiled to object files
-                obj_names.append (os.path.join (output_dir, 
-                                            base + ext + self.obj_extension))
-            else:
-                obj_names.append (os.path.join (output_dir,
-                                            base + self.obj_extension))
-        return obj_names
+        return windowsccompiler.object_filenames (self,source_filenames,strip_dir,output_dir)
 
     # object_filenames ()
 
@@ -334,64 +346,6 @@
     # __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.
-
-CONFIG_H_OK = "ok"
-CONFIG_H_NOTOK = "not ok"
-CONFIG_H_UNCERTAIN = "uncertain"
-
-def check_config_h():
-
-    """Check if the current Python installation (specifically, config.h)
-    appears amenable to building extensions with GCC.  Returns a tuple
-    (status, details), where 'status' is one of the following constants:
-      CONFIG_H_OK
-        all is well, go ahead and compile
-      CONFIG_H_NOTOK
-        doesn't look good
-      CONFIG_H_UNCERTAIN
-        not sure -- unable to read config.h
-    'details' is a human-readable string explaining the situation.
-
-    Note there are two ways to conclude "OK": either 'sys.version' contains
-    the string "GCC" (implying that this Python was built with GCC), or the
-    installed "config.h" contains the string "__GNUC__".
-    """
-
-    # XXX since this function also checks sys.version, it's not strictly a
-    # "config.h" check -- should probably be renamed...
-
-    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 string.find(sys.version,"GCC") >= 0:
-        return (CONFIG_H_OK, "sys.version mentions 'GCC'")
-    
-    fn = sysconfig.get_config_h_filename()
-    try:
-        # It would probably better to read single lines to search.
-        # But we do this only once, and it is fast enough 
-        f = open(fn)
-        s = f.read()
-        f.close()
-        
-    except IOError, exc:
-        # if we can't read this file, we cannot say it is wrong
-        # the compiler will complain later about this file as missing
-        return (CONFIG_H_UNCERTAIN,
-                "couldn't read '%s': %s" % (fn, exc.strerror))
-
-    else:
-        # "config.h" contains an "#ifdef __GNUC__" or something similar
-        if string.find(s,"__GNUC__") >= 0:
-            return (CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn)
-        else:
-            return (CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn)
-
 
 
 def get_versions():
diff -BurN --exclude=*.pyc --minimal distutils.orig/distutils/msvccompiler.py distutils.patched/distutils/msvccompiler.py
--- distutils.orig/distutils/msvccompiler.py	Wed Sep 27 11:26:10 2000
+++ distutils.patched/distutils/msvccompiler.py	Thu Oct 19 14:44:41 2000
@@ -15,8 +15,10 @@
 from distutils.errors import \
      DistutilsExecError, DistutilsPlatformError, \
      CompileError, LibError, LinkError
-from distutils.ccompiler import \
-     CCompiler, gen_preprocess_options, gen_lib_options
+from distutils.ccompiler import gen_preprocess_options, gen_lib_options
+from distutils.windowsccompiler import WindowsCCompiler
+
+
 
 _can_read_reg = 0
 try:
@@ -50,8 +52,6 @@
     HKEY_LOCAL_MACHINE = hkey_mod.HKEY_LOCAL_MACHINE
     HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
     HKEY_USERS = hkey_mod.HKEY_USERS
-    
-    
 
 def get_devstudio_versions ():
     """Get list of devstudio versions from the Windows registry.  Return a
@@ -162,43 +162,18 @@
         os.environ[name] = string.join (p,';')
 
 
-class MSVCCompiler (CCompiler) :
+class MSVCCompiler (WindowsCCompiler) :
     """Concrete class that implements an interface to Microsoft Visual C++,
-       as defined by the CCompiler abstract class."""
-
+       as defined by the CCompiler abstract class.
+    """
     compiler_type = 'msvc'
 
-    # Just set this so CCompiler's constructor doesn't barf.  We currently
-    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
-    # as it really isn't necessary for this sort of single-compiler class.
-    # Would be nice to have a consistent interface with UnixCCompiler,
-    # though, so it's worth thinking about.
-    executables = {}
-
-    # Private class data (need to distinguish C from C++ source for compiler)
-    _c_extensions = ['.c']
-    _cpp_extensions = ['.cc', '.cpp', '.cxx']
-    _rc_extensions = ['.rc']
-    _mc_extensions = ['.mc']
-
-    # Needed for the filename generation methods provided by the
-    # base class, CCompiler.
-    src_extensions = (_c_extensions + _cpp_extensions +
-                      _rc_extensions + _mc_extensions)
-    res_extension = '.res'
-    obj_extension = '.obj'
-    static_lib_extension = '.lib'
-    shared_lib_extension = '.dll'
-    static_lib_format = shared_lib_format = '%s%s'
-    exe_extension = '.exe'
-
-
     def __init__ (self,
                   verbose=0,
                   dry_run=0,
                   force=0):
 
-        CCompiler.__init__ (self, verbose, dry_run, force)
+        WindowsCCompiler.__init__ (self, verbose, dry_run, force)
         versions = get_devstudio_versions ()
 
         if versions:
@@ -211,7 +186,7 @@
             self.mc   = find_exe("mc.exe", version)     # message compiler
             set_path_env_var ('lib', version)
             set_path_env_var ('include', version)
-            path=get_msvc_paths('path', version)
+            path = get_msvc_paths('path', version)
             try:
                 for p in string.split(os.environ['path'],';'):
                     path.append(p)
@@ -240,36 +215,6 @@
 
     # -- Worker methods ------------------------------------------------
 
-    def object_filenames (self,
-                          source_filenames,
-                          strip_dir=0,
-                          output_dir=''):
-        # Copied from ccompiler.py, extended to return .res as 'object'-file
-        # for .rc input file
-        if output_dir is None: output_dir = ''
-        obj_names = []
-        for src_name in source_filenames:
-            (base, ext) = os.path.splitext (src_name)
-            if ext not in self.src_extensions:
-                # Better to raise an exception instead of silently continuing
-                # and later complain about sources and targets having
-                # different lengths
-                raise CompileError ("Don't know how to compile %s" % src_name)
-            if strip_dir:
-                base = os.path.basename (base)
-            if ext in self._rc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
-            elif ext in self._mc_extensions:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.res_extension))
-            else:
-                obj_names.append (os.path.join (output_dir,
-                                                base + self.obj_extension))
-        return obj_names
-
-    # object_filenames ()
-
 
     def compile (self,
                  sources,
@@ -420,13 +365,13 @@
         
         lib_opts = gen_lib_options (self,
                                     library_dirs, runtime_library_dirs,
-                                    libraries)
+                                    libraries,debug)
         if output_dir is not None:
             output_filename = os.path.join (output_dir, output_filename)
 
         if self._need_link (objects, output_filename):
 
-            if target_desc == CCompiler.EXECUTABLE:
+            if target_desc == self.EXECUTABLE:
                 if debug:
                     ldflags = self.ldflags_shared_debug[1:]
                 else:
@@ -485,26 +430,11 @@
         raise DistutilsPlatformError, \
               "don't know how to set runtime library search path for MSVC++"
 
-    def library_option (self, lib):
-        return self.library_filename (lib)
-
-
-    def find_library_file (self, dirs, lib, debug=0):
-        # Prefer a debugging library if found (and requested), but deal
-        # with it if we don't have one.
-        if debug:
-            try_names = [lib + "_d", lib]
-        else:
-            try_names = [lib]
-        for dir in dirs:
-            for name in try_names:
-                libfile = os.path.join(dir, self.library_filename (name))
-                if os.path.exists(libfile):
-                    return libfile
+    def library_option (self, lib, debug=0):
+	if debug:
+	    return self.library_filename (lib + "_d")
         else:
-            # Oops, didn't find it in *any* of 'dirs'
-            return None
-
-    # find_library_file ()
+	    return self.library_filename (lib)
 
 # class MSVCCompiler
+
diff -BurN --exclude=*.pyc --minimal distutils.orig/distutils/windowsccompiler.py distutils.patched/distutils/windowsccompiler.py
--- distutils.orig/distutils/windowsccompiler.py	Thu Jan  1 01:00:00 1970
+++ distutils.patched/distutils/windowsccompiler.py	Thu Oct 19 14:42:32 2000
@@ -0,0 +1,223 @@
+"""distutils.windowsccompiler
+
+Contains WindowsCCompiler, which is the base class for all Windows compilers.
+
+This module is under development. 
+Currently it only implements some helper functions.
+In the future it might be also provide default implementations for
+the compile() and link() methods of the compiler classes.
+"""
+
+# created 2000/09/28, Rene Liebscher
+
+__revision__ = "$Id: windowsccompiler.py $"
+
+
+from distutils.ccompiler import CCompiler
+import os
+
+
+class WindowsCCompiler(CCompiler) :
+    """Abstract class that serves as base class for all
+    Windows compilers.
+    """
+
+    compiler_type = 'windows'
+
+    # Just set this so CCompiler's constructor doesn't barf.  We currently
+    # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+    # as it really isn't necessary for this sort of single-compiler class.
+    # Would be nice to have a consistent interface with UnixCCompiler,
+    # though, so it's worth thinking about.
+    executables = {}
+
+    # Private class data (need to distinguish C from C++ source for compiler)
+    _c_extensions = ['.c']
+    _cpp_extensions = ['.cc', '.cpp', '.cxx']
+    _rc_extensions = ['.rc'] # resource source files
+    _mc_extensions = ['.mc'] # messages source files
+    _rc_obj_extension = '.res' # to what are compiles rc files
+    _mc_obj_extension = '.res' # to what are compiles rc files
+    # res-files go for most compilers to the linker, but gcc compiles them
+    # to a real object file in the compile() method
+    # so you have to support res-files as source files
+    _passthrough_extensions = ['.res'] # files not to compile
+
+    # Needed for the filename generation methods provided by the
+    # base class, CCompiler. ( object_filenames() )
+    src_extensions = ( _c_extensions + _cpp_extensions
+                     + _rc_extensions + _mc_extensions
+                     + _passthrough_extensions )
+
+    obj_extension = '.obj'
+    static_lib_extension = '.lib'
+    shared_lib_extension = '.dll'
+    static_lib_format = shared_lib_format = '%s%s'
+    exe_extension = '.exe'
+
+
+    def __init__ (self,
+                  verbose=0,
+                  dry_run=0,
+                  force=0):
+
+        CCompiler.__init__ (self, verbose, dry_run, force)
+
+        # programs to use
+        self.cc = None      # C/C++ complier
+        self.linker = None  # linker
+        self.lib = None     # librarian
+        self.rc = None      # resource compiler
+        self.mc = None      # message compiler
+
+    # __init__ ()
+
+
+    # -- Miscellaneous methods -----------------------------------------
+
+    # overwrite the one from CCompiler to support rc, mc and res-files
+    def object_filenames (self,
+                          source_filenames,
+                          strip_dir=0,
+                          output_dir=''):
+        return object_filenames(self,source_filenames,strip_dir,output_dir)
+    # object_filenames ()
+
+
+    def find_library_file (self, dirs, lib, debug=0):
+        return find_library_file(self, dirs, lib, debug)
+    # find_library_file ()
+
+
+# WindowsCCompiler
+
+
+
+# plain functions on module level
+
+# these functions can be used from outside WindowsCCompiler,
+# especially from CygwinCCompiler
+
+# overwrite method from CCompiler to support rc, mc and res-files
+def object_filenames (compiler, source_filenames, strip_dir=0, output_dir=''):
+    # Copied from ccompiler.py, extended to return .res as 'object'-file
+    # for .rc input file
+    if output_dir is None: output_dir = ''
+    obj_names = []
+    for src_name in source_filenames:
+        # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+        (base, ext) = os.path.splitext (os.path.normcase(src_name))
+        if ext not in compiler.src_extensions:
+            # Better to raise an exception instead of silently continuing
+            # and later complain about sources and targets having
+            # different lengths
+            raise CompileError ("Don't know how to compile %s" % src_name)
+        if strip_dir:
+            base = os.path.basename (base)
+        if ext in compiler._passthrough_extensions:
+            # these can go unchanged                
+            obj_names.append (os.path.join (output_dir, base + ext))
+        elif ext in compiler._rc_extensions:
+            # these need to be compiled to .res-files
+            obj_names.append (os.path.join (output_dir,
+                                            base + compiler._rc_obj_extension))
+        elif ext in compiler._mc_extensions:
+            obj_names.append (os.path.join (output_dir,
+                                            base + compiler._mc_obj_extension))
+        else:
+            obj_names.append (os.path.join (output_dir,
+                                                base + compiler.obj_extension))
+    return obj_names
+
+# object_filenames ()
+
+
+def find_library_file (compiler, dirs, lib, debug=0):
+    # List of effective library names to try, in order of preference:
+    # xxx_????.lib is better than xxx.lib
+    # and xxx_d.lib is better than xxx.lib if debug is set
+    # (???? = compiler_type)
+    # The "_????" suffix is to handle a Python installation for people
+    # with multiple compilers (primarily Distutils hackers, I suspect
+    # ;-).  The idea is they'd have one static library for each
+    # compiler they care about, since (almost?) every Windows compiler
+    # seems to have a different format for static libraries.
+    if debug:
+        dlib = (lib + "_d")
+        try_names = (dlib + "_" + compiler.compiler_type, 
+                      lib + "_"  + compiler.compiler_type, dlib, lib)                      
+    else:
+        try_names = (lib + "_" + compiler.compiler_type, lib)
+    for dir in dirs:
+        for name in try_names:
+            libfile = os.path.join(dir, compiler.library_filename(name))   
+            if os.path.exists(libfile):
+                return libfile
+    # Oops, didn't find it in *any* of 'dirs'
+    return None
+
+# find_library_file ()
+
+
+# Because not all windows compilers are configured in Python's config.h file by
+# default, we do here some simple test to find out if it could work or if we
+# should warn the user that compiling could fail
+
+COMPATIBILITY_OK = "ok"
+COMPATIBILITY_NOTOK = "not ok"
+COMPATIBILITY_UNCERTAIN = "uncertain"
+
+def check_compatibility(compiler_name="FOO",compiler_macro="__FOO__"):
+
+    """Check if the current Python installation (specifically, config.h)
+    appears amenable to building extensions with FOO.  Returns a tuple
+    (status, details), where 'status' is one of the following constants:
+      COMPATIBILITY_OK
+        all is well, go ahead and compile
+      COMPATIBILITY_NOTOK
+        doesn't look good
+      COMPATIBILITY_UNCERTAIN
+        not sure -- unable to read config.h
+    'details' is a human-readable string explaining the situation.
+
+    Note there are two ways to conclude "OK": either 'sys.version' contains
+    the string "FOO" (implying that this Python was built with FOO), or the
+    installed "config.h" contains the string "__FOO__".
+
+    compiler_name is the name as it could be found in sys.version.
+    compiler_macro is the macro name which would be used to start a compiler
+                 specific section in config.h.
+    """
+
+    from distutils import sysconfig
+    import string,sys
+    # if sys.version contains FOO then python was compiled with
+    # FOO, and the config.h file should be OK
+    if string.find(sys.version,compiler_name) >= 0:
+        return (COMPATIBILITY_OK, "sys.version mentions '%s'" % compiler_name)
+    
+    fn = sysconfig.get_config_h_filename()
+    try:
+        # It would probably better to read single lines to search.
+        # But we do this only once, and it is fast enough 
+        f = open(fn)
+        s = f.read()
+        f.close()
+        
+    except IOError, exc:
+        # if we can't read this file, we cannot say it is wrong
+        # the compiler will complain later about this file as missing
+        return (COMPATIBILITY_UNCERTAIN,
+                "couldn't read '%s': %s" % (fn, exc.strerror))
+
+    else:
+        # "config.h" contains an "#ifdef __FOO__" or something similar
+        if string.find(s,compiler_macro) >= 0:
+            return (COMPATIBILITY_OK, 
+                            "'%s' mentions '%s'" % (fn,compiler_macro))
+        else:
+            return (COMPATIBILITY_NOTOK, 
+                            "'%s' does not mention '%s'" % (fn,compiler_macro))
+
+# check_compatibility
+

--------------731E085DA5F2F5144B5381BE
Content-Type: text/plain; charset=us-ascii;
 name="archive_util.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="archive_util.patch"

diff -BurN --minimal --exclude=*.pyc distutils.orig/distutils/archive_util.py distutils.patched/distutils/archive_util.py
--- distutils.orig/distutils/archive_util.py	Tue Sep 26 11:28:52 2000
+++ distutils.patched/distutils/archive_util.py	Sat Oct  7 09:55:25 2000
@@ -7,7 +7,7 @@
 
 __revision__ = "$Id: archive_util.py,v 1.9 2000/09/26 02:13:49 gward Exp $"
 
-import os
+import os,string
 from distutils.errors import DistutilsExecError
 from distutils.spawn import spawn
 from distutils.dir_util import mkpath
@@ -143,7 +143,7 @@
     if root_dir is not None:
         if verbose:
             print "changing into '%s'" % root_dir
-        base_name = os.path.abspath(base_name)
+        base_name = path_relative_to(base_name,root_dir)
         if not dry_run:
             os.chdir(root_dir)
 
@@ -166,8 +166,37 @@
     if root_dir is not None:
         if verbose:
             print "changing back to '%s'" % save_cwd
+        filename = path_relative_to(filename,save_cwd)
         os.chdir(save_cwd)
 
     return filename
 
 # make_archive ()
+
+def path_relative_to(base_name,root_dir):
+    # save the file name 
+    (base_name,file_name) = os.path.split(os.path.abspath(base_name))
+    root_dir = os.path.abspath(root_dir)
+    # is there a common prefix?
+    # win32: (C:\foo,C:\bar)=> "C:\" or (C:\...,D:\...) => ""
+    #  the second case has to result in an absolute path as return value
+    # unix: at least the root slash is common  
+    common_prefix = len(os.path.commonprefix([base_name,root_dir]))
+    # any common prefix (win32: both on same drive) 
+    base_name = base_name[common_prefix:]
+    root_dir = root_dir[common_prefix:]
+    if (base_name <> "") and (base_name[0] == os.sep):
+        # test for "/foo..."
+        # could happen for common_prefix(["/abc/foo...","/abc/foo"])
+        base_name = base_name[1:]
+    if (root_dir <> "") and (root_dir[0] == os.sep):
+        # test for "/foo..."
+        # could happen for common_prefix(["/abc","/abc/foo..."])
+        root_dir = root_dir[1:]
+    if (common_prefix > 0) and (root_dir <> ""):
+        number_of_pardirs = len(string.split(root_dir,os.sep))
+        par_dirs = string.join([os.pardir]*number_of_pardirs,os.sep)
+    else:
+        # no common prefix (only possible on win32)
+        par_dirs = ""
+    return os.path.join(par_dirs,base_name,file_name)
\ No newline at end of file

--------------731E085DA5F2F5144B5381BE--