sdist.py revision a6483d2e9a9d9a69e9823217bf6b6334bb0f0066
1"""distutils.command.sdist 2 3Implements the Distutils 'sdist' command (create a source distribution).""" 4 5__revision__ = "$Id$" 6 7import sys, os, string 8from types import * 9from glob import glob 10from distutils.core import Command 11from distutils import dir_util, dep_util, file_util, archive_util 12from distutils.text_file import TextFile 13from distutils.errors import * 14from distutils.filelist import FileList 15from distutils import log 16 17 18def show_formats (): 19 """Print all possible values for the 'formats' option (used by 20 the "--help-formats" command-line option). 21 """ 22 from distutils.fancy_getopt import FancyGetopt 23 from distutils.archive_util import ARCHIVE_FORMATS 24 formats=[] 25 for format in ARCHIVE_FORMATS.keys(): 26 formats.append(("formats=" + format, None, 27 ARCHIVE_FORMATS[format][2])) 28 formats.sort() 29 pretty_printer = FancyGetopt(formats) 30 pretty_printer.print_help( 31 "List of available source distribution formats:") 32 33class sdist (Command): 34 35 description = "create a source distribution (tarball, zip file, etc.)" 36 37 user_options = [ 38 ('template=', 't', 39 "name of manifest template file [default: MANIFEST.in]"), 40 ('manifest=', 'm', 41 "name of manifest file [default: MANIFEST]"), 42 ('use-defaults', None, 43 "include the default file set in the manifest " 44 "[default; disable with --no-defaults]"), 45 ('no-defaults', None, 46 "don't include the default file set"), 47 ('prune', None, 48 "specifically exclude files/directories that should not be " 49 "distributed (build tree, RCS/CVS dirs, etc.) " 50 "[default; disable with --no-prune]"), 51 ('no-prune', None, 52 "don't automatically exclude anything"), 53 ('manifest-only', 'o', 54 "just regenerate the manifest and then stop " 55 "(implies --force-manifest)"), 56 ('force-manifest', 'f', 57 "forcibly regenerate the manifest and carry on as usual"), 58 ('formats=', None, 59 "formats for source distribution (comma-separated list)"), 60 ('keep-temp', 'k', 61 "keep the distribution tree around after creating " + 62 "archive file(s)"), 63 ('dist-dir=', 'd', 64 "directory to put the source distribution archive(s) in " 65 "[default: dist]"), 66 ] 67 68 boolean_options = ['use-defaults', 'prune', 69 'manifest-only', 'force-manifest', 70 'keep-temp'] 71 72 help_options = [ 73 ('help-formats', None, 74 "list available distribution formats", show_formats), 75 ] 76 77 negative_opt = {'no-defaults': 'use-defaults', 78 'no-prune': 'prune' } 79 80 default_format = { 'posix': 'gztar', 81 'nt': 'zip' } 82 83 def initialize_options (self): 84 # 'template' and 'manifest' are, respectively, the names of 85 # the manifest template and manifest file. 86 self.template = None 87 self.manifest = None 88 89 # 'use_defaults': if true, we will include the default file set 90 # in the manifest 91 self.use_defaults = 1 92 self.prune = 1 93 94 self.manifest_only = 0 95 self.force_manifest = 0 96 97 self.formats = None 98 self.keep_temp = 0 99 self.dist_dir = None 100 101 self.archive_files = None 102 103 104 def finalize_options (self): 105 if self.manifest is None: 106 self.manifest = "MANIFEST" 107 if self.template is None: 108 self.template = "MANIFEST.in" 109 110 self.ensure_string_list('formats') 111 if self.formats is None: 112 try: 113 self.formats = [self.default_format[os.name]] 114 except KeyError: 115 raise DistutilsPlatformError, \ 116 "don't know how to create source distributions " + \ 117 "on platform %s" % os.name 118 119 bad_format = archive_util.check_archive_formats(self.formats) 120 if bad_format: 121 raise DistutilsOptionError, \ 122 "unknown archive format '%s'" % bad_format 123 124 if self.dist_dir is None: 125 self.dist_dir = "dist" 126 127 128 def run (self): 129 130 # 'filelist' contains the list of files that will make up the 131 # manifest 132 self.filelist = FileList() 133 134 # Ensure that all required meta-data is given; warn if not (but 135 # don't die, it's not *that* serious!) 136 self.check_metadata() 137 138 # Do whatever it takes to get the list of files to process 139 # (process the manifest template, read an existing manifest, 140 # whatever). File list is accumulated in 'self.filelist'. 141 self.get_file_list() 142 143 # If user just wanted us to regenerate the manifest, stop now. 144 if self.manifest_only: 145 return 146 147 # Otherwise, go ahead and create the source distribution tarball, 148 # or zipfile, or whatever. 149 self.make_distribution() 150 151 152 def check_metadata (self): 153 """Ensure that all required elements of meta-data (name, version, 154 URL, (author and author_email) or (maintainer and 155 maintainer_email)) are supplied by the Distribution object; warn if 156 any are missing. 157 """ 158 metadata = self.distribution.metadata 159 160 missing = [] 161 for attr in ('name', 'version', 'url'): 162 if not (hasattr(metadata, attr) and getattr(metadata, attr)): 163 missing.append(attr) 164 165 if missing: 166 self.warn("missing required meta-data: " + 167 string.join(missing, ", ")) 168 169 if metadata.author: 170 if not metadata.author_email: 171 self.warn("missing meta-data: if 'author' supplied, " + 172 "'author_email' must be supplied too") 173 elif metadata.maintainer: 174 if not metadata.maintainer_email: 175 self.warn("missing meta-data: if 'maintainer' supplied, " + 176 "'maintainer_email' must be supplied too") 177 else: 178 self.warn("missing meta-data: either (author and author_email) " + 179 "or (maintainer and maintainer_email) " + 180 "must be supplied") 181 182 # check_metadata () 183 184 185 def get_file_list (self): 186 """Figure out the list of files to include in the source 187 distribution, and put it in 'self.filelist'. This might involve 188 reading the manifest template (and writing the manifest), or just 189 reading the manifest, or just using the default file set -- it all 190 depends on the user's options and the state of the filesystem. 191 """ 192 193 # If we have a manifest template, see if it's newer than the 194 # manifest; if so, we'll regenerate the manifest. 195 template_exists = os.path.isfile(self.template) 196 if template_exists: 197 template_newer = dep_util.newer(self.template, self.manifest) 198 199 # The contents of the manifest file almost certainly depend on the 200 # setup script as well as the manifest template -- so if the setup 201 # script is newer than the manifest, we'll regenerate the manifest 202 # from the template. (Well, not quite: if we already have a 203 # manifest, but there's no template -- which will happen if the 204 # developer elects to generate a manifest some other way -- then we 205 # can't regenerate the manifest, so we don't.) 206 self.debug_print("checking if %s newer than %s" % 207 (self.distribution.script_name, self.manifest)) 208 setup_newer = dep_util.newer(self.distribution.script_name, 209 self.manifest) 210 211 # cases: 212 # 1) no manifest, template exists: generate manifest 213 # (covered by 2a: no manifest == template newer) 214 # 2) manifest & template exist: 215 # 2a) template or setup script newer than manifest: 216 # regenerate manifest 217 # 2b) manifest newer than both: 218 # do nothing (unless --force or --manifest-only) 219 # 3) manifest exists, no template: 220 # do nothing (unless --force or --manifest-only) 221 # 4) no manifest, no template: generate w/ warning ("defaults only") 222 223 manifest_outofdate = (template_exists and 224 (template_newer or setup_newer)) 225 force_regen = self.force_manifest or self.manifest_only 226 manifest_exists = os.path.isfile(self.manifest) 227 neither_exists = (not template_exists and not manifest_exists) 228 229 # Regenerate the manifest if necessary (or if explicitly told to) 230 if manifest_outofdate or neither_exists or force_regen: 231 if not template_exists: 232 self.warn(("manifest template '%s' does not exist " + 233 "(using default file list)") % 234 self.template) 235 self.filelist.findall() 236 237 if self.use_defaults: 238 self.add_defaults() 239 if template_exists: 240 self.read_template() 241 if self.prune: 242 self.prune_file_list() 243 244 self.filelist.sort() 245 self.filelist.remove_duplicates() 246 self.write_manifest() 247 248 # Don't regenerate the manifest, just read it in. 249 else: 250 self.read_manifest() 251 252 # get_file_list () 253 254 255 def add_defaults (self): 256 """Add all the default files to self.filelist: 257 - README or README.txt 258 - setup.py 259 - test/test*.py 260 - all pure Python modules mentioned in setup script 261 - all C sources listed as part of extensions or C libraries 262 in the setup script (doesn't catch C headers!) 263 Warns if (README or README.txt) or setup.py are missing; everything 264 else is optional. 265 """ 266 267 standards = [('README', 'README.txt'), self.distribution.script_name] 268 for fn in standards: 269 if type(fn) is TupleType: 270 alts = fn 271 got_it = 0 272 for fn in alts: 273 if os.path.exists(fn): 274 got_it = 1 275 self.filelist.append(fn) 276 break 277 278 if not got_it: 279 self.warn("standard file not found: should have one of " + 280 string.join(alts, ', ')) 281 else: 282 if os.path.exists(fn): 283 self.filelist.append(fn) 284 else: 285 self.warn("standard file '%s' not found" % fn) 286 287 optional = ['test/test*.py', 'setup.cfg'] 288 for pattern in optional: 289 files = filter(os.path.isfile, glob(pattern)) 290 if files: 291 self.filelist.extend(files) 292 293 if self.distribution.has_pure_modules(): 294 build_py = self.get_finalized_command('build_py') 295 self.filelist.extend(build_py.get_source_files()) 296 297 if self.distribution.has_ext_modules(): 298 build_ext = self.get_finalized_command('build_ext') 299 self.filelist.extend(build_ext.get_source_files()) 300 301 if self.distribution.has_c_libraries(): 302 build_clib = self.get_finalized_command('build_clib') 303 self.filelist.extend(build_clib.get_source_files()) 304 305 # add_defaults () 306 307 308 def read_template (self): 309 """Read and parse manifest template file named by self.template. 310 311 (usually "MANIFEST.in") The parsing and processing is done by 312 'self.filelist', which updates itself accordingly. 313 """ 314 log.info("reading manifest template '%s'", self.template) 315 template = TextFile(self.template, 316 strip_comments=1, 317 skip_blanks=1, 318 join_lines=1, 319 lstrip_ws=1, 320 rstrip_ws=1, 321 collapse_join=1) 322 323 while 1: 324 line = template.readline() 325 if line is None: # end of file 326 break 327 328 try: 329 self.filelist.process_template_line(line) 330 except DistutilsTemplateError, msg: 331 self.warn("%s, line %d: %s" % (template.filename, 332 template.current_line, 333 msg)) 334 335 # read_template () 336 337 338 def prune_file_list (self): 339 """Prune off branches that might slip into the file list as created 340 by 'read_template()', but really don't belong there: 341 * the build tree (typically "build") 342 * the release tree itself (only an issue if we ran "sdist" 343 previously with --keep-temp, or it aborted) 344 * any RCS or CVS directories 345 """ 346 build = self.get_finalized_command('build') 347 base_dir = self.distribution.get_fullname() 348 349 self.filelist.exclude_pattern(None, prefix=build.build_base) 350 self.filelist.exclude_pattern(None, prefix=base_dir) 351 self.filelist.exclude_pattern(r'/(RCS|CVS)/.*', is_regex=1) 352 353 354 def write_manifest (self): 355 """Write the file list in 'self.filelist' (presumably as filled in 356 by 'add_defaults()' and 'read_template()') to the manifest file 357 named by 'self.manifest'. 358 """ 359 self.execute(file_util.write_file, 360 (self.manifest, self.filelist.files), 361 "writing manifest file '%s'" % self.manifest) 362 363 # write_manifest () 364 365 366 def read_manifest (self): 367 """Read the manifest file (named by 'self.manifest') and use it to 368 fill in 'self.filelist', the list of files to include in the source 369 distribution. 370 """ 371 log.info("reading manifest file '%s'", self.manifest) 372 manifest = open(self.manifest) 373 while 1: 374 line = manifest.readline() 375 if line == '': # end of file 376 break 377 if line[-1] == '\n': 378 line = line[0:-1] 379 self.filelist.append(line) 380 381 # read_manifest () 382 383 384 def make_release_tree (self, base_dir, files): 385 """Create the directory tree that will become the source 386 distribution archive. All directories implied by the filenames in 387 'files' are created under 'base_dir', and then we hard link or copy 388 (if hard linking is unavailable) those files into place. 389 Essentially, this duplicates the developer's source tree, but in a 390 directory named after the distribution, containing only the files 391 to be distributed. 392 """ 393 # Create all the directories under 'base_dir' necessary to 394 # put 'files' there; the 'mkpath()' is just so we don't die 395 # if the manifest happens to be empty. 396 self.mkpath(base_dir) 397 dir_util.create_tree(base_dir, files, dry_run=self.dry_run) 398 399 # And walk over the list of files, either making a hard link (if 400 # os.link exists) to each one that doesn't already exist in its 401 # corresponding location under 'base_dir', or copying each file 402 # that's out-of-date in 'base_dir'. (Usually, all files will be 403 # out-of-date, because by default we blow away 'base_dir' when 404 # we're done making the distribution archives.) 405 406 if hasattr(os, 'link'): # can make hard links on this system 407 link = 'hard' 408 msg = "making hard links in %s..." % base_dir 409 else: # nope, have to copy 410 link = None 411 msg = "copying files to %s..." % base_dir 412 413 if not files: 414 log.warn("no files to distribute -- empty manifest?") 415 else: 416 log.info(msg) 417 for file in files: 418 if not os.path.isfile(file): 419 log.warn("'%s' not a regular file -- skipping" % file) 420 else: 421 dest = os.path.join(base_dir, file) 422 self.copy_file(file, dest, link=link) 423 424 self.distribution.metadata.write_pkg_info(base_dir) 425 426 # make_release_tree () 427 428 def make_distribution (self): 429 """Create the source distribution(s). First, we create the release 430 tree with 'make_release_tree()'; then, we create all required 431 archive files (according to 'self.formats') from the release tree. 432 Finally, we clean up by blowing away the release tree (unless 433 'self.keep_temp' is true). The list of archive files created is 434 stored so it can be retrieved later by 'get_archive_files()'. 435 """ 436 # Don't warn about missing meta-data here -- should be (and is!) 437 # done elsewhere. 438 base_dir = self.distribution.get_fullname() 439 base_name = os.path.join(self.dist_dir, base_dir) 440 441 self.make_release_tree(base_dir, self.filelist.files) 442 archive_files = [] # remember names of files we create 443 for fmt in self.formats: 444 file = self.make_archive(base_name, fmt, base_dir=base_dir) 445 archive_files.append(file) 446 447 self.archive_files = archive_files 448 449 if not self.keep_temp: 450 dir_util.remove_tree(base_dir, dry_run=self.dry_run) 451 452 def get_archive_files (self): 453 """Return the list of archive files created when the command 454 was run, or None if the command hasn't run yet. 455 """ 456 return self.archive_files 457 458# class sdist 459