PyXR

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



0001 """distutils.command.sdist
0002 
0003 Implements the Distutils 'sdist' command (create a source distribution)."""
0004 
0005 # This module should be kept compatible with Python 1.5.2.
0006 
0007 __revision__ = "$Id: sdist.py,v 1.58 2004/05/31 19:27:59 tim_one Exp $"
0008 
0009 import sys, os, string
0010 from types import *
0011 from glob import glob
0012 from distutils.core import Command
0013 from distutils import dir_util, dep_util, file_util, archive_util
0014 from distutils.text_file import TextFile
0015 from distutils.errors import *
0016 from distutils.filelist import FileList
0017 from distutils import log
0018 
0019 
0020 def show_formats ():
0021     """Print all possible values for the 'formats' option (used by
0022     the "--help-formats" command-line option).
0023     """
0024     from distutils.fancy_getopt import FancyGetopt
0025     from distutils.archive_util import ARCHIVE_FORMATS
0026     formats=[]
0027     for format in ARCHIVE_FORMATS.keys():
0028         formats.append(("formats=" + format, None,
0029                         ARCHIVE_FORMATS[format][2]))
0030     formats.sort()
0031     pretty_printer = FancyGetopt(formats)
0032     pretty_printer.print_help(
0033         "List of available source distribution formats:")
0034 
0035 class sdist (Command):
0036 
0037     description = "create a source distribution (tarball, zip file, etc.)"
0038 
0039     user_options = [
0040         ('template=', 't',
0041          "name of manifest template file [default: MANIFEST.in]"),
0042         ('manifest=', 'm',
0043          "name of manifest file [default: MANIFEST]"),
0044         ('use-defaults', None,
0045          "include the default file set in the manifest "
0046          "[default; disable with --no-defaults]"),
0047         ('no-defaults', None,
0048          "don't include the default file set"),
0049         ('prune', None,
0050          "specifically exclude files/directories that should not be "
0051          "distributed (build tree, RCS/CVS dirs, etc.) "
0052          "[default; disable with --no-prune]"),
0053         ('no-prune', None,
0054          "don't automatically exclude anything"),
0055         ('manifest-only', 'o',
0056          "just regenerate the manifest and then stop "
0057          "(implies --force-manifest)"),
0058         ('force-manifest', 'f',
0059          "forcibly regenerate the manifest and carry on as usual"),
0060         ('formats=', None,
0061          "formats for source distribution (comma-separated list)"),
0062         ('keep-temp', 'k',
0063          "keep the distribution tree around after creating " +
0064          "archive file(s)"),
0065         ('dist-dir=', 'd',
0066          "directory to put the source distribution archive(s) in "
0067          "[default: dist]"),
0068         ]
0069 
0070     boolean_options = ['use-defaults', 'prune',
0071                        'manifest-only', 'force-manifest',
0072                        'keep-temp']
0073 
0074     help_options = [
0075         ('help-formats', None,
0076          "list available distribution formats", show_formats),
0077         ]
0078 
0079     negative_opt = {'no-defaults': 'use-defaults',
0080                     'no-prune': 'prune' }
0081 
0082     default_format = { 'posix': 'gztar',
0083                        'nt': 'zip' }
0084 
0085     def initialize_options (self):
0086         # 'template' and 'manifest' are, respectively, the names of
0087         # the manifest template and manifest file.
0088         self.template = None
0089         self.manifest = None
0090 
0091         # 'use_defaults': if true, we will include the default file set
0092         # in the manifest
0093         self.use_defaults = 1
0094         self.prune = 1
0095 
0096         self.manifest_only = 0
0097         self.force_manifest = 0
0098 
0099         self.formats = None
0100         self.keep_temp = 0
0101         self.dist_dir = None
0102 
0103         self.archive_files = None
0104 
0105 
0106     def finalize_options (self):
0107         if self.manifest is None:
0108             self.manifest = "MANIFEST"
0109         if self.template is None:
0110             self.template = "MANIFEST.in"
0111 
0112         self.ensure_string_list('formats')
0113         if self.formats is None:
0114             try:
0115                 self.formats = [self.default_format[os.name]]
0116             except KeyError:
0117                 raise DistutilsPlatformError, \
0118                       "don't know how to create source distributions " + \
0119                       "on platform %s" % os.name
0120 
0121         bad_format = archive_util.check_archive_formats(self.formats)
0122         if bad_format:
0123             raise DistutilsOptionError, \
0124                   "unknown archive format '%s'" % bad_format
0125 
0126         if self.dist_dir is None:
0127             self.dist_dir = "dist"
0128 
0129 
0130     def run (self):
0131 
0132         # 'filelist' contains the list of files that will make up the
0133         # manifest
0134         self.filelist = FileList()
0135 
0136         # Ensure that all required meta-data is given; warn if not (but
0137         # don't die, it's not *that* serious!)
0138         self.check_metadata()
0139 
0140         # Do whatever it takes to get the list of files to process
0141         # (process the manifest template, read an existing manifest,
0142         # whatever).  File list is accumulated in 'self.filelist'.
0143         self.get_file_list()
0144 
0145         # If user just wanted us to regenerate the manifest, stop now.
0146         if self.manifest_only:
0147             return
0148 
0149         # Otherwise, go ahead and create the source distribution tarball,
0150         # or zipfile, or whatever.
0151         self.make_distribution()
0152 
0153 
0154     def check_metadata (self):
0155         """Ensure that all required elements of meta-data (name, version,
0156         URL, (author and author_email) or (maintainer and
0157         maintainer_email)) are supplied by the Distribution object; warn if
0158         any are missing.
0159         """
0160         metadata = self.distribution.metadata
0161 
0162         missing = []
0163         for attr in ('name', 'version', 'url'):
0164             if not (hasattr(metadata, attr) and getattr(metadata, attr)):
0165                 missing.append(attr)
0166 
0167         if missing:
0168             self.warn("missing required meta-data: " +
0169                       string.join(missing, ", "))
0170 
0171         if metadata.author:
0172             if not metadata.author_email:
0173                 self.warn("missing meta-data: if 'author' supplied, " +
0174                           "'author_email' must be supplied too")
0175         elif metadata.maintainer:
0176             if not metadata.maintainer_email:
0177                 self.warn("missing meta-data: if 'maintainer' supplied, " +
0178                           "'maintainer_email' must be supplied too")
0179         else:
0180             self.warn("missing meta-data: either (author and author_email) " +
0181                       "or (maintainer and maintainer_email) " +
0182                       "must be supplied")
0183 
0184     # check_metadata ()
0185 
0186 
0187     def get_file_list (self):
0188         """Figure out the list of files to include in the source
0189         distribution, and put it in 'self.filelist'.  This might involve
0190         reading the manifest template (and writing the manifest), or just
0191         reading the manifest, or just using the default file set -- it all
0192         depends on the user's options and the state of the filesystem.
0193         """
0194 
0195         # If we have a manifest template, see if it's newer than the
0196         # manifest; if so, we'll regenerate the manifest.
0197         template_exists = os.path.isfile(self.template)
0198         if template_exists:
0199             template_newer = dep_util.newer(self.template, self.manifest)
0200 
0201         # The contents of the manifest file almost certainly depend on the
0202         # setup script as well as the manifest template -- so if the setup
0203         # script is newer than the manifest, we'll regenerate the manifest
0204         # from the template.  (Well, not quite: if we already have a
0205         # manifest, but there's no template -- which will happen if the
0206         # developer elects to generate a manifest some other way -- then we
0207         # can't regenerate the manifest, so we don't.)
0208         self.debug_print("checking if %s newer than %s" %
0209                          (self.distribution.script_name, self.manifest))
0210         setup_newer = dep_util.newer(self.distribution.script_name,
0211                                      self.manifest)
0212 
0213         # cases:
0214         #   1) no manifest, template exists: generate manifest
0215         #      (covered by 2a: no manifest == template newer)
0216         #   2) manifest & template exist:
0217         #      2a) template or setup script newer than manifest:
0218         #          regenerate manifest
0219         #      2b) manifest newer than both:
0220         #          do nothing (unless --force or --manifest-only)
0221         #   3) manifest exists, no template:
0222         #      do nothing (unless --force or --manifest-only)
0223         #   4) no manifest, no template: generate w/ warning ("defaults only")
0224 
0225         manifest_outofdate = (template_exists and
0226                               (template_newer or setup_newer))
0227         force_regen = self.force_manifest or self.manifest_only
0228         manifest_exists = os.path.isfile(self.manifest)
0229         neither_exists = (not template_exists and not manifest_exists)
0230 
0231         # Regenerate the manifest if necessary (or if explicitly told to)
0232         if manifest_outofdate or neither_exists or force_regen:
0233             if not template_exists:
0234                 self.warn(("manifest template '%s' does not exist " +
0235                            "(using default file list)") %
0236                           self.template)
0237             self.filelist.findall()
0238 
0239             if self.use_defaults:
0240                 self.add_defaults()
0241             if template_exists:
0242                 self.read_template()
0243             if self.prune:
0244                 self.prune_file_list()
0245 
0246             self.filelist.sort()
0247             self.filelist.remove_duplicates()
0248             self.write_manifest()
0249 
0250         # Don't regenerate the manifest, just read it in.
0251         else:
0252             self.read_manifest()
0253 
0254     # get_file_list ()
0255 
0256 
0257     def add_defaults (self):
0258         """Add all the default files to self.filelist:
0259           - README or README.txt
0260           - setup.py
0261           - test/test*.py
0262           - all pure Python modules mentioned in setup script
0263           - all C sources listed as part of extensions or C libraries
0264             in the setup script (doesn't catch C headers!)
0265         Warns if (README or README.txt) or setup.py are missing; everything
0266         else is optional.
0267         """
0268 
0269         standards = [('README', 'README.txt'), self.distribution.script_name]
0270         for fn in standards:
0271             if type(fn) is TupleType:
0272                 alts = fn
0273                 got_it = 0
0274                 for fn in alts:
0275                     if os.path.exists(fn):
0276                         got_it = 1
0277                         self.filelist.append(fn)
0278                         break
0279 
0280                 if not got_it:
0281                     self.warn("standard file not found: should have one of " +
0282                               string.join(alts, ', '))
0283             else:
0284                 if os.path.exists(fn):
0285                     self.filelist.append(fn)
0286                 else:
0287                     self.warn("standard file '%s' not found" % fn)
0288 
0289         optional = ['test/test*.py', 'setup.cfg']
0290         for pattern in optional:
0291             files = filter(os.path.isfile, glob(pattern))
0292             if files:
0293                 self.filelist.extend(files)
0294 
0295         if self.distribution.has_pure_modules():
0296             build_py = self.get_finalized_command('build_py')
0297             self.filelist.extend(build_py.get_source_files())
0298 
0299         if self.distribution.has_ext_modules():
0300             build_ext = self.get_finalized_command('build_ext')
0301             self.filelist.extend(build_ext.get_source_files())
0302 
0303         if self.distribution.has_c_libraries():
0304             build_clib = self.get_finalized_command('build_clib')
0305             self.filelist.extend(build_clib.get_source_files())
0306 
0307         if self.distribution.has_scripts():
0308             build_scripts = self.get_finalized_command('build_scripts')
0309             self.filelist.extend(build_scripts.get_source_files())
0310 
0311     # add_defaults ()
0312 
0313 
0314     def read_template (self):
0315         """Read and parse manifest template file named by self.template.
0316 
0317         (usually "MANIFEST.in") The parsing and processing is done by
0318         'self.filelist', which updates itself accordingly.
0319         """
0320         log.info("reading manifest template '%s'", self.template)
0321         template = TextFile(self.template,
0322                             strip_comments=1,
0323                             skip_blanks=1,
0324                             join_lines=1,
0325                             lstrip_ws=1,
0326                             rstrip_ws=1,
0327                             collapse_join=1)
0328 
0329         while 1:
0330             line = template.readline()
0331             if line is None:            # end of file
0332                 break
0333 
0334             try:
0335                 self.filelist.process_template_line(line)
0336             except DistutilsTemplateError, msg:
0337                 self.warn("%s, line %d: %s" % (template.filename,
0338                                                template.current_line,
0339                                                msg))
0340 
0341     # read_template ()
0342 
0343 
0344     def prune_file_list (self):
0345         """Prune off branches that might slip into the file list as created
0346         by 'read_template()', but really don't belong there:
0347           * the build tree (typically "build")
0348           * the release tree itself (only an issue if we ran "sdist"
0349             previously with --keep-temp, or it aborted)
0350           * any RCS, CVS and .svn directories
0351         """
0352         build = self.get_finalized_command('build')
0353         base_dir = self.distribution.get_fullname()
0354 
0355         self.filelist.exclude_pattern(None, prefix=build.build_base)
0356         self.filelist.exclude_pattern(None, prefix=base_dir)
0357         self.filelist.exclude_pattern(r'/(RCS|CVS|\.svn)/.*', is_regex=1)
0358 
0359 
0360     def write_manifest (self):
0361         """Write the file list in 'self.filelist' (presumably as filled in
0362         by 'add_defaults()' and 'read_template()') to the manifest file
0363         named by 'self.manifest'.
0364         """
0365         self.execute(file_util.write_file,
0366                      (self.manifest, self.filelist.files),
0367                      "writing manifest file '%s'" % self.manifest)
0368 
0369     # write_manifest ()
0370 
0371 
0372     def read_manifest (self):
0373         """Read the manifest file (named by 'self.manifest') and use it to
0374         fill in 'self.filelist', the list of files to include in the source
0375         distribution.
0376         """
0377         log.info("reading manifest file '%s'", self.manifest)
0378         manifest = open(self.manifest)
0379         while 1:
0380             line = manifest.readline()
0381             if line == '':              # end of file
0382                 break
0383             if line[-1] == '\n':
0384                 line = line[0:-1]
0385             self.filelist.append(line)
0386 
0387     # read_manifest ()
0388 
0389 
0390     def make_release_tree (self, base_dir, files):
0391         """Create the directory tree that will become the source
0392         distribution archive.  All directories implied by the filenames in
0393         'files' are created under 'base_dir', and then we hard link or copy
0394         (if hard linking is unavailable) those files into place.
0395         Essentially, this duplicates the developer's source tree, but in a
0396         directory named after the distribution, containing only the files
0397         to be distributed.
0398         """
0399         # Create all the directories under 'base_dir' necessary to
0400         # put 'files' there; the 'mkpath()' is just so we don't die
0401         # if the manifest happens to be empty.
0402         self.mkpath(base_dir)
0403         dir_util.create_tree(base_dir, files, dry_run=self.dry_run)
0404 
0405         # And walk over the list of files, either making a hard link (if
0406         # os.link exists) to each one that doesn't already exist in its
0407         # corresponding location under 'base_dir', or copying each file
0408         # that's out-of-date in 'base_dir'.  (Usually, all files will be
0409         # out-of-date, because by default we blow away 'base_dir' when
0410         # we're done making the distribution archives.)
0411 
0412         if hasattr(os, 'link'):        # can make hard links on this system
0413             link = 'hard'
0414             msg = "making hard links in %s..." % base_dir
0415         else:                           # nope, have to copy
0416             link = None
0417             msg = "copying files to %s..." % base_dir
0418 
0419         if not files:
0420             log.warn("no files to distribute -- empty manifest?")
0421         else:
0422             log.info(msg)
0423         for file in files:
0424             if not os.path.isfile(file):
0425                 log.warn("'%s' not a regular file -- skipping" % file)
0426             else:
0427                 dest = os.path.join(base_dir, file)
0428                 self.copy_file(file, dest, link=link)
0429 
0430         self.distribution.metadata.write_pkg_info(base_dir)
0431 
0432     # make_release_tree ()
0433 
0434     def make_distribution (self):
0435         """Create the source distribution(s).  First, we create the release
0436         tree with 'make_release_tree()'; then, we create all required
0437         archive files (according to 'self.formats') from the release tree.
0438         Finally, we clean up by blowing away the release tree (unless
0439         'self.keep_temp' is true).  The list of archive files created is
0440         stored so it can be retrieved later by 'get_archive_files()'.
0441         """
0442         # Don't warn about missing meta-data here -- should be (and is!)
0443         # done elsewhere.
0444         base_dir = self.distribution.get_fullname()
0445         base_name = os.path.join(self.dist_dir, base_dir)
0446 
0447         self.make_release_tree(base_dir, self.filelist.files)
0448         archive_files = []              # remember names of files we create
0449         for fmt in self.formats:
0450             file = self.make_archive(base_name, fmt, base_dir=base_dir)
0451             archive_files.append(file)
0452 
0453         self.archive_files = archive_files
0454 
0455         if not self.keep_temp:
0456             dir_util.remove_tree(base_dir, dry_run=self.dry_run)
0457 
0458     def get_archive_files (self):
0459         """Return the list of archive files created when the command
0460         was run, or None if the command hasn't run yet.
0461         """
0462         return self.archive_files
0463 
0464 # class sdist
0465 

Generated by PyXR 0.9.4
SourceForge.net Logo