blob: 3b91170cb381b51ef5190039137292dfc82dc789 [file] [log] [blame]
Greg Warda82122b2000-02-17 23:56:15 +00001"""distutils.command.sdist
2
3Implements the Distutils 'sdist' command (create a source distribution)."""
4
5# created 1999/09/22, Greg Ward
6
Greg Ward3ce77fd2000-03-02 01:49:45 +00007__revision__ = "$Id$"
Greg Warda82122b2000-02-17 23:56:15 +00008
9import sys, os, string, re
10import fnmatch
11from types import *
12from glob import glob
Greg Warda82122b2000-02-17 23:56:15 +000013from distutils.core import Command
Greg Wardf1fe1032000-06-08 00:14:18 +000014from distutils.util import \
15 convert_path, create_tree, remove_tree, newer, write_file, \
Greg Ward34593812000-06-24 01:23:37 +000016 check_archive_formats
Greg Warda82122b2000-02-17 23:56:15 +000017from distutils.text_file import TextFile
Greg Ward6a9a5452000-04-22 03:11:55 +000018from distutils.errors import DistutilsExecError, DistutilsOptionError
Greg Warda82122b2000-02-17 23:56:15 +000019
20
Greg Ward34593812000-06-24 01:23:37 +000021def show_formats ():
22 """Print all possible values for the 'formats' option (used by
23 the "--help-formats" command-line option).
24 """
25 from distutils.fancy_getopt import FancyGetopt
26 from distutils.archive_util import ARCHIVE_FORMATS
27 formats=[]
28 for format in ARCHIVE_FORMATS.keys():
29 formats.append(("formats=" + format, None,
30 ARCHIVE_FORMATS[format][2]))
31 formats.sort()
32 pretty_printer = FancyGetopt(formats)
33 pretty_printer.print_help(
34 "List of available source distribution formats:")
35
36
Greg Ward1993f9a2000-02-18 00:13:53 +000037class sdist (Command):
Greg Warda82122b2000-02-17 23:56:15 +000038
39 description = "create a source distribution (tarball, zip file, etc.)"
40
Greg Wardbbeceea2000-02-18 00:25:39 +000041 user_options = [
42 ('template=', 't',
43 "name of manifest template file [default: MANIFEST.in]"),
44 ('manifest=', 'm',
45 "name of manifest file [default: MANIFEST]"),
46 ('use-defaults', None,
47 "include the default file set in the manifest "
48 "[default; disable with --no-defaults]"),
Greg Ward499822d2000-06-29 02:06:29 +000049 ('no-defaults', None,
50 "don't include the default file set"),
51 ('prune', None,
52 "specifically exclude files/directories that should not be "
53 "distributed (build tree, RCS/CVS dirs, etc.) "
54 "[default; disable with --no-prune]"),
55 ('no-prune', None,
56 "don't automatically exclude anything"),
Greg Ward839d5322000-04-26 01:14:33 +000057 ('manifest-only', 'o',
Greg Wardc3c8c6e2000-06-08 00:46:45 +000058 "just regenerate the manifest and then stop "
59 "(implies --force-manifest)"),
Greg Ward839d5322000-04-26 01:14:33 +000060 ('force-manifest', 'f',
Greg Wardbbeceea2000-02-18 00:25:39 +000061 "forcibly regenerate the manifest and carry on as usual"),
Greg Wardbbeceea2000-02-18 00:25:39 +000062 ('formats=', None,
Greg Ward2ff78872000-06-24 00:23:20 +000063 "formats for source distribution (comma-separated list)"),
Greg Wardbbeceea2000-02-18 00:25:39 +000064 ('keep-tree', 'k',
65 "keep the distribution tree around after creating " +
66 "archive file(s)"),
Greg Wardc0614102000-07-05 03:06:46 +000067 ('dist-dir=', 'd',
68 "directory to put the source distribution archive(s) in "
69 "[default: dist]"),
Greg Wardbbeceea2000-02-18 00:25:39 +000070 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000071
72
Greg Ward9d17a7a2000-06-07 03:00:06 +000073 help_options = [
74 ('help-formats', None,
Greg Ward2ff78872000-06-24 00:23:20 +000075 "list available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000076 ]
77
Greg Ward499822d2000-06-29 02:06:29 +000078 negative_opt = {'no-defaults': 'use-defaults',
79 'no-prune': 'prune' }
Greg Warda82122b2000-02-17 23:56:15 +000080
81 default_format = { 'posix': 'gztar',
82 'nt': 'zip' }
83
Greg Warde01149c2000-02-18 00:35:22 +000084 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000085 # 'template' and 'manifest' are, respectively, the names of
86 # the manifest template and manifest file.
87 self.template = None
88 self.manifest = None
89
90 # 'use_defaults': if true, we will include the default file set
91 # in the manifest
92 self.use_defaults = 1
Greg Ward499822d2000-06-29 02:06:29 +000093 self.prune = 1
Greg Warda82122b2000-02-17 23:56:15 +000094
95 self.manifest_only = 0
96 self.force_manifest = 0
97
98 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000099 self.keep_tree = 0
Greg Wardc0614102000-07-05 03:06:46 +0000100 self.dist_dir = None
Greg Warda82122b2000-02-17 23:56:15 +0000101
Greg Wardd87eb732000-06-01 01:10:56 +0000102 self.archive_files = None
103
Greg Warda82122b2000-02-17 23:56:15 +0000104
Greg Warde01149c2000-02-18 00:35:22 +0000105 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +0000106 if self.manifest is None:
107 self.manifest = "MANIFEST"
108 if self.template is None:
109 self.template = "MANIFEST.in"
110
Greg Ward62d5a572000-06-04 15:12:51 +0000111 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +0000112 if self.formats is None:
113 try:
114 self.formats = [self.default_format[os.name]]
115 except KeyError:
116 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +0000117 "don't know how to create source distributions " + \
118 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +0000119
Greg Ward6a9a5452000-04-22 03:11:55 +0000120 bad_format = check_archive_formats (self.formats)
121 if bad_format:
122 raise DistutilsOptionError, \
123 "unknown archive format '%s'" % bad_format
124
Greg Wardc0614102000-07-05 03:06:46 +0000125 if self.dist_dir is None:
126 self.dist_dir = "dist"
127
Greg Warda82122b2000-02-17 23:56:15 +0000128
129 def run (self):
130
131 # 'files' is the list of files that will make up the manifest
132 self.files = []
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 put into 'self.files'.
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):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000153 """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 """
Greg Ward535f2d92000-04-21 04:37:12 +0000158 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000159
160 missing = []
161 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000162 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000163 missing.append (attr)
164
165 if missing:
166 self.warn ("missing required meta-data: " +
167 string.join (missing, ", "))
168
Greg Ward535f2d92000-04-21 04:37:12 +0000169 if metadata.author:
170 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000171 self.warn ("missing meta-data: if 'author' supplied, " +
172 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000173 elif metadata.maintainer:
174 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000175 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
Greg Warde0c8c2f2000-06-08 00:24:01 +0000187 distribution, and put it in 'self.files'. 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 """
Greg Wardb2db0eb2000-06-21 03:29:57 +0000192
193 # If we have a manifest template, see if it's newer than the
194 # manifest; if so, we'll regenerate the manifest.
Greg Warda82122b2000-02-17 23:56:15 +0000195 template_exists = os.path.isfile (self.template)
196 if template_exists:
197 template_newer = newer (self.template, self.manifest)
198
Greg Wardb2db0eb2000-06-21 03:29:57 +0000199 # 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 setup_newer = newer(sys.argv[0], self.manifest)
207
208 # cases:
209 # 1) no manifest, template exists: generate manifest
210 # (covered by 2a: no manifest == template newer)
211 # 2) manifest & template exist:
212 # 2a) template or setup script newer than manifest:
213 # regenerate manifest
214 # 2b) manifest newer than both:
215 # do nothing (unless --force or --manifest-only)
216 # 3) manifest exists, no template:
217 # do nothing (unless --force or --manifest-only)
218 # 4) no manifest, no template: generate w/ warning ("defaults only")
219
Greg Warda82122b2000-02-17 23:56:15 +0000220 # Regenerate the manifest if necessary (or if explicitly told to)
Greg Wardb2db0eb2000-06-21 03:29:57 +0000221 if ((template_exists and (template_newer or setup_newer)) or
222 self.force_manifest or self.manifest_only):
Greg Warda82122b2000-02-17 23:56:15 +0000223
224 if not template_exists:
225 self.warn (("manifest template '%s' does not exist " +
226 "(using default file list)") %
227 self.template)
228
229 # Add default file set to 'files'
230 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000231 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000232
233 # Read manifest template if it exists
234 if template_exists:
235 self.read_template ()
236
Greg Ward499822d2000-06-29 02:06:29 +0000237 # Prune away any directories that don't belong in the source
238 # distribution
239 if self.prune:
240 self.prune_file_list()
Greg Wardce15c6c2000-06-08 01:06:02 +0000241
Greg Warda82122b2000-02-17 23:56:15 +0000242 # File list now complete -- sort it so that higher-level files
243 # come first
244 sortable_files = map (os.path.split, self.files)
245 sortable_files.sort ()
246 self.files = []
247 for sort_tuple in sortable_files:
248 self.files.append (apply (os.path.join, sort_tuple))
249
250 # Remove duplicates from the file list
251 for i in range (len(self.files)-1, 0, -1):
252 if self.files[i] == self.files[i-1]:
253 del self.files[i]
254
255 # And write complete file list (including default file set) to
256 # the manifest.
257 self.write_manifest ()
258
259 # Don't regenerate the manifest, just read it in.
260 else:
261 self.read_manifest ()
262
263 # get_file_list ()
264
265
Greg Ward4a7319c2000-06-08 00:52:52 +0000266 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000267 """Add all the default files to self.files:
268 - README or README.txt
269 - setup.py
270 - test/test*.py
271 - all pure Python modules mentioned in setup script
272 - all C sources listed as part of extensions or C libraries
273 in the setup script (doesn't catch C headers!)
274 Warns if (README or README.txt) or setup.py are missing; everything
275 else is optional.
276 """
Greg Ward14c8d052000-06-08 01:22:48 +0000277
278 # XXX name of setup script and config file should be taken
279 # programmatically from the Distribution object (except
280 # it doesn't have that capability... yet!)
Greg Warda82122b2000-02-17 23:56:15 +0000281 standards = [('README', 'README.txt'), 'setup.py']
282 for fn in standards:
283 if type (fn) is TupleType:
284 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000285 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000286 for fn in alts:
287 if os.path.exists (fn):
288 got_it = 1
289 self.files.append (fn)
290 break
291
292 if not got_it:
293 self.warn ("standard file not found: should have one of " +
294 string.join (alts, ', '))
295 else:
296 if os.path.exists (fn):
297 self.files.append (fn)
298 else:
299 self.warn ("standard file '%s' not found" % fn)
300
Greg Ward14c8d052000-06-08 01:22:48 +0000301 optional = ['test/test*.py', 'setup.cfg']
Greg Warda82122b2000-02-17 23:56:15 +0000302 for pattern in optional:
303 files = filter (os.path.isfile, glob (pattern))
304 if files:
305 self.files.extend (files)
306
Greg Ward578c10d2000-03-31 02:50:04 +0000307 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000308 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000309 self.files.extend (build_py.get_source_files ())
310
Greg Ward578c10d2000-03-31 02:50:04 +0000311 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000312 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000313 self.files.extend (build_ext.get_source_files ())
314
Greg Ward60908f12000-04-09 03:51:40 +0000315 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000316 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000317 self.files.extend (build_clib.get_source_files ())
318
Greg Ward4a7319c2000-06-08 00:52:52 +0000319 # add_defaults ()
320
Greg Warda82122b2000-02-17 23:56:15 +0000321
Greg Warda82122b2000-02-17 23:56:15 +0000322 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000323 """Remove filenames from 'self.files' that are under 'dir' and
324 whose basenames match 'pattern'.
325 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000326 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
327 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000328 if pattern is None:
329 pattern_re = None
330 else:
331 pattern_re = translate_pattern (pattern)
332
333 for i in range (len (self.files)-1, -1, -1):
334 (cur_dir, cur_base) = os.path.split (self.files[i])
335 if (cur_dir == dir and
336 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000337 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000338 del self.files[i]
339
340
341 def read_template (self):
342 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000343 'self.template' (usually "MANIFEST.in"). Process all file
344 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000345 update 'self.files' accordingly (filenames may be added to
346 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000347 """
Greg Warda82122b2000-02-17 23:56:15 +0000348 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000349 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000350
351 template = TextFile (self.template,
352 strip_comments=1,
353 skip_blanks=1,
354 join_lines=1,
355 lstrip_ws=1,
356 rstrip_ws=1,
357 collapse_ws=1)
358
359 all_files = findall ()
360
361 while 1:
362
363 line = template.readline()
364 if line is None: # end of file
365 break
366
367 words = string.split (line)
368 action = words[0]
369
370 # First, check that the right number of words are present
371 # for the given action (which is the first word)
372 if action in ('include','exclude',
373 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000374 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000375 template.warn \
376 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000377 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000378 action)
379 continue
380
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000381 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000382
383 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000384 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000385 template.warn \
386 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000387 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000388 action)
389 continue
390
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000391 dir = convert_path(words[1])
392 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000393
394 elif action in ('graft','prune'):
395 if len (words) != 2:
396 template.warn \
397 ("invalid manifest template line: " +
398 "'%s' expects a single <dir_pattern>" %
399 action)
400 continue
401
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000402 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000403
404 else:
405 template.warn ("invalid manifest template line: " +
406 "unknown action '%s'" % action)
407 continue
408
409 # OK, now we know that the action is valid and we have the
410 # right number of words on the line for that action -- so we
411 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000412 # defined either (pattern), (dir and pattern), or
413 # (dir_pattern) -- so we don't have to spend any time
414 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000415
416 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000417 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000418 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000419 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000420 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000421 template.warn ("no files found matching '%s'" %
422 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000423 else:
424 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000425
426 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000427 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000428 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000429 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000430 if num == 0:
431 template.warn (
432 "no previously-included files found matching '%s'"%
433 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000434
435 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000436 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000437 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000438 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000439 if not files:
440 template.warn (("no files found matching '%s' " +
441 "anywhere in distribution") %
442 pattern)
443 else:
444 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000445
446 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000447 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000448 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000449 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000450 if num == 0:
451 template.warn \
452 (("no previously-included files matching '%s' " +
453 "found anywhere in distribution") %
454 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000455
456 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000457 self.debug_print("recursive-include %s %s" %
458 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000459 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000460 files = self.select_pattern (
461 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000462 if not files:
463 template.warn (("no files found matching '%s' " +
464 "under directory '%s'") %
465 (pattern, dir))
466 else:
467 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000468
469 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000470 self.debug_print("recursive-exclude %s %s" %
471 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000472 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000473 num = self.exclude_pattern(
474 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000475 if num == 0:
476 template.warn \
477 (("no previously-included files matching '%s' " +
478 "found under directory '%s'") %
479 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000480
481 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000482 self.debug_print("graft " + dir_pattern)
483 files = self.select_pattern(
484 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000485 if not files:
486 template.warn ("no directories found matching '%s'" %
487 dir_pattern)
488 else:
489 self.files.extend (files)
490
491 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000492 self.debug_print("prune " + dir_pattern)
493 num = self.exclude_pattern(
494 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000495 if num == 0:
496 template.warn \
497 (("no previously-included directories found " +
498 "matching '%s'") %
499 dir_pattern)
500 else:
501 raise RuntimeError, \
502 "this cannot happen: invalid action '%s'" % action
503
504 # while loop over lines of template file
505
506 # read_template ()
507
508
Greg Wardce15c6c2000-06-08 01:06:02 +0000509 def prune_file_list (self):
510 """Prune off branches that might slip into the file list as created
Greg Ward499822d2000-06-29 02:06:29 +0000511 by 'read_template()', but really don't belong there:
512 * the build tree (typically "build")
513 * the release tree itself (only an issue if we ran "sdist"
514 previously with --keep-tree, or it aborted)
515 * any RCS or CVS directories
Greg Wardce15c6c2000-06-08 01:06:02 +0000516 """
517 build = self.get_finalized_command('build')
518 base_dir = self.distribution.get_fullname()
519 self.exclude_pattern (self.files, None, prefix=build.build_base)
520 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Ward499822d2000-06-29 02:06:29 +0000521 self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1)
Greg Wardce15c6c2000-06-08 01:06:02 +0000522
523
Greg Ward499822d2000-06-29 02:06:29 +0000524 def select_pattern (self, files, pattern,
525 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000526 """Select strings (presumably filenames) from 'files' that match
527 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
528 quite the same as implemented by the 'fnmatch' module: '*' and '?'
529 match non-special characters, where "special" is platform-dependent:
530 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
531 Mac OS.
532
533 If 'anchor' is true (the default), then the pattern match is more
534 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
535 'anchor' is false, both of these will match.
536
537 If 'prefix' is supplied, then only filenames starting with 'prefix'
538 (itself a pattern) and ending with 'pattern', with anything in between
539 them, will match. 'anchor' is ignored in this case.
540
Greg Ward499822d2000-06-29 02:06:29 +0000541 If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and
542 'pattern' is assumed to be either a string containing a regex or a
543 regex object -- no translation is done, the regex is just compiled
544 and used as-is.
545
Greg Wardf8b9e202000-06-08 00:08:14 +0000546 Return the list of matching strings, possibly empty.
547 """
548 matches = []
Greg Ward499822d2000-06-29 02:06:29 +0000549 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000550 self.debug_print("select_pattern: applying regex r'%s'" %
551 pattern_re.pattern)
552 for name in files:
553 if pattern_re.search (name):
554 matches.append (name)
555 self.debug_print(" adding " + name)
556
557 return matches
558
559 # select_pattern ()
560
561
Greg Ward499822d2000-06-29 02:06:29 +0000562 def exclude_pattern (self, files, pattern,
563 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000564 """Remove strings (presumably filenames) from 'files' that match
Greg Ward499822d2000-06-29 02:06:29 +0000565 'pattern'. Other parameters are the same as for
566 'select_pattern()', above. The list 'files' is modified in place.
Greg Wardf8b9e202000-06-08 00:08:14 +0000567 """
Greg Ward499822d2000-06-29 02:06:29 +0000568
569 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000570 self.debug_print("exclude_pattern: applying regex r'%s'" %
571 pattern_re.pattern)
572 for i in range (len(files)-1, -1, -1):
573 if pattern_re.search (files[i]):
574 self.debug_print(" removing " + files[i])
575 del files[i]
576
577 # exclude_pattern ()
578
579
Greg Warda82122b2000-02-17 23:56:15 +0000580 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000581 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000582 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000583 by 'self.manifest'.
584 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000585 self.execute(write_file,
586 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000587 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000588
589 # write_manifest ()
590
591
592 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000593 """Read the manifest file (named by 'self.manifest') and use it to
594 fill in 'self.files', the list of files to include in the source
595 distribution.
596 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000597 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000598 manifest = open (self.manifest)
599 while 1:
600 line = manifest.readline ()
601 if line == '': # end of file
602 break
603 if line[-1] == '\n':
604 line = line[0:-1]
605 self.files.append (line)
606
607 # read_manifest ()
608
609
Greg Warda82122b2000-02-17 23:56:15 +0000610 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000611 """Create the directory tree that will become the source
612 distribution archive. All directories implied by the filenames in
613 'files' are created under 'base_dir', and then we hard link or copy
614 (if hard linking is unavailable) those files into place.
615 Essentially, this duplicates the developer's source tree, but in a
616 directory named after the distribution, containing only the files
617 to be distributed.
618 """
Greg Ward578c10d2000-03-31 02:50:04 +0000619 # Create all the directories under 'base_dir' necessary to
620 # put 'files' there.
621 create_tree (base_dir, files,
622 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000623
624 # And walk over the list of files, either making a hard link (if
625 # os.link exists) to each one that doesn't already exist in its
626 # corresponding location under 'base_dir', or copying each file
627 # that's out-of-date in 'base_dir'. (Usually, all files will be
628 # out-of-date, because by default we blow away 'base_dir' when
629 # we're done making the distribution archives.)
630
Greg Ward578c10d2000-03-31 02:50:04 +0000631 if hasattr (os, 'link'): # can make hard links on this system
632 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000633 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000634 else: # nope, have to copy
635 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000636 msg = "copying files to %s..." % base_dir
637
638 self.announce (msg)
639 for file in files:
640 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000641 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000642
643 # make_release_tree ()
644
645
Greg Warda82122b2000-02-17 23:56:15 +0000646 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000647 """Create the source distribution(s). First, we create the release
648 tree with 'make_release_tree()'; then, we create all required
649 archive files (according to 'self.formats') from the release tree.
650 Finally, we clean up by blowing away the release tree (unless
651 'self.keep_tree' is true). The list of archive files created is
652 stored so it can be retrieved later by 'get_archive_files()'.
653 """
Greg Ward578c10d2000-03-31 02:50:04 +0000654 # Don't warn about missing meta-data here -- should be (and is!)
655 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000656 base_dir = self.distribution.get_fullname()
Greg Wardc0614102000-07-05 03:06:46 +0000657 base_name = os.path.join(self.dist_dir, base_dir)
Greg Warda82122b2000-02-17 23:56:15 +0000658
Greg Warda82122b2000-02-17 23:56:15 +0000659 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000660 archive_files = [] # remember names of files we create
Greg Wardc0614102000-07-05 03:06:46 +0000661 if self.dist_dir:
662 self.mkpath(self.dist_dir)
Greg Warda82122b2000-02-17 23:56:15 +0000663 for fmt in self.formats:
Greg Wardc0614102000-07-05 03:06:46 +0000664 file = self.make_archive (base_name, fmt, base_dir=base_dir)
Greg Wardd87eb732000-06-01 01:10:56 +0000665 archive_files.append(file)
666
667 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000668
669 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000670 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000671
Greg Wardd87eb732000-06-01 01:10:56 +0000672 def get_archive_files (self):
673 """Return the list of archive files created when the command
674 was run, or None if the command hasn't run yet.
675 """
676 return self.archive_files
677
Greg Wardfcd974e2000-05-25 01:10:04 +0000678# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000679
680
681# ----------------------------------------------------------------------
682# Utility functions
683
684def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000685 """Find all files under 'dir' and return the list of full filenames
686 (relative to 'dir').
687 """
Greg Ward499822d2000-06-29 02:06:29 +0000688 from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK
689
Greg Warda82122b2000-02-17 23:56:15 +0000690 list = []
691 stack = [dir]
692 pop = stack.pop
693 push = stack.append
694
695 while stack:
696 dir = pop()
697 names = os.listdir (dir)
698
699 for name in names:
Greg Wardaa5372c2000-07-27 01:58:45 +0000700 if dir != os.curdir: # avoid leading "./"
Greg Warda82122b2000-02-17 23:56:15 +0000701 fullname = os.path.join (dir, name)
702 else:
703 fullname = name
Greg Ward499822d2000-06-29 02:06:29 +0000704
705 # Avoid excess stat calls -- just one will do, thank you!
706 stat = os.stat(fullname)
707 mode = stat[ST_MODE]
708 if S_ISREG(mode):
709 list.append (fullname)
710 elif S_ISDIR(mode) and not S_ISLNK(mode):
Greg Warda82122b2000-02-17 23:56:15 +0000711 push (fullname)
712
713 return list
714
715
Greg Warda82122b2000-02-17 23:56:15 +0000716def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000717 """Translate a shell-like glob pattern to a regular expression; return
718 a string containing the regex. Differs from 'fnmatch.translate()' in
719 that '*' does not match "special characters" (which are
720 platform-specific).
721 """
Greg Warda82122b2000-02-17 23:56:15 +0000722 pattern_re = fnmatch.translate (pattern)
723
724 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
725 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
726 # and by extension they shouldn't match such "special characters" under
727 # any OS. So change all non-escaped dots in the RE to match any
728 # character except the special characters.
729 # XXX currently the "special characters" are just slash -- i.e. this is
730 # Unix-only.
731 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
732 return pattern_re
733
734# glob_to_re ()
735
736
Greg Ward499822d2000-06-29 02:06:29 +0000737def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0):
Greg Warda82122b2000-02-17 23:56:15 +0000738 """Translate a shell-like wildcard pattern to a compiled regular
Greg Ward499822d2000-06-29 02:06:29 +0000739 expression. Return the compiled regex. If 'is_regex' true,
740 then 'pattern' is directly compiled to a regex (if it's a string)
741 or just returned as-is (assumes it's a regex object).
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000742 """
Greg Ward499822d2000-06-29 02:06:29 +0000743 if is_regex:
744 if type(pattern) is StringType:
745 return re.compile(pattern)
746 else:
747 return pattern
748
Greg Warda82122b2000-02-17 23:56:15 +0000749 if pattern:
750 pattern_re = glob_to_re (pattern)
751 else:
752 pattern_re = ''
753
754 if prefix is not None:
755 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
756 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
757 else: # no prefix -- respect anchor flag
758 if anchor:
759 pattern_re = "^" + pattern_re
760
761 return re.compile (pattern_re)
762
763# translate_pattern ()