PyXR

c:\python24\lib \ distutils \ command \ build_py.py



0001 """distutils.command.build_py
0002 
0003 Implements the Distutils 'build_py' command."""
0004 
0005 # This module should be kept compatible with Python 1.5.2.
0006 
0007 __revision__ = "$Id: build_py.py,v 1.45 2004/06/18 20:39:11 sjoerd Exp $"
0008 
0009 import sys, string, os
0010 from types import *
0011 from glob import glob
0012 
0013 from distutils.core import Command
0014 from distutils.errors import *
0015 from distutils.util import convert_path
0016 from distutils import log
0017 
0018 class build_py (Command):
0019 
0020     description = "\"build\" pure Python modules (copy to build directory)"
0021 
0022     user_options = [
0023         ('build-lib=', 'd', "directory to \"build\" (copy) to"),
0024         ('compile', 'c', "compile .py to .pyc"),
0025         ('no-compile', None, "don't compile .py files [default]"),
0026         ('optimize=', 'O',
0027          "also compile with optimization: -O1 for \"python -O\", "
0028          "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
0029         ('force', 'f', "forcibly build everything (ignore file timestamps)"),
0030         ]
0031 
0032     boolean_options = ['compile', 'force']
0033     negative_opt = {'no-compile' : 'compile'}
0034 
0035 
0036     def initialize_options (self):
0037         self.build_lib = None
0038         self.py_modules = None
0039         self.package = None
0040         self.package_data = None
0041         self.package_dir = None
0042         self.compile = 0
0043         self.optimize = 0
0044         self.force = None
0045 
0046     def finalize_options (self):
0047         self.set_undefined_options('build',
0048                                    ('build_lib', 'build_lib'),
0049                                    ('force', 'force'))
0050 
0051         # Get the distribution options that are aliases for build_py
0052         # options -- list of packages and list of modules.
0053         self.packages = self.distribution.packages
0054         self.py_modules = self.distribution.py_modules
0055         self.package_data = self.distribution.package_data
0056         self.package_dir = {}
0057         if self.distribution.package_dir:
0058             for name, path in self.distribution.package_dir.items():
0059                 self.package_dir[name] = convert_path(path)
0060         self.data_files = self.get_data_files()
0061 
0062         # Ick, copied straight from install_lib.py (fancy_getopt needs a
0063         # type system!  Hell, *everything* needs a type system!!!)
0064         if type(self.optimize) is not IntType:
0065             try:
0066                 self.optimize = int(self.optimize)
0067                 assert 0 <= self.optimize <= 2
0068             except (ValueError, AssertionError):
0069                 raise DistutilsOptionError, "optimize must be 0, 1, or 2"
0070 
0071     def run (self):
0072 
0073         # XXX copy_file by default preserves atime and mtime.  IMHO this is
0074         # the right thing to do, but perhaps it should be an option -- in
0075         # particular, a site administrator might want installed files to
0076         # reflect the time of installation rather than the last
0077         # modification time before the installed release.
0078 
0079         # XXX copy_file by default preserves mode, which appears to be the
0080         # wrong thing to do: if a file is read-only in the working
0081         # directory, we want it to be installed read/write so that the next
0082         # installation of the same module distribution can overwrite it
0083         # without problems.  (This might be a Unix-specific issue.)  Thus
0084         # we turn off 'preserve_mode' when copying to the build directory,
0085         # since the build directory is supposed to be exactly what the
0086         # installation will look like (ie. we preserve mode when
0087         # installing).
0088 
0089         # Two options control which modules will be installed: 'packages'
0090         # and 'py_modules'.  The former lets us work with whole packages, not
0091         # specifying individual modules at all; the latter is for
0092         # specifying modules one-at-a-time.
0093 
0094         if self.py_modules:
0095             self.build_modules()
0096         if self.packages:
0097             self.build_packages()
0098             self.build_package_data()
0099 
0100         self.byte_compile(self.get_outputs(include_bytecode=0))
0101 
0102     # run ()
0103 
0104     def get_data_files (self):
0105         """Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
0106         data = []
0107         if not self.packages:
0108             return data
0109         for package in self.packages:
0110             # Locate package source directory
0111             src_dir = self.get_package_dir(package)
0112 
0113             # Compute package build directory
0114             build_dir = os.path.join(*([self.build_lib] + package.split('.')))
0115 
0116             # Length of path to strip from found files
0117             plen = len(src_dir)+1
0118 
0119             # Strip directory from globbed filenames
0120             filenames = [
0121                 file[plen:] for file in self.find_data_files(package, src_dir)
0122                 ]
0123             data.append((package, src_dir, build_dir, filenames))
0124         return data
0125 
0126     def find_data_files (self, package, src_dir):
0127         """Return filenames for package's data files in 'src_dir'"""
0128         globs = (self.package_data.get('', [])
0129                  + self.package_data.get(package, []))
0130         files = []
0131         for pattern in globs:
0132             # Each pattern has to be converted to a platform-specific path
0133             filelist = glob(os.path.join(src_dir, convert_path(pattern)))
0134             # Files that match more than one pattern are only added once
0135             files.extend([fn for fn in filelist if fn not in files])
0136         return files
0137 
0138     def build_package_data (self):
0139         """Copy data files into build directory"""
0140         lastdir = None
0141         for package, src_dir, build_dir, filenames in self.data_files:
0142             for filename in filenames:
0143                 target = os.path.join(build_dir, filename)
0144                 self.mkpath(os.path.dirname(target))
0145                 self.copy_file(os.path.join(src_dir, filename), target,
0146                                preserve_mode=False)
0147 
0148     def get_package_dir (self, package):
0149         """Return the directory, relative to the top of the source
0150            distribution, where package 'package' should be found
0151            (at least according to the 'package_dir' option, if any)."""
0152 
0153         path = string.split(package, '.')
0154 
0155         if not self.package_dir:
0156             if path:
0157                 return apply(os.path.join, path)
0158             else:
0159                 return ''
0160         else:
0161             tail = []
0162             while path:
0163                 try:
0164                     pdir = self.package_dir[string.join(path, '.')]
0165                 except KeyError:
0166                     tail.insert(0, path[-1])
0167                     del path[-1]
0168                 else:
0169                     tail.insert(0, pdir)
0170                     return apply(os.path.join, tail)
0171             else:
0172                 # Oops, got all the way through 'path' without finding a
0173                 # match in package_dir.  If package_dir defines a directory
0174                 # for the root (nameless) package, then fallback on it;
0175                 # otherwise, we might as well have not consulted
0176                 # package_dir at all, as we just use the directory implied
0177                 # by 'tail' (which should be the same as the original value
0178                 # of 'path' at this point).
0179                 pdir = self.package_dir.get('')
0180                 if pdir is not None:
0181                     tail.insert(0, pdir)
0182 
0183                 if tail:
0184                     return apply(os.path.join, tail)
0185                 else:
0186                     return ''
0187 
0188     # get_package_dir ()
0189 
0190 
0191     def check_package (self, package, package_dir):
0192 
0193         # Empty dir name means current directory, which we can probably
0194         # assume exists.  Also, os.path.exists and isdir don't know about
0195         # my "empty string means current dir" convention, so we have to
0196         # circumvent them.
0197         if package_dir != "":
0198             if not os.path.exists(package_dir):
0199                 raise DistutilsFileError, \
0200                       "package directory '%s' does not exist" % package_dir
0201             if not os.path.isdir(package_dir):
0202                 raise DistutilsFileError, \
0203                       ("supposed package directory '%s' exists, " +
0204                        "but is not a directory") % package_dir
0205 
0206         # Require __init__.py for all but the "root package"
0207         if package:
0208             init_py = os.path.join(package_dir, "__init__.py")
0209             if os.path.isfile(init_py):
0210                 return init_py
0211             else:
0212                 log.warn(("package init file '%s' not found " +
0213                           "(or not a regular file)"), init_py)
0214 
0215         # Either not in a package at all (__init__.py not expected), or
0216         # __init__.py doesn't exist -- so don't return the filename.
0217         return None
0218 
0219     # check_package ()
0220 
0221 
0222     def check_module (self, module, module_file):
0223         if not os.path.isfile(module_file):
0224             log.warn("file %s (for module %s) not found", module_file, module)
0225             return 0
0226         else:
0227             return 1
0228 
0229     # check_module ()
0230 
0231 
0232     def find_package_modules (self, package, package_dir):
0233         self.check_package(package, package_dir)
0234         module_files = glob(os.path.join(package_dir, "*.py"))
0235         modules = []
0236         setup_script = os.path.abspath(self.distribution.script_name)
0237 
0238         for f in module_files:
0239             abs_f = os.path.abspath(f)
0240             if abs_f != setup_script:
0241                 module = os.path.splitext(os.path.basename(f))[0]
0242                 modules.append((package, module, f))
0243             else:
0244                 self.debug_print("excluding %s" % setup_script)
0245         return modules
0246 
0247 
0248     def find_modules (self):
0249         """Finds individually-specified Python modules, ie. those listed by
0250         module name in 'self.py_modules'.  Returns a list of tuples (package,
0251         module_base, filename): 'package' is a tuple of the path through
0252         package-space to the module; 'module_base' is the bare (no
0253         packages, no dots) module name, and 'filename' is the path to the
0254         ".py" file (relative to the distribution root) that implements the
0255         module.
0256         """
0257 
0258         # Map package names to tuples of useful info about the package:
0259         #    (package_dir, checked)
0260         # package_dir - the directory where we'll find source files for
0261         #   this package
0262         # checked - true if we have checked that the package directory
0263         #   is valid (exists, contains __init__.py, ... ?)
0264         packages = {}
0265 
0266         # List of (package, module, filename) tuples to return
0267         modules = []
0268 
0269         # We treat modules-in-packages almost the same as toplevel modules,
0270         # just the "package" for a toplevel is empty (either an empty
0271         # string or empty list, depending on context).  Differences:
0272         #   - don't check for __init__.py in directory for empty package
0273 
0274         for module in self.py_modules:
0275             path = string.split(module, '.')
0276             package = string.join(path[0:-1], '.')
0277             module_base = path[-1]
0278 
0279             try:
0280                 (package_dir, checked) = packages[package]
0281             except KeyError:
0282                 package_dir = self.get_package_dir(package)
0283                 checked = 0
0284 
0285             if not checked:
0286                 init_py = self.check_package(package, package_dir)
0287                 packages[package] = (package_dir, 1)
0288                 if init_py:
0289                     modules.append((package, "__init__", init_py))
0290 
0291             # XXX perhaps we should also check for just .pyc files
0292             # (so greedy closed-source bastards can distribute Python
0293             # modules too)
0294             module_file = os.path.join(package_dir, module_base + ".py")
0295             if not self.check_module(module, module_file):
0296                 continue
0297 
0298             modules.append((package, module_base, module_file))
0299 
0300         return modules
0301 
0302     # find_modules ()
0303 
0304 
0305     def find_all_modules (self):
0306         """Compute the list of all modules that will be built, whether
0307         they are specified one-module-at-a-time ('self.py_modules') or
0308         by whole packages ('self.packages').  Return a list of tuples
0309         (package, module, module_file), just like 'find_modules()' and
0310         'find_package_modules()' do."""
0311 
0312         modules = []
0313         if self.py_modules:
0314             modules.extend(self.find_modules())
0315         if self.packages:
0316             for package in self.packages:
0317                 package_dir = self.get_package_dir(package)
0318                 m = self.find_package_modules(package, package_dir)
0319                 modules.extend(m)
0320 
0321         return modules
0322 
0323     # find_all_modules ()
0324 
0325 
0326     def get_source_files (self):
0327 
0328         modules = self.find_all_modules()
0329         filenames = []
0330         for module in modules:
0331             filenames.append(module[-1])
0332 
0333         return filenames
0334 
0335 
0336     def get_module_outfile (self, build_dir, package, module):
0337         outfile_path = [build_dir] + list(package) + [module + ".py"]
0338         return apply(os.path.join, outfile_path)
0339 
0340 
0341     def get_outputs (self, include_bytecode=1):
0342         modules = self.find_all_modules()
0343         outputs = []
0344         for (package, module, module_file) in modules:
0345             package = string.split(package, '.')
0346             filename = self.get_module_outfile(self.build_lib, package, module)
0347             outputs.append(filename)
0348             if include_bytecode:
0349                 if self.compile:
0350                     outputs.append(filename + "c")
0351                 if self.optimize > 0:
0352                     outputs.append(filename + "o")
0353 
0354         outputs += [
0355             os.path.join(build_dir, filename)
0356             for package, src_dir, build_dir, filenames in self.data_files
0357             for filename in filenames
0358             ]
0359 
0360         return outputs
0361 
0362 
0363     def build_module (self, module, module_file, package):
0364         if type(package) is StringType:
0365             package = string.split(package, '.')
0366         elif type(package) not in (ListType, TupleType):
0367             raise TypeError, \
0368                   "'package' must be a string (dot-separated), list, or tuple"
0369 
0370         # Now put the module source file into the "build" area -- this is
0371         # easy, we just copy it somewhere under self.build_lib (the build
0372         # directory for Python source).
0373         outfile = self.get_module_outfile(self.build_lib, package, module)
0374         dir = os.path.dirname(outfile)
0375         self.mkpath(dir)
0376         return self.copy_file(module_file, outfile, preserve_mode=0)
0377 
0378 
0379     def build_modules (self):
0380 
0381         modules = self.find_modules()
0382         for (package, module, module_file) in modules:
0383 
0384             # Now "build" the module -- ie. copy the source file to
0385             # self.build_lib (the build directory for Python source).
0386             # (Actually, it gets copied to the directory for this package
0387             # under self.build_lib.)
0388             self.build_module(module, module_file, package)
0389 
0390     # build_modules ()
0391 
0392 
0393     def build_packages (self):
0394 
0395         for package in self.packages:
0396 
0397             # Get list of (package, module, module_file) tuples based on
0398             # scanning the package directory.  'package' is only included
0399             # in the tuple so that 'find_modules()' and
0400             # 'find_package_tuples()' have a consistent interface; it's
0401             # ignored here (apart from a sanity check).  Also, 'module' is
0402             # the *unqualified* module name (ie. no dots, no package -- we
0403             # already know its package!), and 'module_file' is the path to
0404             # the .py file, relative to the current directory
0405             # (ie. including 'package_dir').
0406             package_dir = self.get_package_dir(package)
0407             modules = self.find_package_modules(package, package_dir)
0408 
0409             # Now loop over the modules we found, "building" each one (just
0410             # copy it to self.build_lib).
0411             for (package_, module, module_file) in modules:
0412                 assert package == package_
0413                 self.build_module(module, module_file, package)
0414 
0415     # build_packages ()
0416 
0417 
0418     def byte_compile (self, files):
0419         from distutils.util import byte_compile
0420         prefix = self.build_lib
0421         if prefix[-1] != os.sep:
0422             prefix = prefix + os.sep
0423 
0424         # XXX this code is essentially the same as the 'byte_compile()
0425         # method of the "install_lib" command, except for the determination
0426         # of the 'prefix' string.  Hmmm.
0427 
0428         if self.compile:
0429             byte_compile(files, optimize=0,
0430                          force=self.force, prefix=prefix, dry_run=self.dry_run)
0431         if self.optimize > 0:
0432             byte_compile(files, optimize=self.optimize,
0433                          force=self.force, prefix=prefix, dry_run=self.dry_run)
0434 
0435 # class build_py
0436 

Generated by PyXR 0.9.4
SourceForge.net Logo