blob: 48e9793e5ef5926c745e36b96495ed42a032ddec [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 Ward9d17a7a2000-06-07 03:00:06 +000039 "formats for source distribution"),
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 Wardf1fe1032000-06-08 00:14:18 +000064 "lists 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 Warda82122b2000-02-17 23:56:15 +0000176 template_exists = os.path.isfile (self.template)
177 if template_exists:
178 template_newer = newer (self.template, self.manifest)
179
180 # Regenerate the manifest if necessary (or if explicitly told to)
181 if ((template_exists and template_newer) or
182 self.force_manifest or
183 self.manifest_only):
184
185 if not template_exists:
186 self.warn (("manifest template '%s' does not exist " +
187 "(using default file list)") %
188 self.template)
189
190 # Add default file set to 'files'
191 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000192 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000193
194 # Read manifest template if it exists
195 if template_exists:
196 self.read_template ()
197
Greg Wardce15c6c2000-06-08 01:06:02 +0000198 # Prune away the build and source distribution directories
199 self.prune_file_list()
200
Greg Warda82122b2000-02-17 23:56:15 +0000201 # File list now complete -- sort it so that higher-level files
202 # come first
203 sortable_files = map (os.path.split, self.files)
204 sortable_files.sort ()
205 self.files = []
206 for sort_tuple in sortable_files:
207 self.files.append (apply (os.path.join, sort_tuple))
208
209 # Remove duplicates from the file list
210 for i in range (len(self.files)-1, 0, -1):
211 if self.files[i] == self.files[i-1]:
212 del self.files[i]
213
214 # And write complete file list (including default file set) to
215 # the manifest.
216 self.write_manifest ()
217
218 # Don't regenerate the manifest, just read it in.
219 else:
220 self.read_manifest ()
221
222 # get_file_list ()
223
224
Greg Ward4a7319c2000-06-08 00:52:52 +0000225 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000226 """Add all the default files to self.files:
227 - README or README.txt
228 - setup.py
229 - test/test*.py
230 - all pure Python modules mentioned in setup script
231 - all C sources listed as part of extensions or C libraries
232 in the setup script (doesn't catch C headers!)
233 Warns if (README or README.txt) or setup.py are missing; everything
234 else is optional.
235 """
Greg Ward14c8d052000-06-08 01:22:48 +0000236
237 # XXX name of setup script and config file should be taken
238 # programmatically from the Distribution object (except
239 # it doesn't have that capability... yet!)
Greg Warda82122b2000-02-17 23:56:15 +0000240 standards = [('README', 'README.txt'), 'setup.py']
241 for fn in standards:
242 if type (fn) is TupleType:
243 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000244 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000245 for fn in alts:
246 if os.path.exists (fn):
247 got_it = 1
248 self.files.append (fn)
249 break
250
251 if not got_it:
252 self.warn ("standard file not found: should have one of " +
253 string.join (alts, ', '))
254 else:
255 if os.path.exists (fn):
256 self.files.append (fn)
257 else:
258 self.warn ("standard file '%s' not found" % fn)
259
Greg Ward14c8d052000-06-08 01:22:48 +0000260 optional = ['test/test*.py', 'setup.cfg']
Greg Warda82122b2000-02-17 23:56:15 +0000261 for pattern in optional:
262 files = filter (os.path.isfile, glob (pattern))
263 if files:
264 self.files.extend (files)
265
Greg Ward578c10d2000-03-31 02:50:04 +0000266 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000267 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000268 self.files.extend (build_py.get_source_files ())
269
Greg Ward578c10d2000-03-31 02:50:04 +0000270 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000271 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000272 self.files.extend (build_ext.get_source_files ())
273
Greg Ward60908f12000-04-09 03:51:40 +0000274 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000275 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000276 self.files.extend (build_clib.get_source_files ())
277
Greg Ward4a7319c2000-06-08 00:52:52 +0000278 # add_defaults ()
279
Greg Warda82122b2000-02-17 23:56:15 +0000280
Greg Warda82122b2000-02-17 23:56:15 +0000281 def search_dir (self, dir, pattern=None):
282 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000283 containing a Unix-style glob pattern). If 'pattern' is None, find
284 all files under 'dir'. Return the list of found filenames.
285 """
Greg Warda82122b2000-02-17 23:56:15 +0000286 allfiles = findall (dir)
287 if pattern is None:
288 return allfiles
289
290 pattern_re = translate_pattern (pattern)
291 files = []
292 for file in allfiles:
293 if pattern_re.match (os.path.basename (file)):
294 files.append (file)
295
296 return files
297
298 # search_dir ()
299
300
Greg Warda82122b2000-02-17 23:56:15 +0000301 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000302 """Remove filenames from 'self.files' that are under 'dir' and
303 whose basenames match 'pattern'.
304 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000305 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
306 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000307 if pattern is None:
308 pattern_re = None
309 else:
310 pattern_re = translate_pattern (pattern)
311
312 for i in range (len (self.files)-1, -1, -1):
313 (cur_dir, cur_base) = os.path.split (self.files[i])
314 if (cur_dir == dir and
315 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000316 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000317 del self.files[i]
318
319
320 def read_template (self):
321 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000322 'self.template' (usually "MANIFEST.in"). Process all file
323 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000324 update 'self.files' accordingly (filenames may be added to
325 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000326 """
Greg Warda82122b2000-02-17 23:56:15 +0000327 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000328 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000329
330 template = TextFile (self.template,
331 strip_comments=1,
332 skip_blanks=1,
333 join_lines=1,
334 lstrip_ws=1,
335 rstrip_ws=1,
336 collapse_ws=1)
337
338 all_files = findall ()
339
340 while 1:
341
342 line = template.readline()
343 if line is None: # end of file
344 break
345
346 words = string.split (line)
347 action = words[0]
348
349 # First, check that the right number of words are present
350 # for the given action (which is the first word)
351 if action in ('include','exclude',
352 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000353 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000354 template.warn \
355 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000356 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000357 action)
358 continue
359
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000360 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000361
362 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000363 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000364 template.warn \
365 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000366 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000367 action)
368 continue
369
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000370 dir = convert_path(words[1])
371 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000372
373 elif action in ('graft','prune'):
374 if len (words) != 2:
375 template.warn \
376 ("invalid manifest template line: " +
377 "'%s' expects a single <dir_pattern>" %
378 action)
379 continue
380
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000381 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000382
383 else:
384 template.warn ("invalid manifest template line: " +
385 "unknown action '%s'" % action)
386 continue
387
388 # OK, now we know that the action is valid and we have the
389 # right number of words on the line for that action -- so we
390 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000391 # defined either (pattern), (dir and pattern), or
392 # (dir_pattern) -- so we don't have to spend any time
393 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000394
395 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000396 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000397 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000398 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000399 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000400 template.warn ("no files found matching '%s'" %
401 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000402 else:
403 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000404
405 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000406 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000407 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000408 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000409 if num == 0:
410 template.warn (
411 "no previously-included files found matching '%s'"%
412 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000413
414 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000415 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000416 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000417 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000418 if not files:
419 template.warn (("no files found matching '%s' " +
420 "anywhere in distribution") %
421 pattern)
422 else:
423 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000424
425 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000426 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000427 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000428 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000429 if num == 0:
430 template.warn \
431 (("no previously-included files matching '%s' " +
432 "found anywhere in distribution") %
433 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000434
435 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000436 self.debug_print("recursive-include %s %s" %
437 (dir, 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 (
440 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000441 if not files:
442 template.warn (("no files found matching '%s' " +
443 "under directory '%s'") %
444 (pattern, dir))
445 else:
446 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000447
448 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000449 self.debug_print("recursive-exclude %s %s" %
450 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000451 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000452 num = self.exclude_pattern(
453 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000454 if num == 0:
455 template.warn \
456 (("no previously-included files matching '%s' " +
457 "found under directory '%s'") %
458 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000459
460 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000461 self.debug_print("graft " + dir_pattern)
462 files = self.select_pattern(
463 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000464 if not files:
465 template.warn ("no directories found matching '%s'" %
466 dir_pattern)
467 else:
468 self.files.extend (files)
469
470 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000471 self.debug_print("prune " + dir_pattern)
472 num = self.exclude_pattern(
473 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000474 if num == 0:
475 template.warn \
476 (("no previously-included directories found " +
477 "matching '%s'") %
478 dir_pattern)
479 else:
480 raise RuntimeError, \
481 "this cannot happen: invalid action '%s'" % action
482
483 # while loop over lines of template file
484
485 # read_template ()
486
487
Greg Wardce15c6c2000-06-08 01:06:02 +0000488 def prune_file_list (self):
489 """Prune off branches that might slip into the file list as created
490 by 'read_template()', but really don't belong there: specifically,
491 the build tree (typically "build") and the release tree itself
492 (only an issue if we ran "sdist" previously with --keep-tree, or it
493 aborted).
494 """
495 build = self.get_finalized_command('build')
496 base_dir = self.distribution.get_fullname()
497 self.exclude_pattern (self.files, None, prefix=build.build_base)
498 self.exclude_pattern (self.files, None, prefix=base_dir)
499
500
Greg Wardf8b9e202000-06-08 00:08:14 +0000501 def select_pattern (self, files, pattern, anchor=1, prefix=None):
502 """Select strings (presumably filenames) from 'files' that match
503 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
504 quite the same as implemented by the 'fnmatch' module: '*' and '?'
505 match non-special characters, where "special" is platform-dependent:
506 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
507 Mac OS.
508
509 If 'anchor' is true (the default), then the pattern match is more
510 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
511 'anchor' is false, both of these will match.
512
513 If 'prefix' is supplied, then only filenames starting with 'prefix'
514 (itself a pattern) and ending with 'pattern', with anything in between
515 them, will match. 'anchor' is ignored in this case.
516
517 Return the list of matching strings, possibly empty.
518 """
519 matches = []
520 pattern_re = translate_pattern (pattern, anchor, prefix)
521 self.debug_print("select_pattern: applying regex r'%s'" %
522 pattern_re.pattern)
523 for name in files:
524 if pattern_re.search (name):
525 matches.append (name)
526 self.debug_print(" adding " + name)
527
528 return matches
529
530 # select_pattern ()
531
532
533 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
534 """Remove strings (presumably filenames) from 'files' that match
535 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
536 as for 'select_pattern()', above. The list 'files' is modified
537 in place.
538 """
539 pattern_re = translate_pattern (pattern, anchor, prefix)
540 self.debug_print("exclude_pattern: applying regex r'%s'" %
541 pattern_re.pattern)
542 for i in range (len(files)-1, -1, -1):
543 if pattern_re.search (files[i]):
544 self.debug_print(" removing " + files[i])
545 del files[i]
546
547 # exclude_pattern ()
548
549
Greg Warda82122b2000-02-17 23:56:15 +0000550 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000551 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000552 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000553 by 'self.manifest'.
554 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000555 self.execute(write_file,
556 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000557 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000558
559 # write_manifest ()
560
561
562 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000563 """Read the manifest file (named by 'self.manifest') and use it to
564 fill in 'self.files', the list of files to include in the source
565 distribution.
566 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000567 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000568 manifest = open (self.manifest)
569 while 1:
570 line = manifest.readline ()
571 if line == '': # end of file
572 break
573 if line[-1] == '\n':
574 line = line[0:-1]
575 self.files.append (line)
576
577 # read_manifest ()
578
579
Greg Warda82122b2000-02-17 23:56:15 +0000580 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000581 """Create the directory tree that will become the source
582 distribution archive. All directories implied by the filenames in
583 'files' are created under 'base_dir', and then we hard link or copy
584 (if hard linking is unavailable) those files into place.
585 Essentially, this duplicates the developer's source tree, but in a
586 directory named after the distribution, containing only the files
587 to be distributed.
588 """
Greg Ward578c10d2000-03-31 02:50:04 +0000589 # Create all the directories under 'base_dir' necessary to
590 # put 'files' there.
591 create_tree (base_dir, files,
592 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000593
594 # And walk over the list of files, either making a hard link (if
595 # os.link exists) to each one that doesn't already exist in its
596 # corresponding location under 'base_dir', or copying each file
597 # that's out-of-date in 'base_dir'. (Usually, all files will be
598 # out-of-date, because by default we blow away 'base_dir' when
599 # we're done making the distribution archives.)
600
Greg Ward578c10d2000-03-31 02:50:04 +0000601 if hasattr (os, 'link'): # can make hard links on this system
602 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000603 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000604 else: # nope, have to copy
605 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000606 msg = "copying files to %s..." % base_dir
607
608 self.announce (msg)
609 for file in files:
610 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000611 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000612
613 # make_release_tree ()
614
615
Greg Warda82122b2000-02-17 23:56:15 +0000616 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000617 """Create the source distribution(s). First, we create the release
618 tree with 'make_release_tree()'; then, we create all required
619 archive files (according to 'self.formats') from the release tree.
620 Finally, we clean up by blowing away the release tree (unless
621 'self.keep_tree' is true). The list of archive files created is
622 stored so it can be retrieved later by 'get_archive_files()'.
623 """
Greg Ward578c10d2000-03-31 02:50:04 +0000624 # Don't warn about missing meta-data here -- should be (and is!)
625 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000626 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000627
Greg Warda82122b2000-02-17 23:56:15 +0000628 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000629 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000630 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000631 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
632 archive_files.append(file)
633
634 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000635
636 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000637 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000638
Greg Wardd87eb732000-06-01 01:10:56 +0000639 def get_archive_files (self):
640 """Return the list of archive files created when the command
641 was run, or None if the command hasn't run yet.
642 """
643 return self.archive_files
644
Greg Wardfcd974e2000-05-25 01:10:04 +0000645# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000646
647
648# ----------------------------------------------------------------------
649# Utility functions
650
651def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000652 """Find all files under 'dir' and return the list of full filenames
653 (relative to 'dir').
654 """
Greg Warda82122b2000-02-17 23:56:15 +0000655 list = []
656 stack = [dir]
657 pop = stack.pop
658 push = stack.append
659
660 while stack:
661 dir = pop()
662 names = os.listdir (dir)
663
664 for name in names:
665 if dir != os.curdir: # avoid the dreaded "./" syndrome
666 fullname = os.path.join (dir, name)
667 else:
668 fullname = name
669 list.append (fullname)
670 if os.path.isdir (fullname) and not os.path.islink(fullname):
671 push (fullname)
672
673 return list
674
675
Greg Warda82122b2000-02-17 23:56:15 +0000676def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000677 """Translate a shell-like glob pattern to a regular expression; return
678 a string containing the regex. Differs from 'fnmatch.translate()' in
679 that '*' does not match "special characters" (which are
680 platform-specific).
681 """
Greg Warda82122b2000-02-17 23:56:15 +0000682 pattern_re = fnmatch.translate (pattern)
683
684 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
685 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
686 # and by extension they shouldn't match such "special characters" under
687 # any OS. So change all non-escaped dots in the RE to match any
688 # character except the special characters.
689 # XXX currently the "special characters" are just slash -- i.e. this is
690 # Unix-only.
691 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
692 return pattern_re
693
694# glob_to_re ()
695
696
697def translate_pattern (pattern, anchor=1, prefix=None):
698 """Translate a shell-like wildcard pattern to a compiled regular
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000699 expression. Return the compiled regex.
700 """
Greg Warda82122b2000-02-17 23:56:15 +0000701 if pattern:
702 pattern_re = glob_to_re (pattern)
703 else:
704 pattern_re = ''
705
706 if prefix is not None:
707 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
708 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
709 else: # no prefix -- respect anchor flag
710 if anchor:
711 pattern_re = "^" + pattern_re
712
713 return re.compile (pattern_re)
714
715# translate_pattern ()