blob: 6f867e89d8a90823fe82e5a6fad6462f808719d2 [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)"),
67 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000068
69
Greg Ward9d17a7a2000-06-07 03:00:06 +000070 help_options = [
71 ('help-formats', None,
Greg Ward2ff78872000-06-24 00:23:20 +000072 "list available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000073 ]
74
Greg Ward499822d2000-06-29 02:06:29 +000075 negative_opt = {'no-defaults': 'use-defaults',
76 'no-prune': 'prune' }
Greg Warda82122b2000-02-17 23:56:15 +000077
78 default_format = { 'posix': 'gztar',
79 'nt': 'zip' }
80
Greg Warde01149c2000-02-18 00:35:22 +000081 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000082 # 'template' and 'manifest' are, respectively, the names of
83 # the manifest template and manifest file.
84 self.template = None
85 self.manifest = None
86
87 # 'use_defaults': if true, we will include the default file set
88 # in the manifest
89 self.use_defaults = 1
Greg Ward499822d2000-06-29 02:06:29 +000090 self.prune = 1
Greg Warda82122b2000-02-17 23:56:15 +000091
92 self.manifest_only = 0
93 self.force_manifest = 0
94
95 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000096 self.keep_tree = 0
97
Greg Wardd87eb732000-06-01 01:10:56 +000098 self.archive_files = None
99
Greg Warda82122b2000-02-17 23:56:15 +0000100
Greg Warde01149c2000-02-18 00:35:22 +0000101 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +0000102 if self.manifest is None:
103 self.manifest = "MANIFEST"
104 if self.template is None:
105 self.template = "MANIFEST.in"
106
Greg Ward62d5a572000-06-04 15:12:51 +0000107 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +0000108 if self.formats is None:
109 try:
110 self.formats = [self.default_format[os.name]]
111 except KeyError:
112 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +0000113 "don't know how to create source distributions " + \
114 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +0000115
Greg Ward6a9a5452000-04-22 03:11:55 +0000116 bad_format = check_archive_formats (self.formats)
117 if bad_format:
118 raise DistutilsOptionError, \
119 "unknown archive format '%s'" % bad_format
120
Greg Warda82122b2000-02-17 23:56:15 +0000121
122 def run (self):
123
124 # 'files' is the list of files that will make up the manifest
125 self.files = []
126
127 # Ensure that all required meta-data is given; warn if not (but
128 # don't die, it's not *that* serious!)
129 self.check_metadata ()
130
131 # Do whatever it takes to get the list of files to process
132 # (process the manifest template, read an existing manifest,
133 # whatever). File list is put into 'self.files'.
134 self.get_file_list ()
135
136 # If user just wanted us to regenerate the manifest, stop now.
137 if self.manifest_only:
138 return
139
140 # Otherwise, go ahead and create the source distribution tarball,
141 # or zipfile, or whatever.
142 self.make_distribution ()
143
144
145 def check_metadata (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000146 """Ensure that all required elements of meta-data (name, version,
147 URL, (author and author_email) or (maintainer and
148 maintainer_email)) are supplied by the Distribution object; warn if
149 any are missing.
150 """
Greg Ward535f2d92000-04-21 04:37:12 +0000151 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000152
153 missing = []
154 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000155 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000156 missing.append (attr)
157
158 if missing:
159 self.warn ("missing required meta-data: " +
160 string.join (missing, ", "))
161
Greg Ward535f2d92000-04-21 04:37:12 +0000162 if metadata.author:
163 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000164 self.warn ("missing meta-data: if 'author' supplied, " +
165 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000166 elif metadata.maintainer:
167 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000168 self.warn ("missing meta-data: if 'maintainer' supplied, " +
169 "'maintainer_email' must be supplied too")
170 else:
171 self.warn ("missing meta-data: either (author and author_email) " +
172 "or (maintainer and maintainer_email) " +
173 "must be supplied")
174
175 # check_metadata ()
176
177
178 def get_file_list (self):
179 """Figure out the list of files to include in the source
Greg Warde0c8c2f2000-06-08 00:24:01 +0000180 distribution, and put it in 'self.files'. 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 and the state of the filesystem.
184 """
Greg Wardb2db0eb2000-06-21 03:29:57 +0000185
186 # If we have a manifest template, see if it's newer than the
187 # manifest; if so, we'll regenerate the manifest.
Greg Warda82122b2000-02-17 23:56:15 +0000188 template_exists = os.path.isfile (self.template)
189 if template_exists:
190 template_newer = newer (self.template, self.manifest)
191
Greg Wardb2db0eb2000-06-21 03:29:57 +0000192 # The contents of the manifest file almost certainly depend on the
193 # setup script as well as the manifest template -- so if the setup
194 # script is newer than the manifest, we'll regenerate the manifest
195 # from the template. (Well, not quite: if we already have a
196 # manifest, but there's no template -- which will happen if the
197 # developer elects to generate a manifest some other way -- then we
198 # can't regenerate the manifest, so we don't.)
199 setup_newer = newer(sys.argv[0], self.manifest)
200
201 # cases:
202 # 1) no manifest, template exists: generate manifest
203 # (covered by 2a: no manifest == template newer)
204 # 2) manifest & template exist:
205 # 2a) template or setup script newer than manifest:
206 # regenerate manifest
207 # 2b) manifest newer than both:
208 # do nothing (unless --force or --manifest-only)
209 # 3) manifest exists, no template:
210 # do nothing (unless --force or --manifest-only)
211 # 4) no manifest, no template: generate w/ warning ("defaults only")
212
Greg Warda82122b2000-02-17 23:56:15 +0000213 # Regenerate the manifest if necessary (or if explicitly told to)
Greg Wardb2db0eb2000-06-21 03:29:57 +0000214 if ((template_exists and (template_newer or setup_newer)) or
215 self.force_manifest or self.manifest_only):
Greg Warda82122b2000-02-17 23:56:15 +0000216
217 if not template_exists:
218 self.warn (("manifest template '%s' does not exist " +
219 "(using default file list)") %
220 self.template)
221
222 # Add default file set to 'files'
223 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000224 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000225
226 # Read manifest template if it exists
227 if template_exists:
228 self.read_template ()
229
Greg Ward499822d2000-06-29 02:06:29 +0000230 # Prune away any directories that don't belong in the source
231 # distribution
232 if self.prune:
233 self.prune_file_list()
Greg Wardce15c6c2000-06-08 01:06:02 +0000234
Greg Warda82122b2000-02-17 23:56:15 +0000235 # File list now complete -- sort it so that higher-level files
236 # come first
237 sortable_files = map (os.path.split, self.files)
238 sortable_files.sort ()
239 self.files = []
240 for sort_tuple in sortable_files:
241 self.files.append (apply (os.path.join, sort_tuple))
242
243 # Remove duplicates from the file list
244 for i in range (len(self.files)-1, 0, -1):
245 if self.files[i] == self.files[i-1]:
246 del self.files[i]
247
248 # And write complete file list (including default file set) to
249 # the manifest.
250 self.write_manifest ()
251
252 # Don't regenerate the manifest, just read it in.
253 else:
254 self.read_manifest ()
255
256 # get_file_list ()
257
258
Greg Ward4a7319c2000-06-08 00:52:52 +0000259 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000260 """Add all the default files to self.files:
261 - README or README.txt
262 - setup.py
263 - test/test*.py
264 - all pure Python modules mentioned in setup script
265 - all C sources listed as part of extensions or C libraries
266 in the setup script (doesn't catch C headers!)
267 Warns if (README or README.txt) or setup.py are missing; everything
268 else is optional.
269 """
Greg Ward14c8d052000-06-08 01:22:48 +0000270
271 # XXX name of setup script and config file should be taken
272 # programmatically from the Distribution object (except
273 # it doesn't have that capability... yet!)
Greg Warda82122b2000-02-17 23:56:15 +0000274 standards = [('README', 'README.txt'), 'setup.py']
275 for fn in standards:
276 if type (fn) is TupleType:
277 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000278 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000279 for fn in alts:
280 if os.path.exists (fn):
281 got_it = 1
282 self.files.append (fn)
283 break
284
285 if not got_it:
286 self.warn ("standard file not found: should have one of " +
287 string.join (alts, ', '))
288 else:
289 if os.path.exists (fn):
290 self.files.append (fn)
291 else:
292 self.warn ("standard file '%s' not found" % fn)
293
Greg Ward14c8d052000-06-08 01:22:48 +0000294 optional = ['test/test*.py', 'setup.cfg']
Greg Warda82122b2000-02-17 23:56:15 +0000295 for pattern in optional:
296 files = filter (os.path.isfile, glob (pattern))
297 if files:
298 self.files.extend (files)
299
Greg Ward578c10d2000-03-31 02:50:04 +0000300 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000301 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000302 self.files.extend (build_py.get_source_files ())
303
Greg Ward578c10d2000-03-31 02:50:04 +0000304 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000305 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000306 self.files.extend (build_ext.get_source_files ())
307
Greg Ward60908f12000-04-09 03:51:40 +0000308 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000309 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000310 self.files.extend (build_clib.get_source_files ())
311
Greg Ward4a7319c2000-06-08 00:52:52 +0000312 # add_defaults ()
313
Greg Warda82122b2000-02-17 23:56:15 +0000314
Greg Warda82122b2000-02-17 23:56:15 +0000315 def search_dir (self, dir, pattern=None):
316 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000317 containing a Unix-style glob pattern). If 'pattern' is None, find
318 all files under 'dir'. Return the list of found filenames.
319 """
Greg Warda82122b2000-02-17 23:56:15 +0000320 allfiles = findall (dir)
321 if pattern is None:
322 return allfiles
323
324 pattern_re = translate_pattern (pattern)
325 files = []
326 for file in allfiles:
327 if pattern_re.match (os.path.basename (file)):
328 files.append (file)
329
330 return files
331
332 # search_dir ()
333
334
Greg Warda82122b2000-02-17 23:56:15 +0000335 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000336 """Remove filenames from 'self.files' that are under 'dir' and
337 whose basenames match 'pattern'.
338 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000339 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
340 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000341 if pattern is None:
342 pattern_re = None
343 else:
344 pattern_re = translate_pattern (pattern)
345
346 for i in range (len (self.files)-1, -1, -1):
347 (cur_dir, cur_base) = os.path.split (self.files[i])
348 if (cur_dir == dir and
349 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000350 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000351 del self.files[i]
352
353
354 def read_template (self):
355 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000356 'self.template' (usually "MANIFEST.in"). Process all file
357 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000358 update 'self.files' accordingly (filenames may be added to
359 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000360 """
Greg Warda82122b2000-02-17 23:56:15 +0000361 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000362 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000363
364 template = TextFile (self.template,
365 strip_comments=1,
366 skip_blanks=1,
367 join_lines=1,
368 lstrip_ws=1,
369 rstrip_ws=1,
370 collapse_ws=1)
371
372 all_files = findall ()
373
374 while 1:
375
376 line = template.readline()
377 if line is None: # end of file
378 break
379
380 words = string.split (line)
381 action = words[0]
382
383 # First, check that the right number of words are present
384 # for the given action (which is the first word)
385 if action in ('include','exclude',
386 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000387 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000388 template.warn \
389 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000390 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000391 action)
392 continue
393
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000394 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000395
396 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000397 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000398 template.warn \
399 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000400 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000401 action)
402 continue
403
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000404 dir = convert_path(words[1])
405 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000406
407 elif action in ('graft','prune'):
408 if len (words) != 2:
409 template.warn \
410 ("invalid manifest template line: " +
411 "'%s' expects a single <dir_pattern>" %
412 action)
413 continue
414
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000415 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000416
417 else:
418 template.warn ("invalid manifest template line: " +
419 "unknown action '%s'" % action)
420 continue
421
422 # OK, now we know that the action is valid and we have the
423 # right number of words on the line for that action -- so we
424 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000425 # defined either (pattern), (dir and pattern), or
426 # (dir_pattern) -- so we don't have to spend any time
427 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000428
429 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000430 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000431 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000432 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000433 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000434 template.warn ("no files found matching '%s'" %
435 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000436 else:
437 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000438
439 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000440 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000441 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000442 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000443 if num == 0:
444 template.warn (
445 "no previously-included files found matching '%s'"%
446 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000447
448 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000449 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000450 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000451 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000452 if not files:
453 template.warn (("no files found matching '%s' " +
454 "anywhere in distribution") %
455 pattern)
456 else:
457 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000458
459 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000460 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000461 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000462 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000463 if num == 0:
464 template.warn \
465 (("no previously-included files matching '%s' " +
466 "found anywhere in distribution") %
467 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000468
469 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000470 self.debug_print("recursive-include %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 files = self.select_pattern (
474 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000475 if not files:
476 template.warn (("no files found matching '%s' " +
477 "under directory '%s'") %
478 (pattern, dir))
479 else:
480 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000481
482 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000483 self.debug_print("recursive-exclude %s %s" %
484 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000485 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000486 num = self.exclude_pattern(
487 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000488 if num == 0:
489 template.warn \
490 (("no previously-included files matching '%s' " +
491 "found under directory '%s'") %
492 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000493
494 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000495 self.debug_print("graft " + dir_pattern)
496 files = self.select_pattern(
497 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000498 if not files:
499 template.warn ("no directories found matching '%s'" %
500 dir_pattern)
501 else:
502 self.files.extend (files)
503
504 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000505 self.debug_print("prune " + dir_pattern)
506 num = self.exclude_pattern(
507 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000508 if num == 0:
509 template.warn \
510 (("no previously-included directories found " +
511 "matching '%s'") %
512 dir_pattern)
513 else:
514 raise RuntimeError, \
515 "this cannot happen: invalid action '%s'" % action
516
517 # while loop over lines of template file
518
519 # read_template ()
520
521
Greg Wardce15c6c2000-06-08 01:06:02 +0000522 def prune_file_list (self):
523 """Prune off branches that might slip into the file list as created
Greg Ward499822d2000-06-29 02:06:29 +0000524 by 'read_template()', but really don't belong there:
525 * the build tree (typically "build")
526 * the release tree itself (only an issue if we ran "sdist"
527 previously with --keep-tree, or it aborted)
528 * any RCS or CVS directories
Greg Wardce15c6c2000-06-08 01:06:02 +0000529 """
530 build = self.get_finalized_command('build')
531 base_dir = self.distribution.get_fullname()
532 self.exclude_pattern (self.files, None, prefix=build.build_base)
533 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Ward499822d2000-06-29 02:06:29 +0000534 self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1)
Greg Wardce15c6c2000-06-08 01:06:02 +0000535
536
Greg Ward499822d2000-06-29 02:06:29 +0000537 def select_pattern (self, files, pattern,
538 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000539 """Select strings (presumably filenames) from 'files' that match
540 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
541 quite the same as implemented by the 'fnmatch' module: '*' and '?'
542 match non-special characters, where "special" is platform-dependent:
543 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
544 Mac OS.
545
546 If 'anchor' is true (the default), then the pattern match is more
547 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
548 'anchor' is false, both of these will match.
549
550 If 'prefix' is supplied, then only filenames starting with 'prefix'
551 (itself a pattern) and ending with 'pattern', with anything in between
552 them, will match. 'anchor' is ignored in this case.
553
Greg Ward499822d2000-06-29 02:06:29 +0000554 If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and
555 'pattern' is assumed to be either a string containing a regex or a
556 regex object -- no translation is done, the regex is just compiled
557 and used as-is.
558
Greg Wardf8b9e202000-06-08 00:08:14 +0000559 Return the list of matching strings, possibly empty.
560 """
561 matches = []
Greg Ward499822d2000-06-29 02:06:29 +0000562 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000563 self.debug_print("select_pattern: applying regex r'%s'" %
564 pattern_re.pattern)
565 for name in files:
566 if pattern_re.search (name):
567 matches.append (name)
568 self.debug_print(" adding " + name)
569
570 return matches
571
572 # select_pattern ()
573
574
Greg Ward499822d2000-06-29 02:06:29 +0000575 def exclude_pattern (self, files, pattern,
576 anchor=1, prefix=None, is_regex=0):
Greg Wardf8b9e202000-06-08 00:08:14 +0000577 """Remove strings (presumably filenames) from 'files' that match
Greg Ward499822d2000-06-29 02:06:29 +0000578 'pattern'. Other parameters are the same as for
579 'select_pattern()', above. The list 'files' is modified in place.
Greg Wardf8b9e202000-06-08 00:08:14 +0000580 """
Greg Ward499822d2000-06-29 02:06:29 +0000581
582 pattern_re = translate_pattern (pattern, anchor, prefix, is_regex)
Greg Wardf8b9e202000-06-08 00:08:14 +0000583 self.debug_print("exclude_pattern: applying regex r'%s'" %
584 pattern_re.pattern)
585 for i in range (len(files)-1, -1, -1):
586 if pattern_re.search (files[i]):
587 self.debug_print(" removing " + files[i])
588 del files[i]
589
590 # exclude_pattern ()
591
592
Greg Warda82122b2000-02-17 23:56:15 +0000593 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000594 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000595 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000596 by 'self.manifest'.
597 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000598 self.execute(write_file,
599 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000600 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000601
602 # write_manifest ()
603
604
605 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000606 """Read the manifest file (named by 'self.manifest') and use it to
607 fill in 'self.files', the list of files to include in the source
608 distribution.
609 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000610 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000611 manifest = open (self.manifest)
612 while 1:
613 line = manifest.readline ()
614 if line == '': # end of file
615 break
616 if line[-1] == '\n':
617 line = line[0:-1]
618 self.files.append (line)
619
620 # read_manifest ()
621
622
Greg Warda82122b2000-02-17 23:56:15 +0000623 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000624 """Create the directory tree that will become the source
625 distribution archive. All directories implied by the filenames in
626 'files' are created under 'base_dir', and then we hard link or copy
627 (if hard linking is unavailable) those files into place.
628 Essentially, this duplicates the developer's source tree, but in a
629 directory named after the distribution, containing only the files
630 to be distributed.
631 """
Greg Ward578c10d2000-03-31 02:50:04 +0000632 # Create all the directories under 'base_dir' necessary to
633 # put 'files' there.
634 create_tree (base_dir, files,
635 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000636
637 # And walk over the list of files, either making a hard link (if
638 # os.link exists) to each one that doesn't already exist in its
639 # corresponding location under 'base_dir', or copying each file
640 # that's out-of-date in 'base_dir'. (Usually, all files will be
641 # out-of-date, because by default we blow away 'base_dir' when
642 # we're done making the distribution archives.)
643
Greg Ward578c10d2000-03-31 02:50:04 +0000644 if hasattr (os, 'link'): # can make hard links on this system
645 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000646 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000647 else: # nope, have to copy
648 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000649 msg = "copying files to %s..." % base_dir
650
651 self.announce (msg)
652 for file in files:
653 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000654 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000655
656 # make_release_tree ()
657
658
Greg Warda82122b2000-02-17 23:56:15 +0000659 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000660 """Create the source distribution(s). First, we create the release
661 tree with 'make_release_tree()'; then, we create all required
662 archive files (according to 'self.formats') from the release tree.
663 Finally, we clean up by blowing away the release tree (unless
664 'self.keep_tree' is true). The list of archive files created is
665 stored so it can be retrieved later by 'get_archive_files()'.
666 """
Greg Ward578c10d2000-03-31 02:50:04 +0000667 # Don't warn about missing meta-data here -- should be (and is!)
668 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000669 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000670
Greg Warda82122b2000-02-17 23:56:15 +0000671 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000672 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000673 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000674 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
675 archive_files.append(file)
676
677 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000678
679 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000680 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000681
Greg Wardd87eb732000-06-01 01:10:56 +0000682 def get_archive_files (self):
683 """Return the list of archive files created when the command
684 was run, or None if the command hasn't run yet.
685 """
686 return self.archive_files
687
Greg Wardfcd974e2000-05-25 01:10:04 +0000688# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000689
690
691# ----------------------------------------------------------------------
692# Utility functions
693
694def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000695 """Find all files under 'dir' and return the list of full filenames
696 (relative to 'dir').
697 """
Greg Ward499822d2000-06-29 02:06:29 +0000698 from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK
699
Greg Warda82122b2000-02-17 23:56:15 +0000700 list = []
701 stack = [dir]
702 pop = stack.pop
703 push = stack.append
704
705 while stack:
706 dir = pop()
707 names = os.listdir (dir)
708
709 for name in names:
710 if dir != os.curdir: # avoid the dreaded "./" syndrome
711 fullname = os.path.join (dir, name)
712 else:
713 fullname = name
Greg Ward499822d2000-06-29 02:06:29 +0000714
715 # Avoid excess stat calls -- just one will do, thank you!
716 stat = os.stat(fullname)
717 mode = stat[ST_MODE]
718 if S_ISREG(mode):
719 list.append (fullname)
720 elif S_ISDIR(mode) and not S_ISLNK(mode):
Greg Warda82122b2000-02-17 23:56:15 +0000721 push (fullname)
722
723 return list
724
725
Greg Warda82122b2000-02-17 23:56:15 +0000726def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000727 """Translate a shell-like glob pattern to a regular expression; return
728 a string containing the regex. Differs from 'fnmatch.translate()' in
729 that '*' does not match "special characters" (which are
730 platform-specific).
731 """
Greg Warda82122b2000-02-17 23:56:15 +0000732 pattern_re = fnmatch.translate (pattern)
733
734 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
735 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
736 # and by extension they shouldn't match such "special characters" under
737 # any OS. So change all non-escaped dots in the RE to match any
738 # character except the special characters.
739 # XXX currently the "special characters" are just slash -- i.e. this is
740 # Unix-only.
741 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
742 return pattern_re
743
744# glob_to_re ()
745
746
Greg Ward499822d2000-06-29 02:06:29 +0000747def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0):
Greg Warda82122b2000-02-17 23:56:15 +0000748 """Translate a shell-like wildcard pattern to a compiled regular
Greg Ward499822d2000-06-29 02:06:29 +0000749 expression. Return the compiled regex. If 'is_regex' true,
750 then 'pattern' is directly compiled to a regex (if it's a string)
751 or just returned as-is (assumes it's a regex object).
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000752 """
Greg Ward499822d2000-06-29 02:06:29 +0000753 if is_regex:
754 if type(pattern) is StringType:
755 return re.compile(pattern)
756 else:
757 return pattern
758
Greg Warda82122b2000-02-17 23:56:15 +0000759 if pattern:
760 pattern_re = glob_to_re (pattern)
761 else:
762 pattern_re = ''
763
764 if prefix is not None:
765 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
766 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
767 else: # no prefix -- respect anchor flag
768 if anchor:
769 pattern_re = "^" + pattern_re
770
771 return re.compile (pattern_re)
772
773# translate_pattern ()