[Distutils] Make setup.py work with all versions of Distutils

Bastian Kleineidam calvin@cs.uni-sb.de
Tue Dec 5 12:20:01 2000

Hello developers,

some of you may have noticed that the Distutils allow customized command
classes by inheriting them. This is extremely powerful especially when
it comes to compatibility fixes.

I'll summarize all of my custom functions which will work around a
number of unknown/known/fixed bugs in the Distutils.
If you want to be compatible with older versions of the Distutils, you 
can include them in your setup.py file.

1) Distutils traceback when DISTUTILS_DEBUG is set. This is a known 
   but unfixed bug.
   Just override the dump_dirs function in install.py with your own:

from distutils.core import DEBUG
import string

class MyInstall(install):
    def dump_dirs (self, msg):
        if DEBUG:
            from distutils.fancy_getopt import longopt_xlate
            print msg + ":"
            for opt in self.user_options:
                opt_name = opt[0]
                if opt_name[-1] == "=":
                    opt_name = opt_name[0:-1]
                if self.negative_opt.has_key(opt_name):
                    opt_name = string.translate(self.negative_opt[opt_name],
                    val = not getattr(self, opt_name)
                    opt_name = string.translate(opt_name, longopt_xlate)
                    val = getattr(self, opt_name)
                print "  %s: %s" % (opt_name, val)

2) the 'config' target does not initialize all values. This is a known
   and fixed bug (fixed in version 1.0.1):
import string,os
from types import StringType

class MyConfig(config):
    def finalize_options(self):
        """fix up types of option values. This is cut'n'paste from
	   Distutils 1.0.1"""
        if self.include_dirs is None:
            self.include_dirs = self.distribution.include_dirs or []
        elif type(self.include_dirs) is StringType:
            self.include_dirs = string.split(self.include_dirs, os.pathsep)

        if self.libraries is None:
            self.libraries = []
        elif type(self.libraries) is StringType:
            self.libraries = [self.libraries]

        if self.library_dirs is None:
            self.library_dirs = []
        elif type(self.library_dirs) is StringType:
            self.library_dirs = string.split(self.library_dirs, os.pathsep)

3) Use this only if you have man(1) files and you want to build RPM
   The bdist_rpm command cannot handle man pages.
   Whats happening here? The rpm command calls
   python setup.py install --root=... --record=INSTALLED_FILES
   In INSTALLED_FILES are now all installed files listed inclusive a
   man page, for example ./usr/man/man1/myprog.1
   But this man page gets compressed by rpm and we have now
   This makes subsequent 'rpm' commands fail which are based on the
   filenames found in INSTALLED_FILES.
   Below you see a quick and very dirty hack: just append ".gz" to all
   man file names in the INSTALLED_FILES index.
   Of course the proper solution would be to define multiple handler
   classes for all kinds of documentation (man pages, info pages, html
   pages ...). But thats a long way to go.

from types import StringType
import os,re

class LCInstallData(install_data):
    """My own data installer to handle .man pages"""
    def run (self):
        for f in self.data_files:
            if type(f) == StringType:
                # it's a simple file, so copy it
                if self.warn_dir:
                    self.warn("setup script did not provide a directory for "
                              "'%s' -- installing right in '%s'" %
                              (f, self.install_dir))
                self._install_file(f, self.install_dir)
                # it's a tuple with path to install to and a list of files
                dir = f[0]
                if not os.path.isabs(dir):
                    dir = os.path.join(self.install_dir, dir)
                elif self.root:
                    dir = change_root(self.root, dir)
                for data in f[1]:
                    self._install_file(data, dir)

    def _install_file(self, filename, dirname):
        (out, _) = self.copy_file(filename, dirname)
        # match for man pages .[0-9]
        if re.search(r'/man/.+\.\d$', out):
            out = out+".gz"