10c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi"""distutils.command.build_py
20c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
30c5958b1636c47ed7c284f859c8e805fd06a0e6Bill YiImplements the Distutils 'build_py' command."""
40c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
50c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi__revision__ = "$Id$"
60c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
70c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport os
80c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiimport sys
90c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom glob import glob
100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils.core import Command
120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils.errors import DistutilsOptionError, DistutilsFileError
130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils.util import convert_path
140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yifrom distutils import log
150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yiclass build_py(Command):
170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    description = "\"build\" pure Python modules (copy to build directory)"
190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    user_options = [
210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('build-lib=', 'd', "directory to \"build\" (copy) to"),
220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('compile', 'c', "compile .py to .pyc"),
230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('no-compile', None, "don't compile .py files [default]"),
240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('optimize=', 'O',
250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi         "also compile with optimization: -O1 for \"python -O\", "
260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi         "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ]
290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    boolean_options = ['compile', 'force']
310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    negative_opt = {'no-compile' : 'compile'}
320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def initialize_options(self):
340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.build_lib = None
350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.py_modules = None
360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.package = None
370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.package_data = None
380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.package_dir = None
390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.compile = 0
400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.optimize = 0
410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.force = None
420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def finalize_options(self):
440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.set_undefined_options('build',
450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                   ('build_lib', 'build_lib'),
460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                                   ('force', 'force'))
470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Get the distribution options that are aliases for build_py
490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # options -- list of packages and list of modules.
500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.packages = self.distribution.packages
510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.py_modules = self.distribution.py_modules
520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.package_data = self.distribution.package_data
530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.package_dir = {}
540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.distribution.package_dir:
550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for name, path in self.distribution.package_dir.items():
560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.package_dir[name] = convert_path(path)
570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.data_files = self.get_data_files()
580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Ick, copied straight from install_lib.py (fancy_getopt needs a
600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # type system!  Hell, *everything* needs a type system!!!)
610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not isinstance(self.optimize, int):
620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            try:
630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.optimize = int(self.optimize)
640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                assert 0 <= self.optimize <= 2
650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            except (ValueError, AssertionError):
660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                raise DistutilsOptionError("optimize must be 0, 1, or 2")
670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def run(self):
690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX copy_file by default preserves atime and mtime.  IMHO this is
700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # the right thing to do, but perhaps it should be an option -- in
710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # particular, a site administrator might want installed files to
720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # reflect the time of installation rather than the last
730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # modification time before the installed release.
740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX copy_file by default preserves mode, which appears to be the
760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # wrong thing to do: if a file is read-only in the working
770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # directory, we want it to be installed read/write so that the next
780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # installation of the same module distribution can overwrite it
790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # without problems.  (This might be a Unix-specific issue.)  Thus
800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # we turn off 'preserve_mode' when copying to the build directory,
810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # since the build directory is supposed to be exactly what the
820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # installation will look like (ie. we preserve mode when
830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # installing).
840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Two options control which modules will be installed: 'packages'
860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # and 'py_modules'.  The former lets us work with whole packages, not
870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # specifying individual modules at all; the latter is for
880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # specifying modules one-at-a-time.
890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.py_modules:
910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.build_modules()
920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.packages:
930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.build_packages()
940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.build_package_data()
950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.byte_compile(self.get_outputs(include_bytecode=0))
970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_data_files(self):
990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
1000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        data = []
1010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not self.packages:
1020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return data
1030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for package in self.packages:
1040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Locate package source directory
1050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            src_dir = self.get_package_dir(package)
1060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Compute package build directory
1080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            build_dir = os.path.join(*([self.build_lib] + package.split('.')))
1090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Length of path to strip from found files
1110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            plen = 0
1120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if src_dir:
1130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                plen = len(src_dir)+1
1140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Strip directory from globbed filenames
1160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            filenames = [
1170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                file[plen:] for file in self.find_data_files(package, src_dir)
1180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                ]
1190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            data.append((package, src_dir, build_dir, filenames))
1200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return data
1210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def find_data_files(self, package, src_dir):
1230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Return filenames for package's data files in 'src_dir'"""
1240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        globs = (self.package_data.get('', [])
1250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                 + self.package_data.get(package, []))
1260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        files = []
1270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for pattern in globs:
1280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Each pattern has to be converted to a platform-specific path
1290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            filelist = glob(os.path.join(src_dir, convert_path(pattern)))
1300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Files that match more than one pattern are only added once
1310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            files.extend([fn for fn in filelist if fn not in files])
1320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return files
1330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def build_package_data(self):
1350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Copy data files into build directory"""
1360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for package, src_dir, build_dir, filenames in self.data_files:
1370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for filename in filenames:
1380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                target = os.path.join(build_dir, filename)
1390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.mkpath(os.path.dirname(target))
1400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.copy_file(os.path.join(src_dir, filename), target,
1410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                               preserve_mode=False)
1420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_package_dir(self, package):
1440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Return the directory, relative to the top of the source
1450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           distribution, where package 'package' should be found
1460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi           (at least according to the 'package_dir' option, if any)."""
1470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        path = package.split('.')
1490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not self.package_dir:
1510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if path:
1520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                return os.path.join(*path)
1530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                return ''
1550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
1560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            tail = []
1570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            while path:
1580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                try:
1590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    pdir = self.package_dir['.'.join(path)]
1600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                except KeyError:
1610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    tail.insert(0, path[-1])
1620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    del path[-1]
1630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
1640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    tail.insert(0, pdir)
1650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    return os.path.join(*tail)
1660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
1670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # Oops, got all the way through 'path' without finding a
1680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # match in package_dir.  If package_dir defines a directory
1690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # for the root (nameless) package, then fallback on it;
1700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # otherwise, we might as well have not consulted
1710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # package_dir at all, as we just use the directory implied
1720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # by 'tail' (which should be the same as the original value
1730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                # of 'path' at this point).
1740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                pdir = self.package_dir.get('')
1750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if pdir is not None:
1760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    tail.insert(0, pdir)
1770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if tail:
1790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    return os.path.join(*tail)
1800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                else:
1810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    return ''
1820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def check_package(self, package, package_dir):
1840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Empty dir name means current directory, which we can probably
1850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # assume exists.  Also, os.path.exists and isdir don't know about
1860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # my "empty string means current dir" convention, so we have to
1870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # circumvent them.
1880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if package_dir != "":
1890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not os.path.exists(package_dir):
1900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                raise DistutilsFileError(
1910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                      "package directory '%s' does not exist" % package_dir)
1920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not os.path.isdir(package_dir):
1930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                raise DistutilsFileError(
1940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                       "supposed package directory '%s' exists, "
1950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                       "but is not a directory" % package_dir)
1960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
1970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Require __init__.py for all but the "root package"
1980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if package:
1990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            init_py = os.path.join(package_dir, "__init__.py")
2000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if os.path.isfile(init_py):
2010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                return init_py
2020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
2030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                log.warn(("package init file '%s' not found " +
2040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                          "(or not a regular file)"), init_py)
2050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Either not in a package at all (__init__.py not expected), or
2070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # __init__.py doesn't exist -- so don't return the filename.
2080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return None
2090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def check_module(self, module, module_file):
2110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if not os.path.isfile(module_file):
2120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            log.warn("file %s (for module %s) not found", module_file, module)
2130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return False
2140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        else:
2150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return True
2160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def find_package_modules(self, package, package_dir):
2180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.check_package(package, package_dir)
2190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        module_files = glob(os.path.join(package_dir, "*.py"))
2200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        modules = []
2210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        setup_script = os.path.abspath(self.distribution.script_name)
2220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for f in module_files:
2240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            abs_f = os.path.abspath(f)
2250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if abs_f != setup_script:
2260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                module = os.path.splitext(os.path.basename(f))[0]
2270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                modules.append((package, module, f))
2280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            else:
2290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.debug_print("excluding %s" % setup_script)
2300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return modules
2310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def find_modules(self):
2330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Finds individually-specified Python modules, ie. those listed by
2340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        module name in 'self.py_modules'.  Returns a list of tuples (package,
2350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        module_base, filename): 'package' is a tuple of the path through
2360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        package-space to the module; 'module_base' is the bare (no
2370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        packages, no dots) module name, and 'filename' is the path to the
2380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        ".py" file (relative to the distribution root) that implements the
2390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        module.
2400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """
2410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Map package names to tuples of useful info about the package:
2420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #    (package_dir, checked)
2430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # package_dir - the directory where we'll find source files for
2440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #   this package
2450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # checked - true if we have checked that the package directory
2460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #   is valid (exists, contains __init__.py, ... ?)
2470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        packages = {}
2480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # List of (package, module, filename) tuples to return
2500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        modules = []
2510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # We treat modules-in-packages almost the same as toplevel modules,
2530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # just the "package" for a toplevel is empty (either an empty
2540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # string or empty list, depending on context).  Differences:
2550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        #   - don't check for __init__.py in directory for empty package
2560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for module in self.py_modules:
2570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            path = module.split('.')
2580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            package = '.'.join(path[0:-1])
2590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            module_base = path[-1]
2600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            try:
2620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                (package_dir, checked) = packages[package]
2630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            except KeyError:
2640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                package_dir = self.get_package_dir(package)
2650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                checked = 0
2660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not checked:
2680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                init_py = self.check_package(package, package_dir)
2690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                packages[package] = (package_dir, 1)
2700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if init_py:
2710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    modules.append((package, "__init__", init_py))
2720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # XXX perhaps we should also check for just .pyc files
2740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # (so greedy closed-source bastards can distribute Python
2750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # modules too)
2760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            module_file = os.path.join(package_dir, module_base + ".py")
2770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if not self.check_module(module, module_file):
2780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                continue
2790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            modules.append((package, module_base, module_file))
2810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return modules
2830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
2840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def find_all_modules(self):
2850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        """Compute the list of all modules that will be built, whether
2860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        they are specified one-module-at-a-time ('self.py_modules') or
2870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        by whole packages ('self.packages').  Return a list of tuples
2880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        (package, module, module_file), just like 'find_modules()' and
2890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        'find_package_modules()' do."""
2900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        modules = []
2910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.py_modules:
2920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            modules.extend(self.find_modules())
2930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.packages:
2940c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for package in self.packages:
2950c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                package_dir = self.get_package_dir(package)
2960c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                m = self.find_package_modules(package, package_dir)
2970c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                modules.extend(m)
2980c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return modules
2990c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3000c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_source_files(self):
3010c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return [module[-1] for module in self.find_all_modules()]
3020c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3030c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_module_outfile(self, build_dir, package, module):
3040c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        outfile_path = [build_dir] + list(package) + [module + ".py"]
3050c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return os.path.join(*outfile_path)
3060c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3070c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def get_outputs(self, include_bytecode=1):
3080c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        modules = self.find_all_modules()
3090c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        outputs = []
3100c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for (package, module, module_file) in modules:
3110c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            package = package.split('.')
3120c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            filename = self.get_module_outfile(self.build_lib, package, module)
3130c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            outputs.append(filename)
3140c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            if include_bytecode:
3150c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if self.compile:
3160c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    outputs.append(filename + "c")
3170c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                if self.optimize > 0:
3180c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                    outputs.append(filename + "o")
3190c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3200c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        outputs += [
3210c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            os.path.join(build_dir, filename)
3220c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for package, src_dir, build_dir, filenames in self.data_files
3230c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for filename in filenames
3240c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            ]
3250c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3260c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return outputs
3270c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3280c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def build_module(self, module, module_file, package):
3290c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if isinstance(package, str):
3300c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            package = package.split('.')
3310c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        elif not isinstance(package, (list, tuple)):
3320c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            raise TypeError(
3330c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                  "'package' must be a string (dot-separated), list, or tuple")
3340c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3350c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # Now put the module source file into the "build" area -- this is
3360c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # easy, we just copy it somewhere under self.build_lib (the build
3370c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # directory for Python source).
3380c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        outfile = self.get_module_outfile(self.build_lib, package, module)
3390c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        dir = os.path.dirname(outfile)
3400c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        self.mkpath(dir)
3410c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        return self.copy_file(module_file, outfile, preserve_mode=0)
3420c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3430c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def build_modules(self):
3440c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        modules = self.find_modules()
3450c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for (package, module, module_file) in modules:
3460c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3470c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Now "build" the module -- ie. copy the source file to
3480c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # self.build_lib (the build directory for Python source).
3490c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # (Actually, it gets copied to the directory for this package
3500c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # under self.build_lib.)
3510c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.build_module(module, module_file, package)
3520c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3530c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def build_packages(self):
3540c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        for package in self.packages:
3550c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3560c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Get list of (package, module, module_file) tuples based on
3570c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # scanning the package directory.  'package' is only included
3580c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # in the tuple so that 'find_modules()' and
3590c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # 'find_package_tuples()' have a consistent interface; it's
3600c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # ignored here (apart from a sanity check).  Also, 'module' is
3610c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # the *unqualified* module name (ie. no dots, no package -- we
3620c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # already know its package!), and 'module_file' is the path to
3630c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # the .py file, relative to the current directory
3640c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # (ie. including 'package_dir').
3650c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            package_dir = self.get_package_dir(package)
3660c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            modules = self.find_package_modules(package, package_dir)
3670c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3680c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # Now loop over the modules we found, "building" each one (just
3690c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            # copy it to self.build_lib).
3700c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            for (package_, module, module_file) in modules:
3710c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                assert package == package_
3720c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                self.build_module(module, module_file, package)
3730c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3740c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi    def byte_compile(self, files):
3750c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if sys.dont_write_bytecode:
3760c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            self.warn('byte-compiling is disabled, skipping.')
3770c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            return
3780c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3790c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        from distutils.util import byte_compile
3800c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        prefix = self.build_lib
3810c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if prefix[-1] != os.sep:
3820c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            prefix = prefix + os.sep
3830c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3840c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # XXX this code is essentially the same as the 'byte_compile()
3850c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # method of the "install_lib" command, except for the determination
3860c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        # of the 'prefix' string.  Hmmm.
3870c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi
3880c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.compile:
3890c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            byte_compile(files, optimize=0,
3900c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         force=self.force, prefix=prefix, dry_run=self.dry_run)
3910c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi        if self.optimize > 0:
3920c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi            byte_compile(files, optimize=self.optimize,
3930c5958b1636c47ed7c284f859c8e805fd06a0e6Bill Yi                         force=self.force, prefix=prefix, dry_run=self.dry_run)
394