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