1 """distutils.command.sdist
3 Implements the Distutils 'sdist' command (create a source distribution)."""
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
20 """Print all possible values for the 'formats' option (used by
21 the "--help-formats" command-line option).
23 from distutils
.fancy_getopt
import FancyGetopt
24 from distutils
.archive_util
import ARCHIVE_FORMATS
26 for format
in ARCHIVE_FORMATS
.keys():
27 formats
.append(("formats=" + format
, None,
28 ARCHIVE_FORMATS
[format
][2]))
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.)"
40 "name of manifest template file [default: MANIFEST.in]"),
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]"),
47 "don't include the default file set"),
49 "specifically exclude files/directories that should not be "
50 "distributed (build tree, RCS/CVS dirs, etc.) "
51 "[default; disable with --no-prune]"),
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"),
60 "formats for source distribution (comma-separated list)"),
62 "keep the distribution tree around after creating " +
65 "directory to put the source distribution archive(s) in "
69 boolean_options
= ['use-defaults', 'prune',
70 'manifest-only', 'force-manifest',
74 ('help-formats', None,
75 "list available distribution formats", show_formats
),
78 negative_opt
= {'no-defaults': 'use-defaults',
81 default_format
= { 'posix': 'gztar',
84 def initialize_options (self
):
85 # 'template' and 'manifest' are, respectively, the names of
86 # the manifest template and manifest file.
90 # 'use_defaults': if true, we will include the default file set
95 self
.manifest_only
= 0
96 self
.force_manifest
= 0
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:
114 self
.formats
= [self
.default_format
[os
.name
]]
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
)
122 raise DistutilsOptionError
, \
123 "unknown archive format '%s'" % bad_format
125 if self
.dist_dir
is None:
126 self
.dist_dir
= "dist"
131 # 'filelist' contains the list of files that will make up the
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'.
144 # If user just wanted us to regenerate the manifest, stop now.
145 if self
.manifest_only
:
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
159 metadata
= self
.distribution
.metadata
162 for attr
in ('name', 'version', 'url'):
163 if not (hasattr(metadata
, attr
) and getattr(metadata
, attr
)):
167 self
.warn("missing required meta-data: " +
168 string
.join(missing
, ", "))
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")
179 self
.warn("missing meta-data: either (author and author_email) " +
180 "or (maintainer and maintainer_email) " +
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
)
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
,
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)") %
236 self
.filelist
.findall()
238 if self
.use_defaults
:
243 self
.prune_file_list()
246 self
.filelist
.remove_duplicates()
247 self
.write_manifest()
249 # Don't regenerate the manifest, just read it in.
256 def add_defaults (self
):
257 """Add all the default files to self.filelist:
258 - README or README.txt
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
268 standards
= [('README', 'README.txt'), self
.distribution
.script_name
]
270 if type(fn
) is TupleType
:
274 if os
.path
.exists(fn
):
276 self
.filelist
.append(fn
)
280 self
.warn("standard file not found: should have one of " +
281 string
.join(alts
, ', '))
283 if os
.path
.exists(fn
):
284 self
.filelist
.append(fn
)
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
))
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())
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
,
329 line
= template
.readline()
330 if line
is None: # end of file
334 self
.filelist
.process_template_line(line
)
335 except DistutilsTemplateError
, msg
:
336 self
.warn("%s, line %d: %s" % (template
.filename
,
337 template
.current_line
,
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':
364 vcs_dirs
= ['RCS', 'CVS', r
'\.svn', r
'\.hg', r
'\.git', r
'\.bzr',
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
)
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
386 log
.info("reading manifest file '%s'", self
.manifest
)
387 manifest
= open(self
.manifest
)
389 line
= manifest
.readline()
390 if line
== '': # end of file
394 self
.filelist
.append(line
)
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
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
424 msg
= "making hard links in %s..." % base_dir
425 else: # nope, have to copy
427 msg
= "copying files to %s..." % base_dir
430 log
.warn("no files to distribute -- empty manifest?")
434 if not os
.path
.isfile(file):
435 log
.warn("'%s' not a regular file -- skipping" % file)
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!)
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