113ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward"""distutils.command.build_py 213ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 313ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg WardImplements the Distutils 'build_py' command.""" 413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 53ce77fd05ed00168f618b63401d770ccc4f04b09Greg Ward__revision__ = "$Id$" 613ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 7fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadéimport os 8b9c1cfc42881370322ff6894568b45dcfec5ba2eTarek Ziadéimport sys 917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Wardfrom glob import glob 1017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 1113ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Wardfrom distutils.core import Command 12fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadéfrom distutils.errors import DistutilsOptionError, DistutilsFileError 130c350bfad0ec9350aec57e37962b1aadb8492173Thomas Hellerfrom distutils.util import convert_path 14cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hyltonfrom distutils import log 1513ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 16fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadéclass build_py(Command): 1713ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 1837bc81505379facad85a7c6ff273de0201f28656Greg Ward description = "\"build\" pure Python modules (copy to build directory)" 1937bc81505379facad85a7c6ff273de0201f28656Greg Ward 20bbeceeaf9a5edf878154b17a6a94403a26822d51Greg Ward user_options = [ 21e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward ('build-lib=', 'd', "directory to \"build\" (copy) to"), 2273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward ('compile', 'c', "compile .py to .pyc"), 2373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward ('no-compile', None, "don't compile .py files [default]"), 2473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward ('optimize=', 'O', 2573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward "also compile with optimization: -O1 for \"python -O\", " 2673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), 276a2035d76ba9572ccd2ed61d59df46e2bdcb74edGreg Ward ('force', 'f', "forcibly build everything (ignore file timestamps)"), 28bbeceeaf9a5edf878154b17a6a94403a26822d51Greg Ward ] 2913ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 3073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward boolean_options = ['compile', 'force'] 3173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward negative_opt = {'no-compile' : 'compile'} 3299b032eaf24e1887f6451eceea7f1e24ced05a0fGreg Ward 33fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def initialize_options(self): 34e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward self.build_lib = None 356f980b59368c022fe7c1925e83f909dabf74e634Greg Ward self.py_modules = None 3671eb8644d7e27fd379a2cf78c509155bdb179332Greg Ward self.package = None 370eb32a65b06613d25aeb18f0104554171bea4840Fred Drake self.package_data = None 3817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward self.package_dir = None 3973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward self.compile = 0 4073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward self.optimize = 0 41c41d6b35a9de3dd9e3057a0db9a83b182e792f79Greg Ward self.force = None 4213ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 43fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def finalize_options(self): 44cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.set_undefined_options('build', 45cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward ('build_lib', 'build_lib'), 46cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward ('force', 'force')) 4717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 4817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Get the distribution options that are aliases for build_py 4917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # options -- list of packages and list of modules. 5017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward self.packages = self.distribution.packages 516f980b59368c022fe7c1925e83f909dabf74e634Greg Ward self.py_modules = self.distribution.py_modules 520eb32a65b06613d25aeb18f0104554171bea4840Fred Drake self.package_data = self.distribution.package_data 530c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller self.package_dir = {} 540c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller if self.distribution.package_dir: 550c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller for name, path in self.distribution.package_dir.items(): 560c350bfad0ec9350aec57e37962b1aadb8492173Thomas Heller self.package_dir[name] = convert_path(path) 57b849eddde6722549f9df1be1cd6ea7cf79d28807Fred Drake self.data_files = self.get_data_files() 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!!!) 61fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé if not isinstance(self.optimize, int): 6273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward try: 6373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward self.optimize = int(self.optimize) 6473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward assert 0 <= self.optimize <= 2 6573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward except (ValueError, AssertionError): 66fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé raise DistutilsOptionError("optimize must be 0, 1, or 2") 6713ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 68fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def run(self): 699b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # XXX copy_file by default preserves atime and mtime. IMHO this is 709b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # the right thing to do, but perhaps it should be an option -- in 719b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # particular, a site administrator might want installed files to 729b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # reflect the time of installation rather than the last 739b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # modification time before the installed release. 749b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward 759b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # XXX copy_file by default preserves mode, which appears to be the 769b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # wrong thing to do: if a file is read-only in the working 779b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # directory, we want it to be installed read/write so that the next 789b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # installation of the same module distribution can overwrite it 799b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # without problems. (This might be a Unix-specific issue.) Thus 809b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # we turn off 'preserve_mode' when copying to the build directory, 819b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # since the build directory is supposed to be exactly what the 829b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # installation will look like (ie. we preserve mode when 839b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward # installing). 8413ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward 8517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Two options control which modules will be installed: 'packages' 866f980b59368c022fe7c1925e83f909dabf74e634Greg Ward # and 'py_modules'. The former lets us work with whole packages, not 8717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # specifying individual modules at all; the latter is for 88e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling # specifying modules one-at-a-time. 89e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling 906f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if self.py_modules: 91cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_modules() 92e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling if self.packages: 93cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_packages() 940eb32a65b06613d25aeb18f0104554171bea4840Fred Drake self.build_package_data() 9517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 9673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward self.byte_compile(self.get_outputs(include_bytecode=0)) 9773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 98fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def get_data_files(self): 990eb32a65b06613d25aeb18f0104554171bea4840Fred Drake """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" 1000eb32a65b06613d25aeb18f0104554171bea4840Fred Drake data = [] 1014dbda47aea1759faa68e491237f078cf0c7050beSjoerd Mullender if not self.packages: 1024dbda47aea1759faa68e491237f078cf0c7050beSjoerd Mullender return data 1030eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for package in self.packages: 1040eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Locate package source directory 1050eb32a65b06613d25aeb18f0104554171bea4840Fred Drake src_dir = self.get_package_dir(package) 1060eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 1070eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Compute package build directory 1080eb32a65b06613d25aeb18f0104554171bea4840Fred Drake build_dir = os.path.join(*([self.build_lib] + package.split('.'))) 1090eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 1100eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Length of path to strip from found files 1114a700bb4698c1ec7f94b26f78f866929b013647fNeal Norwitz plen = 0 1124a700bb4698c1ec7f94b26f78f866929b013647fNeal Norwitz if src_dir: 1134a700bb4698c1ec7f94b26f78f866929b013647fNeal Norwitz plen = len(src_dir)+1 1140eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 1150eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Strip directory from globbed filenames 1160eb32a65b06613d25aeb18f0104554171bea4840Fred Drake filenames = [ 1170eb32a65b06613d25aeb18f0104554171bea4840Fred Drake file[plen:] for file in self.find_data_files(package, src_dir) 1180eb32a65b06613d25aeb18f0104554171bea4840Fred Drake ] 1190eb32a65b06613d25aeb18f0104554171bea4840Fred Drake data.append((package, src_dir, build_dir, filenames)) 1200eb32a65b06613d25aeb18f0104554171bea4840Fred Drake return data 1210eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 122fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def find_data_files(self, package, src_dir): 1230eb32a65b06613d25aeb18f0104554171bea4840Fred Drake """Return filenames for package's data files in 'src_dir'""" 1240eb32a65b06613d25aeb18f0104554171bea4840Fred Drake globs = (self.package_data.get('', []) 1250eb32a65b06613d25aeb18f0104554171bea4840Fred Drake + self.package_data.get(package, [])) 1260eb32a65b06613d25aeb18f0104554171bea4840Fred Drake files = [] 1270eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for pattern in globs: 1280eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Each pattern has to be converted to a platform-specific path 1290eb32a65b06613d25aeb18f0104554171bea4840Fred Drake filelist = glob(os.path.join(src_dir, convert_path(pattern))) 1300eb32a65b06613d25aeb18f0104554171bea4840Fred Drake # Files that match more than one pattern are only added once 1313c0713550ec72fbba502466e84bd5271fd4e4cf3Jason R. Coombs files.extend([fn for fn in filelist if fn not in files 1323c0713550ec72fbba502466e84bd5271fd4e4cf3Jason R. Coombs and os.path.isfile(fn)]) 1330eb32a65b06613d25aeb18f0104554171bea4840Fred Drake return files 1340eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 135fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def build_package_data(self): 1360eb32a65b06613d25aeb18f0104554171bea4840Fred Drake """Copy data files into build directory""" 1370eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for package, src_dir, build_dir, filenames in self.data_files: 1380eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for filename in filenames: 1390eb32a65b06613d25aeb18f0104554171bea4840Fred Drake target = os.path.join(build_dir, filename) 1400eb32a65b06613d25aeb18f0104554171bea4840Fred Drake self.mkpath(os.path.dirname(target)) 1410eb32a65b06613d25aeb18f0104554171bea4840Fred Drake self.copy_file(os.path.join(src_dir, filename), target, 1420eb32a65b06613d25aeb18f0104554171bea4840Fred Drake preserve_mode=False) 14317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 144fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def get_package_dir(self, package): 14517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward """Return the directory, relative to the top of the source 14617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward distribution, where package 'package' should be found 14717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward (at least according to the 'package_dir' option, if any).""" 14817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 149fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé path = package.split('.') 15017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 15117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if not self.package_dir: 152631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if path: 153fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé return os.path.join(*path) 154631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward else: 155631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward return '' 15617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 15717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward tail = [] 15817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward while path: 15917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward try: 160fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé pdir = self.package_dir['.'.join(path)] 16117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward except KeyError: 162cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward tail.insert(0, path[-1]) 16317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward del path[-1] 16417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 165cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward tail.insert(0, pdir) 166047e4a915d899b31dcb49fd820914df6a9991870Brett Cannon return os.path.join(*tail) 16713ae1c8ff81befcfd0b0ece98ef471cd504642d8Greg Ward else: 1688bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # Oops, got all the way through 'path' without finding a 1698bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # match in package_dir. If package_dir defines a directory 1708bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # for the root (nameless) package, then fallback on it; 1718bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # otherwise, we might as well have not consulted 1728bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # package_dir at all, as we just use the directory implied 1738bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # by 'tail' (which should be the same as the original value 1748bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # of 'path' at this point). 1758bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward pdir = self.package_dir.get('') 1768bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward if pdir is not None: 1778bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward tail.insert(0, pdir) 1788bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward 179631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if tail: 180fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé return os.path.join(*tail) 181631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward else: 182631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward return '' 18317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 184fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def check_package(self, package, package_dir): 18517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Empty dir name means current directory, which we can probably 18617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # assume exists. Also, os.path.exists and isdir don't know about 18717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # my "empty string means current dir" convention, so we have to 18817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # circumvent them. 18917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if package_dir != "": 190cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.exists(package_dir): 191fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé raise DistutilsFileError( 192fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé "package directory '%s' does not exist" % package_dir) 193cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.isdir(package_dir): 194fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé raise DistutilsFileError( 195fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé "supposed package directory '%s' exists, " 196fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé "but is not a directory" % package_dir) 19717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 19817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Require __init__.py for all but the "root package" 199631e6a0c070810b064c48ff6cf777ebb0276f038Greg Ward if package: 200cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward init_py = os.path.join(package_dir, "__init__.py") 201cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if os.path.isfile(init_py): 2028bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward return init_py 2038bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward else: 204cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton log.warn(("package init file '%s' not found " + 205cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton "(or not a regular file)"), init_py) 2068bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward 2078bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # Either not in a package at all (__init__.py not expected), or 2088bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # __init__.py doesn't exist -- so don't return the filename. 20911a52708f7f2223b335734e584e2ba30eae74a5fJeremy Hylton return None 21021d45356b8a22bb3091a97f50b919b57798675f5Fred Drake 211fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def check_module(self, module, module_file): 212cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not os.path.isfile(module_file): 213cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton log.warn("file %s (for module %s) not found", module_file, module) 214fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé return False 21517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward else: 216fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé return True 21717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 218fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def find_package_modules(self, package, package_dir): 219cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.check_package(package, package_dir) 220cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module_files = glob(os.path.join(package_dir, "*.py")) 2218b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward modules = [] 2229821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward setup_script = os.path.abspath(self.distribution.script_name) 2239b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward 22417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward for f in module_files: 225cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward abs_f = os.path.abspath(f) 2269b45443c1bdf99b0f27b12baf06fea475b60e145Greg Ward if abs_f != setup_script: 227cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module = os.path.splitext(os.path.basename(f))[0] 228cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.append((package, module, f)) 2299821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward else: 2309821bf4e62bcb7d503aed782a8f6398e5de720afGreg Ward self.debug_print("excluding %s" % setup_script) 2318b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return modules 23217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 233fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def find_modules(self): 2348bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward """Finds individually-specified Python modules, ie. those listed by 2356f980b59368c022fe7c1925e83f909dabf74e634Greg Ward module name in 'self.py_modules'. Returns a list of tuples (package, 2368bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward module_base, filename): 'package' is a tuple of the path through 2378bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward package-space to the module; 'module_base' is the bare (no 2388bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward packages, no dots) module name, and 'filename' is the path to the 2398bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward ".py" file (relative to the distribution root) that implements the 2408bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward module. 2418bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward """ 24217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Map package names to tuples of useful info about the package: 24317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # (package_dir, checked) 24417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # package_dir - the directory where we'll find source files for 24517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # this package 24617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # checked - true if we have checked that the package directory 24717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # is valid (exists, contains __init__.py, ... ?) 24817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward packages = {} 24917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 2508bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward # List of (package, module, filename) tuples to return 2512a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward modules = [] 2522a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 25317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # We treat modules-in-packages almost the same as toplevel modules, 25417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # just the "package" for a toplevel is empty (either an empty 25517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # string or empty list, depending on context). Differences: 25617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # - don't check for __init__.py in directory for empty package 2576f980b59368c022fe7c1925e83f909dabf74e634Greg Ward for module in self.py_modules: 258fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé path = module.split('.') 259fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé package = '.'.join(path[0:-1]) 2602a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward module_base = path[-1] 26117dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 26217dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward try: 26317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward (package_dir, checked) = packages[package] 26417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward except KeyError: 265cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 26617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward checked = 0 26717dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 26817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward if not checked: 269cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward init_py = self.check_package(package, package_dir) 27017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward packages[package] = (package_dir, 1) 2718bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward if init_py: 2728bbba17d3815a44eefbd0cf33db937a56fe50db5Greg Ward modules.append((package, "__init__", init_py)) 27317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 27417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # XXX perhaps we should also check for just .pyc files 27517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # (so greedy closed-source bastards can distribute Python 27617dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # modules too) 277cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward module_file = os.path.join(package_dir, module_base + ".py") 278cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward if not self.check_module(module, module_file): 27917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward continue 28017dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 281cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.append((package, module_base, module_file)) 2822a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 2832a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward return modules 2842a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 285fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def find_all_modules(self): 2868b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward """Compute the list of all modules that will be built, whether 2876f980b59368c022fe7c1925e83f909dabf74e634Greg Ward they are specified one-module-at-a-time ('self.py_modules') or 2888b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward by whole packages ('self.packages'). Return a list of tuples 2898b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward (package, module, module_file), just like 'find_modules()' and 2908b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 'find_package_modules()' do.""" 291e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling modules = [] 2926f980b59368c022fe7c1925e83f909dabf74e634Greg Ward if self.py_modules: 293e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling modules.extend(self.find_modules()) 294e557f3556f1f87825e00a018eabf837c4c55f7d5Andrew M. Kuchling if self.packages: 2952a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward for package in self.packages: 296cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 297cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward m = self.find_package_modules(package, package_dir) 298cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules.extend(m) 2998b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return modules 3008b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 301fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def get_source_files(self): 302fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé return [module[-1] for module in self.find_all_modules()] 3032a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 304fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def get_module_outfile(self, build_dir, package, module): 3058b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward outfile_path = [build_dir] + list(package) + [module + ".py"] 306047e4a915d899b31dcb49fd820914df6a9991870Brett Cannon return os.path.join(*outfile_path) 3078b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 308fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def get_outputs(self, include_bytecode=1): 309cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_all_modules() 3108b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward outputs = [] 3118b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package, module, module_file) in modules: 312fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé package = package.split('.') 31373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward filename = self.get_module_outfile(self.build_lib, package, module) 31473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename) 31573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if include_bytecode: 31673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.compile: 31773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename + "c") 31873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.optimize > 0: 31973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward outputs.append(filename + "o") 32073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 3210eb32a65b06613d25aeb18f0104554171bea4840Fred Drake outputs += [ 3220eb32a65b06613d25aeb18f0104554171bea4840Fred Drake os.path.join(build_dir, filename) 3230eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for package, src_dir, build_dir, filenames in self.data_files 3240eb32a65b06613d25aeb18f0104554171bea4840Fred Drake for filename in filenames 3250eb32a65b06613d25aeb18f0104554171bea4840Fred Drake ] 3260eb32a65b06613d25aeb18f0104554171bea4840Fred Drake 3278b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward return outputs 3282a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 329fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def build_module(self, module, module_file, package): 330fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé if isinstance(package, str): 331fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé package = package.split('.') 332fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé elif not isinstance(package, (list, tuple)): 333fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé raise TypeError( 334fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé "'package' must be a string (dot-separated), list, or tuple") 3352a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 3362a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # Now put the module source file into the "build" area -- this is 337e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # easy, we just copy it somewhere under self.build_lib (the build 3382a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # directory for Python source). 339cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward outfile = self.get_module_outfile(self.build_lib, package, module) 340cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward dir = os.path.dirname(outfile) 341cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.mkpath(dir) 342cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward return self.copy_file(module_file, outfile, preserve_mode=0) 3432a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 344fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def build_modules(self): 3452a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward modules = self.find_modules() 3468b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package, module, module_file) in modules: 3472a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward 34817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Now "build" the module -- ie. copy the source file to 349e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # self.build_lib (the build directory for Python source). 3502a612067e60a98f05d39b39f4a7a5a7a8065bfc9Greg Ward # (Actually, it gets copied to the directory for this package 351e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # under self.build_lib.) 352cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_module(module, module_file, package) 35317dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 354fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def build_packages(self): 35517dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward for package in self.packages: 3568b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward 3578b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # Get list of (package, module, module_file) tuples based on 3588b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # scanning the package directory. 'package' is only included 3598b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # in the tuple so that 'find_modules()' and 3608b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # 'find_package_tuples()' have a consistent interface; it's 3618b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # ignored here (apart from a sanity check). Also, 'module' is 3628b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # the *unqualified* module name (ie. no dots, no package -- we 3638b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # already know its package!), and 'module_file' is the path to 3648b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # the .py file, relative to the current directory 3658b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward # (ie. including 'package_dir'). 366cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward package_dir = self.get_package_dir(package) 367cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward modules = self.find_package_modules(package, package_dir) 36817dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 36917dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward # Now loop over the modules we found, "building" each one (just 370e6916516828a88ab71fab7a749f8c9cf6b52775aGreg Ward # copy it to self.build_lib). 3718b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward for (package_, module, module_file) in modules: 3728b2e95edd69cacafdfbcf00270065bd6444f3336Greg Ward assert package == package_ 373cb1f4c4d33711a964dcd40b32a562e2acf745fc2Greg Ward self.build_module(module, module_file, package) 37417dc6e7ed8cd62068b5f244a9f1023917d3caf4aGreg Ward 375fe97ebbf6215d3127e44241fd7beda8465725e31Tarek Ziadé def byte_compile(self, files): 376b9c1cfc42881370322ff6894568b45dcfec5ba2eTarek Ziadé if sys.dont_write_bytecode: 3771733c9362b7520278120423550de28da44d15f33Tarek Ziadé self.warn('byte-compiling is disabled, skipping.') 378b9c1cfc42881370322ff6894568b45dcfec5ba2eTarek Ziadé return 379b9c1cfc42881370322ff6894568b45dcfec5ba2eTarek Ziadé 38073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward from distutils.util import byte_compile 38173a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix = self.build_lib 38273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if prefix[-1] != os.sep: 38373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward prefix = prefix + os.sep 38473a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 38573a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # XXX this code is essentially the same as the 'byte_compile() 38673a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # method of the "install_lib" command, except for the determination 38773a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward # of the 'prefix' string. Hmmm. 38873a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward 38973a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.compile: 39073a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward byte_compile(files, optimize=0, 391cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton force=self.force, prefix=prefix, dry_run=self.dry_run) 39273a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward if self.optimize > 0: 39373a6c942cda33c0b6c97a10b7ef5664e043f987fGreg Ward byte_compile(files, optimize=self.optimize, 394cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton force=self.force, prefix=prefix, dry_run=self.dry_run) 395