1 """distutils.command.sdist
3 Implements the Distutils 'sdist' command (create a source distribution)."""
11 from warnings
import warn
13 from distutils
.core
import Command
14 from distutils
import dir_util
, dep_util
, file_util
, archive_util
15 from distutils
.text_file
import TextFile
16 from distutils
.errors
import (DistutilsPlatformError
, DistutilsOptionError
,
17 DistutilsTemplateError
)
18 from distutils
.filelist
import FileList
19 from distutils
import log
20 from distutils
.util
import convert_path
23 """Print all possible values for the 'formats' option (used by
24 the "--help-formats" command-line option).
26 from distutils
.fancy_getopt
import FancyGetopt
27 from distutils
.archive_util
import ARCHIVE_FORMATS
29 for format
in ARCHIVE_FORMATS
.keys():
30 formats
.append(("formats=" + format
, None,
31 ARCHIVE_FORMATS
[format
][2]))
33 FancyGetopt(formats
).print_help(
34 "List of available source distribution formats:")
38 description
= "create a source distribution (tarball, zip file, etc.)"
40 def checking_metadata(self
):
41 """Callable used for the check sub-command.
43 Placed here so user_options can view it"""
44 return self
.metadata_check
48 "name of manifest template file [default: MANIFEST.in]"),
50 "name of manifest file [default: MANIFEST]"),
51 ('use-defaults', None,
52 "include the default file set in the manifest "
53 "[default; disable with --no-defaults]"),
55 "don't include the default file set"),
57 "specifically exclude files/directories that should not be "
58 "distributed (build tree, RCS/CVS dirs, etc.) "
59 "[default; disable with --no-prune]"),
61 "don't automatically exclude anything"),
62 ('manifest-only', 'o',
63 "just regenerate the manifest and then stop "
64 "(implies --force-manifest)"),
65 ('force-manifest', 'f',
66 "forcibly regenerate the manifest and carry on as usual. "
67 "Deprecated: now the manifest is always regenerated."),
69 "formats for source distribution (comma-separated list)"),
71 "keep the distribution tree around after creating " +
74 "directory to put the source distribution archive(s) in "
76 ('metadata-check', None,
77 "Ensure that all required elements of meta-data "
78 "are supplied. Warn if any missing. [default]"),
80 "Owner name used when creating a tar file [default: current user]"),
82 "Group name used when creating a tar file [default: current group]"),
85 boolean_options
= ['use-defaults', 'prune',
86 'manifest-only', 'force-manifest',
87 'keep-temp', 'metadata-check']
90 ('help-formats', None,
91 "list available distribution formats", show_formats
),
94 negative_opt
= {'no-defaults': 'use-defaults',
97 default_format
= {'posix': 'gztar',
100 sub_commands
= [('check', checking_metadata
)]
102 def initialize_options(self
):
103 # 'template' and 'manifest' are, respectively, the names of
104 # the manifest template and manifest file.
108 # 'use_defaults': if true, we will include the default file set
110 self
.use_defaults
= 1
113 self
.manifest_only
= 0
114 self
.force_manifest
= 0
120 self
.archive_files
= None
121 self
.metadata_check
= 1
125 def finalize_options(self
):
126 if self
.manifest
is None:
127 self
.manifest
= "MANIFEST"
128 if self
.template
is None:
129 self
.template
= "MANIFEST.in"
131 self
.ensure_string_list('formats')
132 if self
.formats
is None:
134 self
.formats
= [self
.default_format
[os
.name
]]
136 raise DistutilsPlatformError
, \
137 "don't know how to create source distributions " + \
138 "on platform %s" % os
.name
140 bad_format
= archive_util
.check_archive_formats(self
.formats
)
142 raise DistutilsOptionError
, \
143 "unknown archive format '%s'" % bad_format
145 if self
.dist_dir
is None:
146 self
.dist_dir
= "dist"
149 # 'filelist' contains the list of files that will make up the
151 self
.filelist
= FileList()
154 for cmd_name
in self
.get_sub_commands():
155 self
.run_command(cmd_name
)
157 # Do whatever it takes to get the list of files to process
158 # (process the manifest template, read an existing manifest,
159 # whatever). File list is accumulated in 'self.filelist'.
162 # If user just wanted us to regenerate the manifest, stop now.
163 if self
.manifest_only
:
166 # Otherwise, go ahead and create the source distribution tarball,
167 # or zipfile, or whatever.
168 self
.make_distribution()
170 def check_metadata(self
):
171 """Deprecated API."""
172 warn("distutils.command.sdist.check_metadata is deprecated, \
173 use the check command instead", PendingDeprecationWarning
)
174 check
= self
.distribution
.get_command_obj('check')
175 check
.ensure_finalized()
178 def get_file_list(self
):
179 """Figure out the list of files to include in the source
180 distribution, and put it in 'self.filelist'. This might involve
181 reading the manifest template (and writing the manifest), or just
182 reading the manifest, or just using the default file set -- it all
183 depends on the user's options.
186 # the file list is recalculated everytime because
187 # even if MANIFEST.in or setup.py are not changed
188 # the user might have added some files in the tree that
189 # need to be included.
191 # This makes --force the default and only behavior.
192 template_exists
= os
.path
.isfile(self
.template
)
193 if not template_exists
:
194 self
.warn(("manifest template '%s' does not exist " +
195 "(using default file list)") %
197 self
.filelist
.findall()
199 if self
.use_defaults
:
206 self
.prune_file_list()
209 self
.filelist
.remove_duplicates()
210 self
.write_manifest()
212 def add_defaults(self
):
213 """Add all the default files to self.filelist:
214 - README or README.txt
217 - all pure Python modules mentioned in setup script
218 - all files pointed by package_data (build_py)
219 - all files defined in data_files.
220 - all files defined as scripts.
221 - all C sources listed as part of extensions or C libraries
222 in the setup script (doesn't catch C headers!)
223 Warns if (README or README.txt) or setup.py are missing; everything
227 standards
= [('README', 'README.txt'), self
.distribution
.script_name
]
229 if isinstance(fn
, tuple):
233 if os
.path
.exists(fn
):
235 self
.filelist
.append(fn
)
239 self
.warn("standard file not found: should have one of " +
240 string
.join(alts
, ', '))
242 if os
.path
.exists(fn
):
243 self
.filelist
.append(fn
)
245 self
.warn("standard file '%s' not found" % fn
)
247 optional
= ['test/test*.py', 'setup.cfg']
248 for pattern
in optional
:
249 files
= filter(os
.path
.isfile
, glob(pattern
))
251 self
.filelist
.extend(files
)
253 # build_py is used to get:
255 # - files defined in package_data
256 build_py
= self
.get_finalized_command('build_py')
258 # getting python files
259 if self
.distribution
.has_pure_modules():
260 self
.filelist
.extend(build_py
.get_source_files())
262 # getting package_data files
263 # (computed in build_py.data_files by build_py.finalize_options)
264 for pkg
, src_dir
, build_dir
, filenames
in build_py
.data_files
:
265 for filename
in filenames
:
266 self
.filelist
.append(os
.path
.join(src_dir
, filename
))
268 # getting distribution.data_files
269 if self
.distribution
.has_data_files():
270 for item
in self
.distribution
.data_files
:
271 if isinstance(item
, str): # plain file
272 item
= convert_path(item
)
273 if os
.path
.isfile(item
):
274 self
.filelist
.append(item
)
275 else: # a (dirname, filenames) tuple
276 dirname
, filenames
= item
279 if os
.path
.isfile(f
):
280 self
.filelist
.append(f
)
282 if self
.distribution
.has_ext_modules():
283 build_ext
= self
.get_finalized_command('build_ext')
284 self
.filelist
.extend(build_ext
.get_source_files())
286 if self
.distribution
.has_c_libraries():
287 build_clib
= self
.get_finalized_command('build_clib')
288 self
.filelist
.extend(build_clib
.get_source_files())
290 if self
.distribution
.has_scripts():
291 build_scripts
= self
.get_finalized_command('build_scripts')
292 self
.filelist
.extend(build_scripts
.get_source_files())
294 def read_template(self
):
295 """Read and parse manifest template file named by self.template.
297 (usually "MANIFEST.in") The parsing and processing is done by
298 'self.filelist', which updates itself accordingly.
300 log
.info("reading manifest template '%s'", self
.template
)
301 template
= TextFile(self
.template
,
311 line
= template
.readline()
312 if line
is None: # end of file
316 self
.filelist
.process_template_line(line
)
317 except DistutilsTemplateError
, msg
:
318 self
.warn("%s, line %d: %s" % (template
.filename
,
319 template
.current_line
,
324 def prune_file_list(self
):
325 """Prune off branches that might slip into the file list as created
326 by 'read_template()', but really don't belong there:
327 * the build tree (typically "build")
328 * the release tree itself (only an issue if we ran "sdist"
329 previously with --keep-temp, or it aborted)
330 * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories
332 build
= self
.get_finalized_command('build')
333 base_dir
= self
.distribution
.get_fullname()
335 self
.filelist
.exclude_pattern(None, prefix
=build
.build_base
)
336 self
.filelist
.exclude_pattern(None, prefix
=base_dir
)
338 # pruning out vcs directories
339 # both separators are used under win32
340 if sys
.platform
== 'win32':
345 vcs_dirs
= ['RCS', 'CVS', r
'\.svn', r
'\.hg', r
'\.git', r
'\.bzr',
347 vcs_ptrn
= r
'(^|%s)(%s)(%s).*' % (seps
, '|'.join(vcs_dirs
), seps
)
348 self
.filelist
.exclude_pattern(vcs_ptrn
, is_regex
=1)
350 def write_manifest(self
):
351 """Write the file list in 'self.filelist' (presumably as filled in
352 by 'add_defaults()' and 'read_template()') to the manifest file
353 named by 'self.manifest'.
355 if os
.path
.isfile(self
.manifest
):
356 fp
= open(self
.manifest
)
358 first_line
= fp
.readline()
362 if first_line
!= '# file GENERATED by distutils, do NOT edit\n':
363 log
.info("not writing to manually maintained "
364 "manifest file '%s'" % self
.manifest
)
367 content
= self
.filelist
.files
[:]
368 content
.insert(0, '# file GENERATED by distutils, do NOT edit')
369 self
.execute(file_util
.write_file
, (self
.manifest
, content
),
370 "writing manifest file '%s'" % self
.manifest
)
372 def read_manifest(self
):
373 """Read the manifest file (named by 'self.manifest') and use it to
374 fill in 'self.filelist', the list of files to include in the source
377 log
.info("reading manifest file '%s'", self
.manifest
)
378 manifest
= open(self
.manifest
)
380 line
= manifest
.readline()
381 if line
== '': # end of file
385 self
.filelist
.append(line
)
388 def make_release_tree(self
, base_dir
, files
):
389 """Create the directory tree that will become the source
390 distribution archive. All directories implied by the filenames in
391 'files' are created under 'base_dir', and then we hard link or copy
392 (if hard linking is unavailable) those files into place.
393 Essentially, this duplicates the developer's source tree, but in a
394 directory named after the distribution, containing only the files
397 # Create all the directories under 'base_dir' necessary to
398 # put 'files' there; the 'mkpath()' is just so we don't die
399 # if the manifest happens to be empty.
400 self
.mkpath(base_dir
)
401 dir_util
.create_tree(base_dir
, files
, dry_run
=self
.dry_run
)
403 # And walk over the list of files, either making a hard link (if
404 # os.link exists) to each one that doesn't already exist in its
405 # corresponding location under 'base_dir', or copying each file
406 # that's out-of-date in 'base_dir'. (Usually, all files will be
407 # out-of-date, because by default we blow away 'base_dir' when
408 # we're done making the distribution archives.)
410 if hasattr(os
, 'link'): # can make hard links on this system
412 msg
= "making hard links in %s..." % base_dir
413 else: # nope, have to copy
415 msg
= "copying files to %s..." % base_dir
418 log
.warn("no files to distribute -- empty manifest?")
422 if not os
.path
.isfile(file):
423 log
.warn("'%s' not a regular file -- skipping" % file)
425 dest
= os
.path
.join(base_dir
, file)
426 self
.copy_file(file, dest
, link
=link
)
428 self
.distribution
.metadata
.write_pkg_info(base_dir
)
430 def make_distribution(self
):
431 """Create the source distribution(s). First, we create the release
432 tree with 'make_release_tree()'; then, we create all required
433 archive files (according to 'self.formats') from the release tree.
434 Finally, we clean up by blowing away the release tree (unless
435 'self.keep_temp' is true). The list of archive files created is
436 stored so it can be retrieved later by 'get_archive_files()'.
438 # Don't warn about missing meta-data here -- should be (and is!)
440 base_dir
= self
.distribution
.get_fullname()
441 base_name
= os
.path
.join(self
.dist_dir
, base_dir
)
443 self
.make_release_tree(base_dir
, self
.filelist
.files
)
444 archive_files
= [] # remember names of files we create
445 # tar archive must be created last to avoid overwrite and remove
446 if 'tar' in self
.formats
:
447 self
.formats
.append(self
.formats
.pop(self
.formats
.index('tar')))
449 for fmt
in self
.formats
:
450 file = self
.make_archive(base_name
, fmt
, base_dir
=base_dir
,
451 owner
=self
.owner
, group
=self
.group
)
452 archive_files
.append(file)
453 self
.distribution
.dist_files
.append(('sdist', '', file))
455 self
.archive_files
= archive_files
457 if not self
.keep_temp
:
458 dir_util
.remove_tree(base_dir
, dry_run
=self
.dry_run
)
460 def get_archive_files(self
):
461 """Return the list of archive files created when the command
462 was run, or None if the command hasn't run yet.
464 return self
.archive_files