blob: 2d31640f44345f6a922f7dd20dfe6ee1f3304c69 [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 search_dir (self, dir, pattern=None):
323 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000324 containing a Unix-style glob pattern). If 'pattern' is None, find
325 all files under 'dir'. Return the list of found filenames.
326 """
Greg Warda82122b2000-02-17 23:56:15 +0000327 allfiles = findall (dir)
328 if pattern is None:
329 return allfiles
330
331 pattern_re = translate_pattern (pattern)
332 files = []
333 for file in allfiles:
334 if pattern_re.match (os.path.basename (file)):
335 files.append (file)
336
337 return files
338
339 # search_dir ()
340
341
Greg Warda82122b2000-02-17 23:56:15 +0000342 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000343 """Remove filenames from 'self.files' that are under 'dir' and
344 whose basenames match 'pattern'.
345 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000346 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
347 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000348 if pattern is None:
349 pattern_re = None
350 else:
351 pattern_re = translate_pattern (pattern)
352
353 for i in range (len (self.files)-1, -1, -1):
354 (cur_dir, cur_base) = os.path.split (self.files[i])
355 if (cur_dir == dir and
356 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000357 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000358 del self.files[i]
359
360
361 def read_template (self):
362 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000363 'self.template' (usually "MANIFEST.in"). Process all file
364 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000365 update 'self.files' accordingly (filenames may be added to
366 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000367 """
Greg Warda82122b2000-02-17 23:56:15 +0000368 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000369 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000370
371 template = TextFile (self.template,
372 strip_comments=1,
373 skip_blanks=1,
374 join_lines=1,
375 lstrip_ws=1,
376 rstrip_ws=1,
377 collapse_ws=1)
378
379 all_files = findall ()
380
381 while 1:
382
383 line = template.readline()
384 if line is None: # end of file
385 break
386
387 words = string.split (line)
388 action = words[0]
389
390 # First, check that the right number of words are present
391 # for the given action (which is the first word)
392 if action in ('include','exclude',
393 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000394 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000395 template.warn \
396 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000397 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000398 action)
399 continue
400
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000401 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000402
403 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000404 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000405 template.warn \
406 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000407 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000408 action)
409 continue
410
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000411 dir = convert_path(words[1])
412 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000413
414 elif action in ('graft','prune'):
415 if len (words) != 2:
416 template.warn \
417 ("invalid manifest template line: " +
418 "'%s' expects a single <dir_pattern>" %
419 action)
420 continue
421
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000422 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000423
424 else:
425 template.warn ("invalid manifest template line: " +
426 "unknown action '%s'" % action)
427 continue
428
429 # OK, now we know that the action is valid and we have the
430 # right number of words on the line for that action -- so we
431 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000432 # defined either (pattern), (dir and pattern), or
433 # (dir_pattern) -- so we don't have to spend any time
434 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000435
436 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000437 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000438 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000439 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000440 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000441 template.warn ("no files found matching '%s'" %
442 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000443 else:
444 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000445
446 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000447 self.debug_print("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=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000450 if num == 0:
451 template.warn (
452 "no previously-included files found matching '%s'"%
453 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000454
455 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000456 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000457 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000458 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000459 if not files:
460 template.warn (("no files found matching '%s' " +
461 "anywhere in distribution") %
462 pattern)
463 else:
464 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000465
466 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000467 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000468 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000469 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000470 if num == 0:
471 template.warn \
472 (("no previously-included files matching '%s' " +
473 "found anywhere in distribution") %
474 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000475
476 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000477 self.debug_print("recursive-include %s %s" %
478 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000479 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000480 files = self.select_pattern (
481 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000482 if not files:
483 template.warn (("no files found matching '%s' " +
484 "under directory '%s'") %
485 (pattern, dir))
486 else:
487 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000488
489 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000490 self.debug_print("recursive-exclude %s %s" %
491 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000492 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000493 num = self.exclude_pattern(
494 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000495 if num == 0:
496 template.warn \
497 (("no previously-included files matching '%s' " +
498 "found under directory '%s'") %
499 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000500
501 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000502 self.debug_print("graft " + dir_pattern)
503 files = self.select_pattern(
504 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000505 if not files:
506 template.warn ("no directories found matching '%s'" %
507 dir_pattern)
508 else:
509 self.files.extend (files)
510
511 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000512 self.debug_print("prune " + dir_pattern)
513 num = self.exclude_pattern(
514 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000515 if num == 0:
516 template.warn \
517 (("no previously-included directories found " +
518 "matching '%s'") %
519 dir_pattern)
520 else:
521 raise RuntimeError, \
522 "this cannot happen: invalid action '%s'" % action
523
524 # while loop over lines of template file
525
526 # read_template ()
527
528
Greg Wardce15c6c2000-06-08 01:06:02 +0000529 def prune_file_list (self):
530 """Prune off branches that might slip into the file list as created
Greg Ward499822d2000-06-29 02:06:29 +0000531 by 'read_template()', but really don't belong there:
532 * the build tree (typically "build")
533 * the release tree itself (only an issue if we ran "sdist"
534 previously with --keep-tree, or it aborted)
535 * any RCS or CVS directories
Greg Wardce15c6c2000-06-08 01:06:02 +0000536 """
537 build = self.get_finalized_command('build')
538 base_dir = self.distribution.get_fullname()
539 self.exclude_pattern (self.files, None, prefix=build.build_base)
540 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Ward499822d2000-06-29 02:06:29 +0000541 self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1)
Greg Wardce15c6c2000-06-08 01:06:02 +0000542
543
Greg Ward499822d2000-06-29 02:06:29 +0000544 def select_pattern (self, files, pattern,
545 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000546 """Select strings (presumably filenames) from 'files' that match
547 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
548 quite the same as implemented by the 'fnmatch' module: '*' and '?'
549 match non-special characters, where "special" is platform-dependent:
550 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
551 Mac OS.
552
553 If 'anchor' is true (the default), then the pattern match is more
554 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
555 'anchor' is false, both of these will match.
556
557 If 'prefix' is supplied, then only filenames starting with 'prefix'
558 (itself a pattern) and ending with 'pattern', with anything in between
559 them, will match. 'anchor' is ignored in this case.
560
Greg Ward499822d2000-06-29 02:06:29 +0000561 If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and
562 'pattern' is assumed to be either a string containing a regex or a
563 regex object -- no translation is done, the regex is just compiled
564 and used as-is.
565
Greg Wardf8b9e202000-06-08 00:08:14 +0000566 Return the list of matching strings, possibly empty.
567 """
568 matches = []
Greg Ward499822d2000-06-29 02:06:29 +0000569 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000570 self.debug_print("select_pattern: applying regex r'%s'" %
571 pattern_re.pattern)
572 for name in files:
573 if pattern_re.search (name):
574 matches.append (name)
575 self.debug_print(" adding " + name)
576
577 return matches
578
579 # select_pattern ()
580
581
Greg Ward499822d2000-06-29 02:06:29 +0000582 def exclude_pattern (self, files, pattern,
583 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000584 """Remove strings (presumably filenames) from 'files' that match
Greg Ward499822d2000-06-29 02:06:29 +0000585 'pattern'. Other parameters are the same as for
586 'select_pattern()', above. The list 'files' is modified in place.
Greg Wardf8b9e202000-06-08 00:08:14 +0000587 """
Greg Ward499822d2000-06-29 02:06:29 +0000588
589 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000590 self.debug_print("exclude_pattern: applying regex r'%s'" %
591 pattern_re.pattern)
592 for i in range (len(files)-1, -1, -1):
593 if pattern_re.search (files[i]):
594 self.debug_print(" removing " + files[i])
595 del files[i]
596
597 # exclude_pattern ()
598
599
Greg Warda82122b2000-02-17 23:56:15 +0000600 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000601 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000602 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000603 by 'self.manifest'.
604 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000605 self.execute(write_file,
606 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000607 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000608
609 # write_manifest ()
610
611
612 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000613 """Read the manifest file (named by 'self.manifest') and use it to
614 fill in 'self.files', the list of files to include in the source
615 distribution.
616 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000617 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000618 manifest = open (self.manifest)
619 while 1:
620 line = manifest.readline ()
621 if line == '': # end of file
622 break
623 if line[-1] == '\n':
624 line = line[0:-1]
625 self.files.append (line)
626
627 # read_manifest ()
628
629
Greg Warda82122b2000-02-17 23:56:15 +0000630 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000631 """Create the directory tree that will become the source
632 distribution archive. All directories implied by the filenames in
633 'files' are created under 'base_dir', and then we hard link or copy
634 (if hard linking is unavailable) those files into place.
635 Essentially, this duplicates the developer's source tree, but in a
636 directory named after the distribution, containing only the files
637 to be distributed.
638 """
Greg Ward578c10d2000-03-31 02:50:04 +0000639 # Create all the directories under 'base_dir' necessary to
640 # put 'files' there.
641 create_tree (base_dir, files,
642 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000643
644 # And walk over the list of files, either making a hard link (if
645 # os.link exists) to each one that doesn't already exist in its
646 # corresponding location under 'base_dir', or copying each file
647 # that's out-of-date in 'base_dir'. (Usually, all files will be
648 # out-of-date, because by default we blow away 'base_dir' when
649 # we're done making the distribution archives.)
650
Greg Ward578c10d2000-03-31 02:50:04 +0000651 if hasattr (os, 'link'): # can make hard links on this system
652 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000653 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000654 else: # nope, have to copy
655 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000656 msg = "copying files to %s..." % base_dir
657
658 self.announce (msg)
659 for file in files:
660 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000661 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000662
663 # make_release_tree ()
664
665
Greg Warda82122b2000-02-17 23:56:15 +0000666 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000667 """Create the source distribution(s). First, we create the release
668 tree with 'make_release_tree()'; then, we create all required
669 archive files (according to 'self.formats') from the release tree.
670 Finally, we clean up by blowing away the release tree (unless
671 'self.keep_tree' is true). The list of archive files created is
672 stored so it can be retrieved later by 'get_archive_files()'.
673 """
Greg Ward578c10d2000-03-31 02:50:04 +0000674 # Don't warn about missing meta-data here -- should be (and is!)
675 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000676 base_dir = self.distribution.get_fullname()
Greg Wardc0614102000-07-05 03:06:46 +0000677 base_name = os.path.join(self.dist_dir, base_dir)
Greg Warda82122b2000-02-17 23:56:15 +0000678
Greg Warda82122b2000-02-17 23:56:15 +0000679 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000680 archive_files = [] # remember names of files we create
Greg Wardc0614102000-07-05 03:06:46 +0000681 if self.dist_dir:
682 self.mkpath(self.dist_dir)
Greg Warda82122b2000-02-17 23:56:15 +0000683 for fmt in self.formats:
Greg Wardc0614102000-07-05 03:06:46 +0000684 file = self.make_archive (base_name, fmt, base_dir=base_dir)
Greg Wardd87eb732000-06-01 01:10:56 +0000685 archive_files.append(file)
686
687 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000688
689 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000690 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000691
Greg Wardd87eb732000-06-01 01:10:56 +0000692 def get_archive_files (self):
693 """Return the list of archive files created when the command
694 was run, or None if the command hasn't run yet.
695 """
696 return self.archive_files
697
Greg Wardfcd974e2000-05-25 01:10:04 +0000698# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000699
700
701# ----------------------------------------------------------------------
702# Utility functions
703
704def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000705 """Find all files under 'dir' and return the list of full filenames
706 (relative to 'dir').
707 """
Greg Ward499822d2000-06-29 02:06:29 +0000708 from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK
709
Greg Warda82122b2000-02-17 23:56:15 +0000710 list = []
711 stack = [dir]
712 pop = stack.pop
713 push = stack.append
714
715 while stack:
716 dir = pop()
717 names = os.listdir (dir)
718
719 for name in names:
720 if dir != os.curdir: # avoid the dreaded "./" syndrome
721 fullname = os.path.join (dir, name)
722 else:
723 fullname = name
Greg Ward499822d2000-06-29 02:06:29 +0000724
725 # Avoid excess stat calls -- just one will do, thank you!
726 stat = os.stat(fullname)
727 mode = stat[ST_MODE]
728 if S_ISREG(mode):
729 list.append (fullname)
730 elif S_ISDIR(mode) and not S_ISLNK(mode):
Greg Warda82122b2000-02-17 23:56:15 +0000731 push (fullname)
732
733 return list
734
735
Greg Warda82122b2000-02-17 23:56:15 +0000736def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000737 """Translate a shell-like glob pattern to a regular expression; return
738 a string containing the regex. Differs from 'fnmatch.translate()' in
739 that '*' does not match "special characters" (which are
740 platform-specific).
741 """
Greg Warda82122b2000-02-17 23:56:15 +0000742 pattern_re = fnmatch.translate (pattern)
743
744 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
745 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
746 # and by extension they shouldn't match such "special characters" under
747 # any OS. So change all non-escaped dots in the RE to match any
748 # character except the special characters.
749 # XXX currently the "special characters" are just slash -- i.e. this is
750 # Unix-only.
751 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
752 return pattern_re
753
754# glob_to_re ()
755
756
Greg Ward499822d2000-06-29 02:06:29 +0000757def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0):
Greg Warda82122b2000-02-17 23:56:15 +0000758 """Translate a shell-like wildcard pattern to a compiled regular
Greg Ward499822d2000-06-29 02:06:29 +0000759 expression. Return the compiled regex. If 'is_regex' true,
760 then 'pattern' is directly compiled to a regex (if it's a string)
761 or just returned as-is (assumes it's a regex object).
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000762 """
Greg Ward499822d2000-06-29 02:06:29 +0000763 if is_regex:
764 if type(pattern) is StringType:
765 return re.compile(pattern)
766 else:
767 return pattern
768
Greg Warda82122b2000-02-17 23:56:15 +0000769 if pattern:
770 pattern_re = glob_to_re (pattern)
771 else:
772 pattern_re = ''
773
774 if prefix is not None:
775 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
776 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
777 else: # no prefix -- respect anchor flag
778 if anchor:
779 pattern_re = "^" + pattern_re
780
781 return re.compile (pattern_re)
782
783# translate_pattern ()