blob: 93e53bbd66b73ce924716206aedad918472e795a [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, \
16 check_archive_formats, 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 Ward1993f9a2000-02-18 00:13:53 +000021class sdist (Command):
Greg Warda82122b2000-02-17 23:56:15 +000022
23 description = "create a source distribution (tarball, zip file, etc.)"
24
Greg Wardbbeceea2000-02-18 00:25:39 +000025 user_options = [
26 ('template=', 't',
27 "name of manifest template file [default: MANIFEST.in]"),
28 ('manifest=', 'm',
29 "name of manifest file [default: MANIFEST]"),
30 ('use-defaults', None,
31 "include the default file set in the manifest "
32 "[default; disable with --no-defaults]"),
Greg Ward839d5322000-04-26 01:14:33 +000033 ('manifest-only', 'o',
Greg Wardc3c8c6e2000-06-08 00:46:45 +000034 "just regenerate the manifest and then stop "
35 "(implies --force-manifest)"),
Greg Ward839d5322000-04-26 01:14:33 +000036 ('force-manifest', 'f',
Greg Wardbbeceea2000-02-18 00:25:39 +000037 "forcibly regenerate the manifest and carry on as usual"),
Greg Wardbbeceea2000-02-18 00:25:39 +000038 ('formats=', None,
Greg Ward2ff78872000-06-24 00:23:20 +000039 "formats for source distribution (comma-separated list)"),
Greg Wardbbeceea2000-02-18 00:25:39 +000040 ('keep-tree', 'k',
41 "keep the distribution tree around after creating " +
42 "archive file(s)"),
43 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000044
45
46 # XXX ugh: this has to precede the 'help_options' list, because
47 # it is mentioned there -- also, this is not a method, even though
48 # it's defined in a class: double-ugh!
49 def show_formats ():
50 """Print all possible values for the 'formats' option -- used by
51 the "--help-formats" command-line option.
52 """
Greg Ward9d17a7a2000-06-07 03:00:06 +000053 from distutils.fancy_getopt import FancyGetopt
Greg Wardf1fe1032000-06-08 00:14:18 +000054 formats=[]
Greg Ward9d17a7a2000-06-07 03:00:06 +000055 for format in ARCHIVE_FORMATS.keys():
Greg Wardf1fe1032000-06-08 00:14:18 +000056 formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2]))
57 formats.sort()
58 pretty_printer = FancyGetopt(formats)
59 pretty_printer.print_help(
60 "List of available source distribution formats:")
Greg Ward9d17a7a2000-06-07 03:00:06 +000061
62 help_options = [
63 ('help-formats', None,
Greg Ward2ff78872000-06-24 00:23:20 +000064 "list available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000065 ]
66
Greg Warda82122b2000-02-17 23:56:15 +000067 negative_opts = {'use-defaults': 'no-defaults'}
68
69 default_format = { 'posix': 'gztar',
70 'nt': 'zip' }
71
Greg Warda82122b2000-02-17 23:56:15 +000072
Greg Warde01149c2000-02-18 00:35:22 +000073 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000074 # 'template' and 'manifest' are, respectively, the names of
75 # the manifest template and manifest file.
76 self.template = None
77 self.manifest = None
78
79 # 'use_defaults': if true, we will include the default file set
80 # in the manifest
81 self.use_defaults = 1
82
83 self.manifest_only = 0
84 self.force_manifest = 0
85
86 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000087 self.keep_tree = 0
88
Greg Wardd87eb732000-06-01 01:10:56 +000089 self.archive_files = None
90
Greg Warda82122b2000-02-17 23:56:15 +000091
Greg Warde01149c2000-02-18 00:35:22 +000092 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000093 if self.manifest is None:
94 self.manifest = "MANIFEST"
95 if self.template is None:
96 self.template = "MANIFEST.in"
97
Greg Ward62d5a572000-06-04 15:12:51 +000098 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +000099 if self.formats is None:
100 try:
101 self.formats = [self.default_format[os.name]]
102 except KeyError:
103 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +0000104 "don't know how to create source distributions " + \
105 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +0000106
Greg Ward6a9a5452000-04-22 03:11:55 +0000107 bad_format = check_archive_formats (self.formats)
108 if bad_format:
109 raise DistutilsOptionError, \
110 "unknown archive format '%s'" % bad_format
111
Greg Warda82122b2000-02-17 23:56:15 +0000112
113 def run (self):
114
115 # 'files' is the list of files that will make up the manifest
116 self.files = []
117
118 # Ensure that all required meta-data is given; warn if not (but
119 # don't die, it's not *that* serious!)
120 self.check_metadata ()
121
122 # Do whatever it takes to get the list of files to process
123 # (process the manifest template, read an existing manifest,
124 # whatever). File list is put into 'self.files'.
125 self.get_file_list ()
126
127 # If user just wanted us to regenerate the manifest, stop now.
128 if self.manifest_only:
129 return
130
131 # Otherwise, go ahead and create the source distribution tarball,
132 # or zipfile, or whatever.
133 self.make_distribution ()
134
135
136 def check_metadata (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000137 """Ensure that all required elements of meta-data (name, version,
138 URL, (author and author_email) or (maintainer and
139 maintainer_email)) are supplied by the Distribution object; warn if
140 any are missing.
141 """
Greg Ward535f2d92000-04-21 04:37:12 +0000142 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000143
144 missing = []
145 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000146 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000147 missing.append (attr)
148
149 if missing:
150 self.warn ("missing required meta-data: " +
151 string.join (missing, ", "))
152
Greg Ward535f2d92000-04-21 04:37:12 +0000153 if metadata.author:
154 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000155 self.warn ("missing meta-data: if 'author' supplied, " +
156 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000157 elif metadata.maintainer:
158 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000159 self.warn ("missing meta-data: if 'maintainer' supplied, " +
160 "'maintainer_email' must be supplied too")
161 else:
162 self.warn ("missing meta-data: either (author and author_email) " +
163 "or (maintainer and maintainer_email) " +
164 "must be supplied")
165
166 # check_metadata ()
167
168
169 def get_file_list (self):
170 """Figure out the list of files to include in the source
Greg Warde0c8c2f2000-06-08 00:24:01 +0000171 distribution, and put it in 'self.files'. This might involve
172 reading the manifest template (and writing the manifest), or just
173 reading the manifest, or just using the default file set -- it all
174 depends on the user's options and the state of the filesystem.
175 """
Greg Wardb2db0eb2000-06-21 03:29:57 +0000176
177 # If we have a manifest template, see if it's newer than the
178 # manifest; if so, we'll regenerate the manifest.
Greg Warda82122b2000-02-17 23:56:15 +0000179 template_exists = os.path.isfile (self.template)
180 if template_exists:
181 template_newer = newer (self.template, self.manifest)
182
Greg Wardb2db0eb2000-06-21 03:29:57 +0000183 # The contents of the manifest file almost certainly depend on the
184 # setup script as well as the manifest template -- so if the setup
185 # script is newer than the manifest, we'll regenerate the manifest
186 # from the template. (Well, not quite: if we already have a
187 # manifest, but there's no template -- which will happen if the
188 # developer elects to generate a manifest some other way -- then we
189 # can't regenerate the manifest, so we don't.)
190 setup_newer = newer(sys.argv[0], self.manifest)
191
192 # cases:
193 # 1) no manifest, template exists: generate manifest
194 # (covered by 2a: no manifest == template newer)
195 # 2) manifest & template exist:
196 # 2a) template or setup script newer than manifest:
197 # regenerate manifest
198 # 2b) manifest newer than both:
199 # do nothing (unless --force or --manifest-only)
200 # 3) manifest exists, no template:
201 # do nothing (unless --force or --manifest-only)
202 # 4) no manifest, no template: generate w/ warning ("defaults only")
203
Greg Warda82122b2000-02-17 23:56:15 +0000204 # Regenerate the manifest if necessary (or if explicitly told to)
Greg Wardb2db0eb2000-06-21 03:29:57 +0000205 if ((template_exists and (template_newer or setup_newer)) or
206 self.force_manifest or self.manifest_only):
Greg Warda82122b2000-02-17 23:56:15 +0000207
208 if not template_exists:
209 self.warn (("manifest template '%s' does not exist " +
210 "(using default file list)") %
211 self.template)
212
213 # Add default file set to 'files'
214 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000215 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000216
217 # Read manifest template if it exists
218 if template_exists:
219 self.read_template ()
220
Greg Wardce15c6c2000-06-08 01:06:02 +0000221 # Prune away the build and source distribution directories
222 self.prune_file_list()
223
Greg Warda82122b2000-02-17 23:56:15 +0000224 # File list now complete -- sort it so that higher-level files
225 # come first
226 sortable_files = map (os.path.split, self.files)
227 sortable_files.sort ()
228 self.files = []
229 for sort_tuple in sortable_files:
230 self.files.append (apply (os.path.join, sort_tuple))
231
232 # Remove duplicates from the file list
233 for i in range (len(self.files)-1, 0, -1):
234 if self.files[i] == self.files[i-1]:
235 del self.files[i]
236
237 # And write complete file list (including default file set) to
238 # the manifest.
239 self.write_manifest ()
240
241 # Don't regenerate the manifest, just read it in.
242 else:
243 self.read_manifest ()
244
245 # get_file_list ()
246
247
Greg Ward4a7319c2000-06-08 00:52:52 +0000248 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000249 """Add all the default files to self.files:
250 - README or README.txt
251 - setup.py
252 - test/test*.py
253 - all pure Python modules mentioned in setup script
254 - all C sources listed as part of extensions or C libraries
255 in the setup script (doesn't catch C headers!)
256 Warns if (README or README.txt) or setup.py are missing; everything
257 else is optional.
258 """
Greg Ward14c8d052000-06-08 01:22:48 +0000259
260 # XXX name of setup script and config file should be taken
261 # programmatically from the Distribution object (except
262 # it doesn't have that capability... yet!)
Greg Warda82122b2000-02-17 23:56:15 +0000263 standards = [('README', 'README.txt'), 'setup.py']
264 for fn in standards:
265 if type (fn) is TupleType:
266 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000267 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000268 for fn in alts:
269 if os.path.exists (fn):
270 got_it = 1
271 self.files.append (fn)
272 break
273
274 if not got_it:
275 self.warn ("standard file not found: should have one of " +
276 string.join (alts, ', '))
277 else:
278 if os.path.exists (fn):
279 self.files.append (fn)
280 else:
281 self.warn ("standard file '%s' not found" % fn)
282
Greg Ward14c8d052000-06-08 01:22:48 +0000283 optional = ['test/test*.py', 'setup.cfg']
Greg Warda82122b2000-02-17 23:56:15 +0000284 for pattern in optional:
285 files = filter (os.path.isfile, glob (pattern))
286 if files:
287 self.files.extend (files)
288
Greg Ward578c10d2000-03-31 02:50:04 +0000289 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000290 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000291 self.files.extend (build_py.get_source_files ())
292
Greg Ward578c10d2000-03-31 02:50:04 +0000293 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000294 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000295 self.files.extend (build_ext.get_source_files ())
296
Greg Ward60908f12000-04-09 03:51:40 +0000297 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000298 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000299 self.files.extend (build_clib.get_source_files ())
300
Greg Ward4a7319c2000-06-08 00:52:52 +0000301 # add_defaults ()
302
Greg Warda82122b2000-02-17 23:56:15 +0000303
Greg Warda82122b2000-02-17 23:56:15 +0000304 def search_dir (self, dir, pattern=None):
305 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000306 containing a Unix-style glob pattern). If 'pattern' is None, find
307 all files under 'dir'. Return the list of found filenames.
308 """
Greg Warda82122b2000-02-17 23:56:15 +0000309 allfiles = findall (dir)
310 if pattern is None:
311 return allfiles
312
313 pattern_re = translate_pattern (pattern)
314 files = []
315 for file in allfiles:
316 if pattern_re.match (os.path.basename (file)):
317 files.append (file)
318
319 return files
320
321 # search_dir ()
322
323
Greg Warda82122b2000-02-17 23:56:15 +0000324 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000325 """Remove filenames from 'self.files' that are under 'dir' and
326 whose basenames match 'pattern'.
327 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000328 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
329 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000330 if pattern is None:
331 pattern_re = None
332 else:
333 pattern_re = translate_pattern (pattern)
334
335 for i in range (len (self.files)-1, -1, -1):
336 (cur_dir, cur_base) = os.path.split (self.files[i])
337 if (cur_dir == dir and
338 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000339 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000340 del self.files[i]
341
342
343 def read_template (self):
344 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000345 'self.template' (usually "MANIFEST.in"). Process all file
346 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000347 update 'self.files' accordingly (filenames may be added to
348 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000349 """
Greg Warda82122b2000-02-17 23:56:15 +0000350 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000351 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000352
353 template = TextFile (self.template,
354 strip_comments=1,
355 skip_blanks=1,
356 join_lines=1,
357 lstrip_ws=1,
358 rstrip_ws=1,
359 collapse_ws=1)
360
361 all_files = findall ()
362
363 while 1:
364
365 line = template.readline()
366 if line is None: # end of file
367 break
368
369 words = string.split (line)
370 action = words[0]
371
372 # First, check that the right number of words are present
373 # for the given action (which is the first word)
374 if action in ('include','exclude',
375 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000376 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000377 template.warn \
378 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000379 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000380 action)
381 continue
382
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000383 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000384
385 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000386 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000387 template.warn \
388 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000389 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000390 action)
391 continue
392
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000393 dir = convert_path(words[1])
394 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000395
396 elif action in ('graft','prune'):
397 if len (words) != 2:
398 template.warn \
399 ("invalid manifest template line: " +
400 "'%s' expects a single <dir_pattern>" %
401 action)
402 continue
403
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000404 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000405
406 else:
407 template.warn ("invalid manifest template line: " +
408 "unknown action '%s'" % action)
409 continue
410
411 # OK, now we know that the action is valid and we have the
412 # right number of words on the line for that action -- so we
413 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000414 # defined either (pattern), (dir and pattern), or
415 # (dir_pattern) -- so we don't have to spend any time
416 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000417
418 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000419 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000420 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000421 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000422 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000423 template.warn ("no files found matching '%s'" %
424 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000425 else:
426 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000427
428 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000429 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000430 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000431 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000432 if num == 0:
433 template.warn (
434 "no previously-included files found matching '%s'"%
435 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000436
437 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000438 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000439 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000440 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000441 if not files:
442 template.warn (("no files found matching '%s' " +
443 "anywhere in distribution") %
444 pattern)
445 else:
446 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000447
448 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000449 self.debug_print("global-exclude " + 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 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000452 if num == 0:
453 template.warn \
454 (("no previously-included files matching '%s' " +
455 "found anywhere in distribution") %
456 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000457
458 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000459 self.debug_print("recursive-include %s %s" %
460 (dir, 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 files = self.select_pattern (
463 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000464 if not files:
465 template.warn (("no files found matching '%s' " +
466 "under directory '%s'") %
467 (pattern, dir))
468 else:
469 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000470
471 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000472 self.debug_print("recursive-exclude %s %s" %
473 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000474 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000475 num = self.exclude_pattern(
476 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000477 if num == 0:
478 template.warn \
479 (("no previously-included files matching '%s' " +
480 "found under directory '%s'") %
481 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000482
483 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000484 self.debug_print("graft " + dir_pattern)
485 files = self.select_pattern(
486 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000487 if not files:
488 template.warn ("no directories found matching '%s'" %
489 dir_pattern)
490 else:
491 self.files.extend (files)
492
493 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000494 self.debug_print("prune " + dir_pattern)
495 num = self.exclude_pattern(
496 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000497 if num == 0:
498 template.warn \
499 (("no previously-included directories found " +
500 "matching '%s'") %
501 dir_pattern)
502 else:
503 raise RuntimeError, \
504 "this cannot happen: invalid action '%s'" % action
505
506 # while loop over lines of template file
507
508 # read_template ()
509
510
Greg Wardce15c6c2000-06-08 01:06:02 +0000511 def prune_file_list (self):
512 """Prune off branches that might slip into the file list as created
513 by 'read_template()', but really don't belong there: specifically,
514 the build tree (typically "build") and the release tree itself
515 (only an issue if we ran "sdist" previously with --keep-tree, or it
516 aborted).
517 """
518 build = self.get_finalized_command('build')
519 base_dir = self.distribution.get_fullname()
520 self.exclude_pattern (self.files, None, prefix=build.build_base)
521 self.exclude_pattern (self.files, None, prefix=base_dir)
522
523
Greg Wardf8b9e202000-06-08 00:08:14 +0000524 def select_pattern (self, files, pattern, anchor=1, prefix=None):
525 """Select strings (presumably filenames) from 'files' that match
526 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
527 quite the same as implemented by the 'fnmatch' module: '*' and '?'
528 match non-special characters, where "special" is platform-dependent:
529 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
530 Mac OS.
531
532 If 'anchor' is true (the default), then the pattern match is more
533 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
534 'anchor' is false, both of these will match.
535
536 If 'prefix' is supplied, then only filenames starting with 'prefix'
537 (itself a pattern) and ending with 'pattern', with anything in between
538 them, will match. 'anchor' is ignored in this case.
539
540 Return the list of matching strings, possibly empty.
541 """
542 matches = []
543 pattern_re = translate_pattern (pattern, anchor, prefix)
544 self.debug_print("select_pattern: applying regex r'%s'" %
545 pattern_re.pattern)
546 for name in files:
547 if pattern_re.search (name):
548 matches.append (name)
549 self.debug_print(" adding " + name)
550
551 return matches
552
553 # select_pattern ()
554
555
556 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
557 """Remove strings (presumably filenames) from 'files' that match
558 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
559 as for 'select_pattern()', above. The list 'files' is modified
560 in place.
561 """
562 pattern_re = translate_pattern (pattern, anchor, prefix)
563 self.debug_print("exclude_pattern: applying regex r'%s'" %
564 pattern_re.pattern)
565 for i in range (len(files)-1, -1, -1):
566 if pattern_re.search (files[i]):
567 self.debug_print(" removing " + files[i])
568 del files[i]
569
570 # exclude_pattern ()
571
572
Greg Warda82122b2000-02-17 23:56:15 +0000573 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000574 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000575 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000576 by 'self.manifest'.
577 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000578 self.execute(write_file,
579 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000580 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000581
582 # write_manifest ()
583
584
585 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000586 """Read the manifest file (named by 'self.manifest') and use it to
587 fill in 'self.files', the list of files to include in the source
588 distribution.
589 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000590 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000591 manifest = open (self.manifest)
592 while 1:
593 line = manifest.readline ()
594 if line == '': # end of file
595 break
596 if line[-1] == '\n':
597 line = line[0:-1]
598 self.files.append (line)
599
600 # read_manifest ()
601
602
Greg Warda82122b2000-02-17 23:56:15 +0000603 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000604 """Create the directory tree that will become the source
605 distribution archive. All directories implied by the filenames in
606 'files' are created under 'base_dir', and then we hard link or copy
607 (if hard linking is unavailable) those files into place.
608 Essentially, this duplicates the developer's source tree, but in a
609 directory named after the distribution, containing only the files
610 to be distributed.
611 """
Greg Ward578c10d2000-03-31 02:50:04 +0000612 # Create all the directories under 'base_dir' necessary to
613 # put 'files' there.
614 create_tree (base_dir, files,
615 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000616
617 # And walk over the list of files, either making a hard link (if
618 # os.link exists) to each one that doesn't already exist in its
619 # corresponding location under 'base_dir', or copying each file
620 # that's out-of-date in 'base_dir'. (Usually, all files will be
621 # out-of-date, because by default we blow away 'base_dir' when
622 # we're done making the distribution archives.)
623
Greg Ward578c10d2000-03-31 02:50:04 +0000624 if hasattr (os, 'link'): # can make hard links on this system
625 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000626 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000627 else: # nope, have to copy
628 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000629 msg = "copying files to %s..." % base_dir
630
631 self.announce (msg)
632 for file in files:
633 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000634 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000635
636 # make_release_tree ()
637
638
Greg Warda82122b2000-02-17 23:56:15 +0000639 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000640 """Create the source distribution(s). First, we create the release
641 tree with 'make_release_tree()'; then, we create all required
642 archive files (according to 'self.formats') from the release tree.
643 Finally, we clean up by blowing away the release tree (unless
644 'self.keep_tree' is true). The list of archive files created is
645 stored so it can be retrieved later by 'get_archive_files()'.
646 """
Greg Ward578c10d2000-03-31 02:50:04 +0000647 # Don't warn about missing meta-data here -- should be (and is!)
648 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000649 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000650
Greg Warda82122b2000-02-17 23:56:15 +0000651 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000652 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000653 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000654 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
655 archive_files.append(file)
656
657 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000658
659 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000660 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000661
Greg Wardd87eb732000-06-01 01:10:56 +0000662 def get_archive_files (self):
663 """Return the list of archive files created when the command
664 was run, or None if the command hasn't run yet.
665 """
666 return self.archive_files
667
Greg Wardfcd974e2000-05-25 01:10:04 +0000668# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000669
670
671# ----------------------------------------------------------------------
672# Utility functions
673
674def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000675 """Find all files under 'dir' and return the list of full filenames
676 (relative to 'dir').
677 """
Greg Warda82122b2000-02-17 23:56:15 +0000678 list = []
679 stack = [dir]
680 pop = stack.pop
681 push = stack.append
682
683 while stack:
684 dir = pop()
685 names = os.listdir (dir)
686
687 for name in names:
688 if dir != os.curdir: # avoid the dreaded "./" syndrome
689 fullname = os.path.join (dir, name)
690 else:
691 fullname = name
692 list.append (fullname)
693 if os.path.isdir (fullname) and not os.path.islink(fullname):
694 push (fullname)
695
696 return list
697
698
Greg Warda82122b2000-02-17 23:56:15 +0000699def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000700 """Translate a shell-like glob pattern to a regular expression; return
701 a string containing the regex. Differs from 'fnmatch.translate()' in
702 that '*' does not match "special characters" (which are
703 platform-specific).
704 """
Greg Warda82122b2000-02-17 23:56:15 +0000705 pattern_re = fnmatch.translate (pattern)
706
707 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
708 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
709 # and by extension they shouldn't match such "special characters" under
710 # any OS. So change all non-escaped dots in the RE to match any
711 # character except the special characters.
712 # XXX currently the "special characters" are just slash -- i.e. this is
713 # Unix-only.
714 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
715 return pattern_re
716
717# glob_to_re ()
718
719
720def translate_pattern (pattern, anchor=1, prefix=None):
721 """Translate a shell-like wildcard pattern to a compiled regular
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000722 expression. Return the compiled regex.
723 """
Greg Warda82122b2000-02-17 23:56:15 +0000724 if pattern:
725 pattern_re = glob_to_re (pattern)
726 else:
727 pattern_re = ''
728
729 if prefix is not None:
730 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
731 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
732 else: # no prefix -- respect anchor flag
733 if anchor:
734 pattern_re = "^" + pattern_re
735
736 return re.compile (pattern_re)
737
738# translate_pattern ()