import os
from distutils import dir_util,filelist,util
from distutils.command import sdist
class SDist(sdist.sdist):
"""
Extended 'sdist' command that uses 'manifest_templates' from the
distribution options instead of manifest files.
"""
command_name = 'sdist'
description = "create a source distribution (tarball, zip file, etc.)"
user_options = [
('use-defaults', None,
"include the default file set in the manifest "
"[default; disable with --no-defaults]"),
('no-defaults', None,
"don't include the default file set"),
('prune', None,
"specifically exclude files/directories that should not be "
"distributed (build tree, RCS/CVS dirs, etc.) "
"[default; disable with --no-prune]"),
('no-prune', None,
"don't automatically exclude anything"),
('formats=', None,
"formats for source distribution (comma-separated list)"),
('keep-temp', 'k',
"keep the distribution tree around after creating " +
"archive file(s)"),
('dist-dir=', 'd',
"directory to put the source distribution archive(s) in "
"[default: dist]"),
]
# sdist.sdist defines this, so we must undefine it
negative_opt = {}
def get_file_list(self):
"""
Figure out the list of files to include in the source
distribution, and put it in 'self.filelist'.
"""
self.filelist.set_allfiles(self.distribution.get_allfiles())
# Add default file set to 'files'
if self.use_defaults:
self.add_defaults()
# Process manifest template lines
if not self.distribution.manifest_templates:
self.announce("using default file list only", 1)
else:
for line in self.distribution.manifest_templates:
try:
self.filelist.process_template_line(line)
except DistutilsTemplateError, msg:
self.warn(str(msg))
# Prune away any directories that don't belong in the source
# distribution
if self.prune:
self.prune_file_list(self.filelist)
# File list now complete -- sort it so that higher-level files
# come first
self.filelist.sort()
# Remove duplicates from the file list
self.filelist.remove_duplicates()
return
def add_defaults(self):
"""Add all the default files to self.filelist:
- setup.py
- README or README.txt (in all directories)
- all pure Python modules mentioned in setup script
- all C sources listed as part of extensions or C libraries
in the setup script (doesn't catch C headers!)
"""
self.filelist.include_pattern('README*')
self.filelist.include_pattern('COPYRIGHT')
# Add the files from the distribution
self.filelist.extend(self.distribution.get_source_files())
return
def prune_file_list(self, filelist):
"""Prune off branches that might slip into the file list as created
by 'read_template()', but really don't belong there:
* the build tree (typically "build")
* the release tree itself (only an issue if we ran "sdist"
previously with --keep-temp, or it aborted)
* any RCS or CVS directories
"""
config = self.get_finalized_command('config')
filelist.exclude_pattern(config.cache_filename, anchor=1)
build = self.get_finalized_command('build')
filelist.exclude_pattern(None, prefix=build.build_base)
filelist.exclude_pattern(None, prefix=self.dist_dir)
# Trim the source code tree
base_dir = self.distribution.get_fullname()
filelist.exclude_pattern(None, prefix=base_dir)
# Trim the documentation tree
base_dir = '%s-docs-%s' % (self.distribution.get_name(),
self.distribution.get_version())
filelist.exclude_pattern(None, prefix=base_dir)
return filelist
def make_distribution(self):
# Ensure that all files in the source tree are included in the
# would be distribution
if not self.check_distribution():
return
# Create the source code distribution
sdist.sdist.make_distribution(self)
# Create the documentation distribution
base_dir = '%s-docs-%s' % (self.distribution.get_name(),
self.distribution.get_version())
base_name = os.path.join(self.dist_dir, base_dir)
# Make the documentation tree
install = self.reinitialize_command('install')
install.install_docs = base_dir
install.ensure_finalized()
for command_name in ('install_text', 'install_html'):
command = self.reinitialize_command(command_name)
command.ensure_finalized()
command.run()
# Add a PKG-INFO entry to allow for uploading to PyPI.
self.distribution.metadata.write_pkg_info(base_dir)
# Archive the documentation tree
for fmt in self.formats:
file = self.make_archive(base_name, fmt, base_dir=base_dir)
self.archive_files.append(file)
if hasattr(self.distribution, 'dist_files'):
# Save it for the "upload" command as well.
self.distribution.dist_files.append(('sdist', '', file))
if not self.keep_temp:
dir_util.remove_tree(base_dir, dry_run=self.dry_run)
return
def check_distribution(self):
# Start out with all files included
allfiles = filelist.FileList()
allfiles.set_allfiles([])
allfiles.extend(self.distribution.get_allfiles())
# Prune the same files as the distribution filelist
self.prune_file_list(allfiles)
# Exclude files included by other sub-packages
validate_distributions = [self.distribution]
if self.distribution.main_distribution:
main_distribution = self.distribution.main_distribution
validate_distributions.append(main_distribution)
for name in main_distribution.package_options:
dist = main_distribution.get_package_distribution(name)
if dist is not self.distribution:
# Remove the other sub-package source files
for filename in dist.get_source_files():
filename = util.convert_path(filename)
allfiles.exclude_pattern(filename)
validate_distributions.append(dist)
# Process the ignorable files for all available distributions
for distribution in validate_distributions:
for line in distribution.validate_templates:
try:
allfiles.process_template_line(line)
except DistutilsTemplateError, msg:
self.warn('in %s: %s' % (dist.package_file, msg))
# File list now complete -- sort it so that higher-level files
# come first
allfiles.sort()
# Remove duplicates from the file list
allfiles.remove_duplicates()
# Ensure file paths are formatted the same
# This removes any dot-slashes and converts all slashes to
# OS-specific separators.
dist_files = map(os.path.normpath, self.filelist.files)
src_files = map(os.path.normpath, allfiles.files)
valid = 1
for file in src_files:
if file not in dist_files:
self.warn('missing from source distribution: %s' % file)
valid = 0
if not valid:
self.warn('Not all source files in distribution')
prompt = raw_input('Do you want to continue? (yes/no)')
valid = prompt.lower() in ['y', 'yes']
return valid
|