build_py.py revision 0c350bfad0ec9350aec57e37962b1aadb8492173
113ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward"""distutils.command.build_py 213ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 313ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg WardImplements the Distutils 'build_py' command.""" 413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 513ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward# created 1999/03/08, Greg Ward 613ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 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 1613ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 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 8917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # specifying modules one-at-a-time. Currently they are mutually 9017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # exclusive: you can define one or the other (or neither), but not 9117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # both. It remains to be seen how limiting this is. 9213ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 9317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Dispose of the two "unusual" cases first: no pure Python modules 9417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # at all (no problem, just return silently), and over-specified 956f980b59368c022fe7c1925e83f909dabf74e634Greg Ward # 'packages' and 'py_modules' options. 9617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 976f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if not self.py_modules and not self.packages: 985d60fcf02a7050a07067a12c7a98c8b6b1e68372Greg Ward return 996f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if self.py_modules and self.packages: 10017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward raise DistutilsOptionError, \ 1016f980b59368c022fe7c1925e83f909dabf74e634Greg Ward "build_py: supplying both 'packages' and 'py_modules' " + \ 1029b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward "options is not allowed" 10317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 1046f980b59368c022fe7c1925e83f909dabf74e634Greg Ward # Now we're down to two cases: 'py_modules' only and 'packages' only. 1056f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if self.py_modules: 106cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_modules() 10717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 108cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_packages() 10917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 11073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward self.byte_compile(self.get_outputs(include_bytecode=0)) 11173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 11217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # run () 11321d45356b8a22bb3091a97f50b919b57798675f5Fred Drake 11417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 11517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward def get_package_dir (self, package): 11617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward """Return the directory, relative to the top of the source 11717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward distribution, where package 'package' should be found 11817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward (at least according to the 'package_dir' option, if any).""" 11917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 120cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward path = string.split(package, '.') 12117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 12217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if not self.package_dir: 123631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if path: 124cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return apply(os.path.join, path) 125631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward else: 126631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward return '' 12717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 12817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward tail = [] 12917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward while path: 13017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward try: 131cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward pdir = self.package_dir[string.join(path, '.')] 13217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward except KeyError: 133cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward tail.insert(0, path[-1]) 13417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward del path[-1] 13517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 136cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward tail.insert(0, pdir) 137cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return apply(os.path.join, tail) 13813ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward else: 1398bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # Oops, got all the way through 'path' without finding a 1408bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # match in package_dir. If package_dir defines a directory 1418bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # for the root (nameless) package, then fallback on it; 1428bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # otherwise, we might as well have not consulted 1438bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # package_dir at all, as we just use the directory implied 1448bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # by 'tail' (which should be the same as the original value 1458bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # of 'path' at this point). 1468bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward pdir = self.package_dir.get('') 1478bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward if pdir is not None: 1488bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward tail.insert(0, pdir) 1498bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward 150631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if tail: 151cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return apply(os.path.join, tail) 152631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward else: 153631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward return '' 15417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 15517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # get_package_dir () 15617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 15717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 15817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward def check_package (self, package, package_dir): 15917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 16017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Empty dir name means current directory, which we can probably 16117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # assume exists. Also, os.path.exists and isdir don't know about 16217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # my "empty string means current dir" convention, so we have to 16317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # circumvent them. 16417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if package_dir != "": 165cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.exists(package_dir): 16617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward raise DistutilsFileError, \ 16717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward "package directory '%s' does not exist" % package_dir 168cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.isdir(package_dir): 16956359f591b361f41f661a14e5ed129bf8f22fa87Greg Ward raise DistutilsFileError, \ 17017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward ("supposed package directory '%s' exists, " + 17117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward "but is not a directory") % package_dir 17217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 17317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Require __init__.py for all but the "root package" 174631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if package: 175cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward init_py = os.path.join(package_dir, "__init__.py") 176cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if os.path.isfile(init_py): 1778bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward return init_py 1788bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward else: 179cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.warn(("package init file '%s' not found " + 180cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward "(or not a regular file)") % init_py) 1818bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward 1828bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # Either not in a package at all (__init__.py not expected), or 1838bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # __init__.py doesn't exist -- so don't return the filename. 1848bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward return 18521d45356b8a22bb3091a97f50b919b57798675f5Fred Drake 18617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # check_package () 18717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 18817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 18917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward def check_module (self, module, module_file): 190cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.isfile(module_file): 19121d45356b8a22bb3091a97f50b919b57798675f5Fred Drake self.warn("file %s (for module %s) not found" % 192cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward (module_file, module)) 19317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward return 0 19417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 19517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward return 1 19617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 19717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # check_module () 19817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 19917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 2002a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward def find_package_modules (self, package, package_dir): 201cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.check_package(package, package_dir) 202cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module_files = glob(os.path.join(package_dir, "*.py")) 2038b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward modules = [] 2049821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward setup_script = os.path.abspath(self.distribution.script_name) 2059b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward 20617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward for f in module_files: 207cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward abs_f = os.path.abspath(f) 2089b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward if abs_f != setup_script: 209cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module = os.path.splitext(os.path.basename(f))[0] 210cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.append((package, module, f)) 2119821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward else: 2129821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward self.debug_print("excluding %s" % setup_script) 2138b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return modules 21417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 21517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 2162a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward def find_modules (self): 2178bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward """Finds individually-specified Python modules, ie. those listed by 2186f980b59368c022fe7c1925e83f909dabf74e634Greg Ward module name in 'self.py_modules'. Returns a list of tuples (package, 2198bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward module_base, filename): 'package' is a tuple of the path through 2208bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward package-space to the module; 'module_base' is the bare (no 2218bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward packages, no dots) module name, and 'filename' is the path to the 2228bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward ".py" file (relative to the distribution root) that implements the 2238bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward module. 2248bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward """ 2258bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward 22617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Map package names to tuples of useful info about the package: 22717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # (package_dir, checked) 22817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # package_dir - the directory where we'll find source files for 22917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # this package 23017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # checked - true if we have checked that the package directory 23117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # is valid (exists, contains __init__.py, ... ?) 23217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward packages = {} 23317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 2348bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # List of (package, module, filename) tuples to return 2352a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward modules = [] 2362a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 23717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # We treat modules-in-packages almost the same as toplevel modules, 23817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # just the "package" for a toplevel is empty (either an empty 23917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # string or empty list, depending on context). Differences: 24017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # - don't check for __init__.py in directory for empty package 24117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 2426f980b59368c022fe7c1925e83f909dabf74e634Greg Ward for module in self.py_modules: 243cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward path = string.split(module, '.') 244c0fe82ca26b1da22e35f2d0676f09795d052e4f0Greg Ward package = string.join(path[0:-1], '.') 2452a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward module_base = path[-1] 24617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 24717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward try: 24817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward (package_dir, checked) = packages[package] 24917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward except KeyError: 250cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 25117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward checked = 0 25217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 25317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if not checked: 254cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward init_py = self.check_package(package, package_dir) 25517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward packages[package] = (package_dir, 1) 2568bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward if init_py: 2578bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward modules.append((package, "__init__", init_py)) 25817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 25917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # XXX perhaps we should also check for just .pyc files 26017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # (so greedy closed-source bastards can distribute Python 26117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # modules too) 262cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module_file = os.path.join(package_dir, module_base + ".py") 263cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not self.check_module(module, module_file): 26417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward continue 26517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 266cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.append((package, module_base, module_file)) 2672a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2682a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward return modules 2692a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2702a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # find_modules () 2712a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2722a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2738b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward def find_all_modules (self): 2748b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward """Compute the list of all modules that will be built, whether 2756f980b59368c022fe7c1925e83f909dabf74e634Greg Ward they are specified one-module-at-a-time ('self.py_modules') or 2768b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward by whole packages ('self.packages'). Return a list of tuples 2778b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward (package, module, module_file), just like 'find_modules()' and 2788b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 'find_package_modules()' do.""" 2792a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2806f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if self.py_modules: 281cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_modules() 2822a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward else: 2832a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward modules = [] 2842a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward for package in self.packages: 285cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 286cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward m = self.find_package_modules(package, package_dir) 287cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.extend(m) 2882a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2898b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return modules 2908b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 2918b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # find_all_modules () 2928b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 2938b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 2948b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward def get_source_files (self): 2958b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 296cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_all_modules() 2972a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward filenames = [] 2982a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward for module in modules: 299cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward filenames.append(module[-1]) 3002a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3018b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return filenames 3022a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3032a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3048b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward def get_module_outfile (self, build_dir, package, module): 3058b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward outfile_path = [build_dir] + list(package) + [module + ".py"] 306cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return apply(os.path.join, outfile_path) 3078b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 3088b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 30973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward def get_outputs (self, include_bytecode=1): 310cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_all_modules() 3118b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward outputs = [] 3128b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package, module, module_file) in modules: 313cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package = string.split(package, '.') 31473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward filename = self.get_module_outfile(self.build_lib, package, module) 31573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename) 31673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if include_bytecode: 31773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.compile: 31873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename + "c") 31973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.optimize > 0: 32073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename + "o") 32173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 3228b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return outputs 3232a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3248b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 3258b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward def build_module (self, module, module_file, package): 326cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if type(package) is StringType: 327cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package = string.split(package, '.') 328cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward elif type(package) not in (ListType, TupleType): 329631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward raise TypeError, \ 330631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward "'package' must be a string (dot-separated), list, or tuple" 3312a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3322a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # Now put the module source file into the "build" area -- this is 333e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # easy, we just copy it somewhere under self.build_lib (the build 3342a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # directory for Python source). 335cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward outfile = self.get_module_outfile(self.build_lib, package, module) 336cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward dir = os.path.dirname(outfile) 337cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.mkpath(dir) 338cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return self.copy_file(module_file, outfile, preserve_mode=0) 3392a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3402a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3412a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward def build_modules (self): 3422a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3432a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward modules = self.find_modules() 3448b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package, module, module_file) in modules: 3452a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 34617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Now "build" the module -- ie. copy the source file to 347e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # self.build_lib (the build directory for Python source). 3482a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # (Actually, it gets copied to the directory for this package 349e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # under self.build_lib.) 350cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_module(module, module_file, package) 35117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 35217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # build_modules () 35317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 35417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 35517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward def build_packages (self): 35617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 35717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward for package in self.packages: 3588b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 3598b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # Get list of (package, module, module_file) tuples based on 3608b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # scanning the package directory. 'package' is only included 3618b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # in the tuple so that 'find_modules()' and 3628b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # 'find_package_tuples()' have a consistent interface; it's 3638b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # ignored here (apart from a sanity check). Also, 'module' is 3648b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # the *unqualified* module name (ie. no dots, no package -- we 3658b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # already know its package!), and 'module_file' is the path to 3668b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # the .py file, relative to the current directory 3678b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # (ie. including 'package_dir'). 368cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 369cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_package_modules(package, package_dir) 37017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 37117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Now loop over the modules we found, "building" each one (just 372e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # copy it to self.build_lib). 3738b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package_, module, module_file) in modules: 3748b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward assert package == package_ 375cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_module(module, module_file, package) 37617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 37717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # build_packages () 37873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 37973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 38073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward def byte_compile (self, files): 38173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward from distutils.util import byte_compile 38273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix = self.build_lib 38373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if prefix[-1] != os.sep: 38473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix = prefix + os.sep 38573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 38673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # XXX this code is essentially the same as the 'byte_compile() 38773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # method of the "install_lib" command, except for the determination 38873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # of the 'prefix' string. Hmmm. 38973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 39073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.compile: 39173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward byte_compile(files, optimize=0, 39273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward force=self.force, 39373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix=prefix, 39473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward verbose=self.verbose, dry_run=self.dry_run) 39573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.optimize > 0: 39673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward byte_compile(files, optimize=self.optimize, 39773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward force=self.force, 39873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix=prefix, 39973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward verbose=self.verbose, dry_run=self.dry_run) 40073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 401fcd974efbb71ab7cb5a75639028508e0195939b8Greg Ward# class build_py 402