build_py.py revision e557f3556f1f87825e00a018eabf837c4c55f7d5
113ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward"""distutils.command.build_py
213ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
313ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg WardImplements the Distutils 'build_py' command."""
413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
5d448f66317130555cfc157683c743b3d004374c4Andrew M. Kuchling# This module should be kept compatible with Python 1.5.2.
6d448f66317130555cfc157683c743b3d004374c4Andrew M. Kuchling
73ce77fd05ed00168f618b63401d770ccc4f04b09Greg Ward__revision__ = "$Id$"
813ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
99b45443c1bdf99b0f27b12baf06fea475b60e145Greg Wardimport sys, string, os
1017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Wardfrom types import *
1117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Wardfrom glob import glob
1217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
1313ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Wardfrom distutils.core import Command
1413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Wardfrom distutils.errors import *
150c350bfad0ec9350aec57e37962b1aadb8492173Thomas Hellerfrom distutils.util import convert_path
16cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hyltonfrom distutils import log
1713ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
181993f9ad0e2cf53c8dc441cbbb44eb2e3a190538Greg Wardclass build_py (Command):
1913ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
2037bc81505379facad85a7c6ff273de0201f28656Greg Ward    description = "\"build\" pure Python modules (copy to build directory)"
2137bc81505379facad85a7c6ff273de0201f28656Greg Ward
22bbeceeaf9a5edf878154b17a6a94403a26822d51Greg Ward    user_options = [
23e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward        ('build-lib=', 'd', "directory to \"build\" (copy) to"),
2473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        ('compile', 'c', "compile .py to .pyc"),
2573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        ('no-compile', None, "don't compile .py files [default]"),
2673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        ('optimize=', 'O',
2773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward         "also compile with optimization: -O1 for \"python -O\", "
2873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward         "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
296a2035d76ba9572ccd2ed61d59df46e2bdcb74edGreg Ward        ('force', 'f', "forcibly build everything (ignore file timestamps)"),
30bbeceeaf9a5edf878154b17a6a94403a26822d51Greg Ward        ]
3113ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
3273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward    boolean_options = ['compile', 'force']
3373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward    negative_opt = {'no-compile' : 'compile'}
3499b032eaf24e1887f6451eceea7f1e24ced05a0fGreg Ward
3513ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
36e01149cbe83778a5cf872a6b429ff33179b7cdcbGreg Ward    def initialize_options (self):
37e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward        self.build_lib = None
386f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        self.py_modules = None
3971eb8644d7e27fd379a2cf78c509155bdb179332Greg Ward        self.package = None
4017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        self.package_dir = None
4173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        self.compile = 0
4273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        self.optimize = 0
43c41d6b35a9de3dd9e3057a0db9a83b182e792f79Greg Ward        self.force = None
4413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
45e01149cbe83778a5cf872a6b429ff33179b7cdcbGreg Ward    def finalize_options (self):
46cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        self.set_undefined_options('build',
47cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                                   ('build_lib', 'build_lib'),
48cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                                   ('force', 'force'))
4917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
5017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # Get the distribution options that are aliases for build_py
5117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # options -- list of packages and list of modules.
5217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        self.packages = self.distribution.packages
536f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        self.py_modules = self.distribution.py_modules
540c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller        self.package_dir = {}
550c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller        if self.distribution.package_dir:
560c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller            for name, path in self.distribution.package_dir.items():
570c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller                self.package_dir[name] = convert_path(path)
5813ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
5973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        # Ick, copied straight from install_lib.py (fancy_getopt needs a
6073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        # type system!  Hell, *everything* needs a type system!!!)
6173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        if type(self.optimize) is not IntType:
6273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            try:
6373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                self.optimize = int(self.optimize)
6473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                assert 0 <= self.optimize <= 2
6573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            except (ValueError, AssertionError):
6673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                raise DistutilsOptionError, "optimize must be 0, 1, or 2"
6713ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
6813ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward    def run (self):
6913ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
709b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # XXX copy_file by default preserves atime and mtime.  IMHO this is
719b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # the right thing to do, but perhaps it should be an option -- in
729b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # particular, a site administrator might want installed files to
739b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # reflect the time of installation rather than the last
749b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # modification time before the installed release.
759b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward
769b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # XXX copy_file by default preserves mode, which appears to be the
779b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # wrong thing to do: if a file is read-only in the working
789b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # directory, we want it to be installed read/write so that the next
799b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # installation of the same module distribution can overwrite it
809b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # without problems.  (This might be a Unix-specific issue.)  Thus
819b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # we turn off 'preserve_mode' when copying to the build directory,
829b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # since the build directory is supposed to be exactly what the
839b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # installation will look like (ie. we preserve mode when
849b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward        # installing).
8513ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward
8617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # Two options control which modules will be installed: 'packages'
876f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        # and 'py_modules'.  The former lets us work with whole packages, not
8817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # specifying individual modules at all; the latter is for
89e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling        # specifying modules one-at-a-time.
90e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling
916f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        if self.py_modules:
92cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            self.build_modules()
93e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling        if self.packages:
94cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            self.build_packages()
9517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
9673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        self.byte_compile(self.get_outputs(include_bytecode=0))
9773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
9817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # run ()
9921d45356b8a22bb3091a97f50b919b57798675f5Fred Drake
10017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
10117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    def get_package_dir (self, package):
10217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        """Return the directory, relative to the top of the source
10317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward           distribution, where package 'package' should be found
10417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward           (at least according to the 'package_dir' option, if any)."""
10517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
106cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        path = string.split(package, '.')
10717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
10817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        if not self.package_dir:
109631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward            if path:
110cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                return apply(os.path.join, path)
111631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward            else:
112631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward                return ''
11317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        else:
11417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            tail = []
11517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            while path:
11617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                try:
117cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                    pdir = self.package_dir[string.join(path, '.')]
11817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                except KeyError:
119cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                    tail.insert(0, path[-1])
12017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                    del path[-1]
12117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                else:
122cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                    tail.insert(0, pdir)
123cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                    return apply(os.path.join, tail)
12413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward            else:
1258bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # Oops, got all the way through 'path' without finding a
1268bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # match in package_dir.  If package_dir defines a directory
1278bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # for the root (nameless) package, then fallback on it;
1288bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # otherwise, we might as well have not consulted
1298bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # package_dir at all, as we just use the directory implied
1308bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # by 'tail' (which should be the same as the original value
1318bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                # of 'path' at this point).
1328bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                pdir = self.package_dir.get('')
1338bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                if pdir is not None:
1348bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                    tail.insert(0, pdir)
1358bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward
136631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward                if tail:
137cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                    return apply(os.path.join, tail)
138631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward                else:
139631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward                    return ''
14017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
14117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # get_package_dir ()
14217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
14317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
14417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    def check_package (self, package, package_dir):
14517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
14617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # Empty dir name means current directory, which we can probably
14717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # assume exists.  Also, os.path.exists and isdir don't know about
14817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # my "empty string means current dir" convention, so we have to
14917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # circumvent them.
15017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        if package_dir != "":
151cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            if not os.path.exists(package_dir):
15217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                raise DistutilsFileError, \
15317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                      "package directory '%s' does not exist" % package_dir
154cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            if not os.path.isdir(package_dir):
15556359f591b361f41f661a14e5ed129bf8f22fa87Greg Ward                raise DistutilsFileError, \
15617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                      ("supposed package directory '%s' exists, " +
15717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                       "but is not a directory") % package_dir
15817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
15917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # Require __init__.py for all but the "root package"
160631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward        if package:
161cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            init_py = os.path.join(package_dir, "__init__.py")
162cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            if os.path.isfile(init_py):
1638bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                return init_py
1648bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward            else:
165cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton                log.warn(("package init file '%s' not found " +
166cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton                          "(or not a regular file)"), init_py)
1678bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward
1688bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        # Either not in a package at all (__init__.py not expected), or
1698bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        # __init__.py doesn't exist -- so don't return the filename.
17011a52708f7f2223b335734e584e2ba30eae74a5fJeremy Hylton        return None
17121d45356b8a22bb3091a97f50b919b57798675f5Fred Drake
17217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # check_package ()
17317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
17417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
17517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    def check_module (self, module, module_file):
176cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        if not os.path.isfile(module_file):
177cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton            log.warn("file %s (for module %s) not found", module_file, module)
178a863270f0403e537f2de6cc665cf172be673ca48Tim Peters            return 0
17917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        else:
180a863270f0403e537f2de6cc665cf172be673ca48Tim Peters            return 1
18117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
18217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # check_module ()
18317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
18417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
1852a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward    def find_package_modules (self, package, package_dir):
186cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        self.check_package(package, package_dir)
187cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        module_files = glob(os.path.join(package_dir, "*.py"))
1888b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        modules = []
1899821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward        setup_script = os.path.abspath(self.distribution.script_name)
1909b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward
19117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        for f in module_files:
192cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            abs_f = os.path.abspath(f)
1939b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward            if abs_f != setup_script:
194cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                module = os.path.splitext(os.path.basename(f))[0]
195cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                modules.append((package, module, f))
1969821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward            else:
1979821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward                self.debug_print("excluding %s" % setup_script)
1988b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        return modules
19917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
20017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
2012a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward    def find_modules (self):
2028bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        """Finds individually-specified Python modules, ie. those listed by
2036f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        module name in 'self.py_modules'.  Returns a list of tuples (package,
2048bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        module_base, filename): 'package' is a tuple of the path through
2058bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        package-space to the module; 'module_base' is the bare (no
2068bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        packages, no dots) module name, and 'filename' is the path to the
2078bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        ".py" file (relative to the distribution root) that implements the
2088bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        module.
2098bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        """
2108bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward
21117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # Map package names to tuples of useful info about the package:
21217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        #    (package_dir, checked)
21317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # package_dir - the directory where we'll find source files for
21417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        #   this package
21517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # checked - true if we have checked that the package directory
21617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        #   is valid (exists, contains __init__.py, ... ?)
21717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        packages = {}
21817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
2198bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward        # List of (package, module, filename) tuples to return
2202a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        modules = []
2212a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
22217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # We treat modules-in-packages almost the same as toplevel modules,
22317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # just the "package" for a toplevel is empty (either an empty
22417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        # string or empty list, depending on context).  Differences:
22517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        #   - don't check for __init__.py in directory for empty package
22617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
2276f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        for module in self.py_modules:
228cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            path = string.split(module, '.')
229c0fe82ca26b1da22e35f2d0676f09795d052e4f0Greg Ward            package = string.join(path[0:-1], '.')
2302a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward            module_base = path[-1]
23117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
23217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            try:
23317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                (package_dir, checked) = packages[package]
23417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            except KeyError:
235cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                package_dir = self.get_package_dir(package)
23617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                checked = 0
23717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
23817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            if not checked:
239cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                init_py = self.check_package(package, package_dir)
24017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                packages[package] = (package_dir, 1)
2418bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                if init_py:
2428bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward                    modules.append((package, "__init__", init_py))
24317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
24417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            # XXX perhaps we should also check for just .pyc files
24517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            # (so greedy closed-source bastards can distribute Python
24617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            # modules too)
247cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            module_file = os.path.join(package_dir, module_base + ".py")
248cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            if not self.check_module(module, module_file):
24917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward                continue
25017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
251cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            modules.append((package, module_base, module_file))
2522a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2532a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        return modules
2542a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2552a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward    # find_modules ()
2562a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2572a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2588b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward    def find_all_modules (self):
2598b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        """Compute the list of all modules that will be built, whether
2606f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        they are specified one-module-at-a-time ('self.py_modules') or
2618b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        by whole packages ('self.packages').  Return a list of tuples
2628b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        (package, module, module_file), just like 'find_modules()' and
2638b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        'find_package_modules()' do."""
2642a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
265e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling        modules = []
2666f980b59368c022fe7c1925e83f909dabf74e634Greg Ward        if self.py_modules:
267e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling            modules.extend(self.find_modules())
268e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling        if self.packages:
2692a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward            for package in self.packages:
270cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                package_dir = self.get_package_dir(package)
271cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                m = self.find_package_modules(package, package_dir)
272cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                modules.extend(m)
2732a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2748b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        return modules
2758b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
2768b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward    # find_all_modules ()
2778b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
2788b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
2798b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward    def get_source_files (self):
2808b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
281cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        modules = self.find_all_modules()
2822a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        filenames = []
2832a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        for module in modules:
284cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            filenames.append(module[-1])
2852a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2868b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        return filenames
2872a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2882a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
2898b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward    def get_module_outfile (self, build_dir, package, module):
2908b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        outfile_path = [build_dir] + list(package) + [module + ".py"]
291cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        return apply(os.path.join, outfile_path)
2928b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
2938b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
29473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward    def get_outputs (self, include_bytecode=1):
295cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        modules = self.find_all_modules()
2968b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        outputs = []
2978b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        for (package, module, module_file) in modules:
298cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            package = string.split(package, '.')
29973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            filename = self.get_module_outfile(self.build_lib, package, module)
30073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            outputs.append(filename)
30173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            if include_bytecode:
30273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                if self.compile:
30373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                    outputs.append(filename + "c")
30473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                if self.optimize > 0:
30573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward                    outputs.append(filename + "o")
30673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
3078b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        return outputs
3082a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
3098b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
3108b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward    def build_module (self, module, module_file, package):
311cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        if type(package) is StringType:
312cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            package = string.split(package, '.')
313cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        elif type(package) not in (ListType, TupleType):
314631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward            raise TypeError, \
315631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward                  "'package' must be a string (dot-separated), list, or tuple"
3162a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
3172a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        # Now put the module source file into the "build" area -- this is
318e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward        # easy, we just copy it somewhere under self.build_lib (the build
3192a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        # directory for Python source).
320cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        outfile = self.get_module_outfile(self.build_lib, package, module)
321cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        dir = os.path.dirname(outfile)
322cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        self.mkpath(dir)
323cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward        return self.copy_file(module_file, outfile, preserve_mode=0)
3242a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
3252a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
3262a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward    def build_modules (self):
3272a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
3282a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward        modules = self.find_modules()
3298b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward        for (package, module, module_file) in modules:
3302a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward
33117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            # Now "build" the module -- ie. copy the source file to
332e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward            # self.build_lib (the build directory for Python source).
3332a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward            # (Actually, it gets copied to the directory for this package
334e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward            # under self.build_lib.)
335cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            self.build_module(module, module_file, package)
33617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
33717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # build_modules ()
33817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
33917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
34017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    def build_packages (self):
34117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
34217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward        for package in self.packages:
3438b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward
3448b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # Get list of (package, module, module_file) tuples based on
3458b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # scanning the package directory.  'package' is only included
3468b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # in the tuple so that 'find_modules()' and
3478b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # 'find_package_tuples()' have a consistent interface; it's
3488b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # ignored here (apart from a sanity check).  Also, 'module' is
3498b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # the *unqualified* module name (ie. no dots, no package -- we
3508b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # already know its package!), and 'module_file' is the path to
3518b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # the .py file, relative to the current directory
3528b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            # (ie. including 'package_dir').
353cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            package_dir = self.get_package_dir(package)
354cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward            modules = self.find_package_modules(package, package_dir)
35517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
35617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward            # Now loop over the modules we found, "building" each one (just
357e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward            # copy it to self.build_lib).
3588b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward            for (package_, module, module_file) in modules:
3598b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward                assert package == package_
360cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward                self.build_module(module, module_file, package)
36117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward
36217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward    # build_packages ()
36373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
36473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
36573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward    def byte_compile (self, files):
36673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        from distutils.util import byte_compile
36773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        prefix = self.build_lib
36873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        if prefix[-1] != os.sep:
36973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            prefix = prefix + os.sep
37073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
37173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        # XXX this code is essentially the same as the 'byte_compile()
37273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        # method of the "install_lib" command, except for the determination
37373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        # of the 'prefix' string.  Hmmm.
37473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
37573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        if self.compile:
37673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            byte_compile(files, optimize=0,
377cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton                         force=self.force, prefix=prefix, dry_run=self.dry_run)
37873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward        if self.optimize > 0:
37973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward            byte_compile(files, optimize=self.optimize,
380cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton                         force=self.force, prefix=prefix, dry_run=self.dry_run)
38173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward
382fcd974efbb71ab7cb5a75639028508e0195939b8Greg Ward# class build_py
383