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