blob: 2a4d346fff325928b8f34a9781f08818720d4471 [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:
192 self.find_defaults ()
193
194 # Read manifest template if it exists
195 if template_exists:
196 self.read_template ()
197
198 # File list now complete -- sort it so that higher-level files
199 # come first
200 sortable_files = map (os.path.split, self.files)
201 sortable_files.sort ()
202 self.files = []
203 for sort_tuple in sortable_files:
204 self.files.append (apply (os.path.join, sort_tuple))
205
206 # Remove duplicates from the file list
207 for i in range (len(self.files)-1, 0, -1):
208 if self.files[i] == self.files[i-1]:
209 del self.files[i]
210
211 # And write complete file list (including default file set) to
212 # the manifest.
213 self.write_manifest ()
214
215 # Don't regenerate the manifest, just read it in.
216 else:
217 self.read_manifest ()
218
219 # get_file_list ()
220
221
222 def find_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000223 """Add all the default files to self.files:
224 - README or README.txt
225 - setup.py
226 - test/test*.py
227 - all pure Python modules mentioned in setup script
228 - all C sources listed as part of extensions or C libraries
229 in the setup script (doesn't catch C headers!)
230 Warns if (README or README.txt) or setup.py are missing; everything
231 else is optional.
232 """
Greg Warda82122b2000-02-17 23:56:15 +0000233 standards = [('README', 'README.txt'), 'setup.py']
234 for fn in standards:
235 if type (fn) is TupleType:
236 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000237 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000238 for fn in alts:
239 if os.path.exists (fn):
240 got_it = 1
241 self.files.append (fn)
242 break
243
244 if not got_it:
245 self.warn ("standard file not found: should have one of " +
246 string.join (alts, ', '))
247 else:
248 if os.path.exists (fn):
249 self.files.append (fn)
250 else:
251 self.warn ("standard file '%s' not found" % fn)
252
253 optional = ['test/test*.py']
254 for pattern in optional:
255 files = filter (os.path.isfile, glob (pattern))
256 if files:
257 self.files.extend (files)
258
Greg Ward578c10d2000-03-31 02:50:04 +0000259 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000260 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000261 self.files.extend (build_py.get_source_files ())
262
Greg Ward578c10d2000-03-31 02:50:04 +0000263 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000264 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000265 self.files.extend (build_ext.get_source_files ())
266
Greg Ward60908f12000-04-09 03:51:40 +0000267 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000268 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000269 self.files.extend (build_clib.get_source_files ())
270
Greg Warda82122b2000-02-17 23:56:15 +0000271
Greg Warda82122b2000-02-17 23:56:15 +0000272 def search_dir (self, dir, pattern=None):
273 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000274 containing a Unix-style glob pattern). If 'pattern' is None, find
275 all files under 'dir'. Return the list of found filenames.
276 """
Greg Warda82122b2000-02-17 23:56:15 +0000277 allfiles = findall (dir)
278 if pattern is None:
279 return allfiles
280
281 pattern_re = translate_pattern (pattern)
282 files = []
283 for file in allfiles:
284 if pattern_re.match (os.path.basename (file)):
285 files.append (file)
286
287 return files
288
289 # search_dir ()
290
291
Greg Wardf8b9e202000-06-08 00:08:14 +0000292# def exclude_pattern (self, pattern):
293# """Remove filenames from 'self.files' that match 'pattern'."""
294# self.debug_print("exclude_pattern: pattern=%s" % pattern)
295# pattern_re = translate_pattern (pattern)
296# for i in range (len (self.files)-1, -1, -1):
297# if pattern_re.match (self.files[i]):
298# self.debug_print("removing %s" % self.files[i])
299# del self.files[i]
Greg Warda82122b2000-02-17 23:56:15 +0000300
301
302 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000303 """Remove filenames from 'self.files' that are under 'dir' and
304 whose basenames match 'pattern'.
305 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000306 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
307 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000308 if pattern is None:
309 pattern_re = None
310 else:
311 pattern_re = translate_pattern (pattern)
312
313 for i in range (len (self.files)-1, -1, -1):
314 (cur_dir, cur_base) = os.path.split (self.files[i])
315 if (cur_dir == dir and
316 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000317 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000318 del self.files[i]
319
320
321 def read_template (self):
322 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000323 'self.template' (usually "MANIFEST.in"). Process all file
324 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000325 update 'self.files' accordingly (filenames may be added to
326 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000327 """
Greg Warda82122b2000-02-17 23:56:15 +0000328 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000329 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000330
331 template = TextFile (self.template,
332 strip_comments=1,
333 skip_blanks=1,
334 join_lines=1,
335 lstrip_ws=1,
336 rstrip_ws=1,
337 collapse_ws=1)
338
339 all_files = findall ()
340
341 while 1:
342
343 line = template.readline()
344 if line is None: # end of file
345 break
346
347 words = string.split (line)
348 action = words[0]
349
350 # First, check that the right number of words are present
351 # for the given action (which is the first word)
352 if action in ('include','exclude',
353 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000354 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000355 template.warn \
356 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000357 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000358 action)
359 continue
360
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000361 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000362
363 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000364 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000365 template.warn \
366 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000367 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000368 action)
369 continue
370
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000371 dir = convert_path(words[1])
372 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000373
374 elif action in ('graft','prune'):
375 if len (words) != 2:
376 template.warn \
377 ("invalid manifest template line: " +
378 "'%s' expects a single <dir_pattern>" %
379 action)
380 continue
381
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000382 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000383
384 else:
385 template.warn ("invalid manifest template line: " +
386 "unknown action '%s'" % action)
387 continue
388
389 # OK, now we know that the action is valid and we have the
390 # right number of words on the line for that action -- so we
391 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000392 # defined either (pattern), (dir and pattern), or
393 # (dir_pattern) -- so we don't have to spend any time
394 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000395
396 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000397 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000398 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000399 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000400 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000401 template.warn ("no files found matching '%s'" %
402 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000403 else:
404 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000405
406 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000407 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000408 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000409 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000410 if num == 0:
411 template.warn (
412 "no previously-included files found matching '%s'"%
413 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000414
415 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000416 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000417 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000418 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000419 if not files:
420 template.warn (("no files found matching '%s' " +
421 "anywhere in distribution") %
422 pattern)
423 else:
424 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000425
426 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000427 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000428 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000429 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000430 if num == 0:
431 template.warn \
432 (("no previously-included files matching '%s' " +
433 "found anywhere in distribution") %
434 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000435
436 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000437 self.debug_print("recursive-include %s %s" %
438 (dir, 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 (
441 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000442 if not files:
443 template.warn (("no files found matching '%s' " +
444 "under directory '%s'") %
445 (pattern, dir))
446 else:
447 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000448
449 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000450 self.debug_print("recursive-exclude %s %s" %
451 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000452 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000453 num = self.exclude_pattern(
454 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000455 if num == 0:
456 template.warn \
457 (("no previously-included files matching '%s' " +
458 "found under directory '%s'") %
459 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000460
461 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000462 self.debug_print("graft " + dir_pattern)
463 files = self.select_pattern(
464 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000465 if not files:
466 template.warn ("no directories found matching '%s'" %
467 dir_pattern)
468 else:
469 self.files.extend (files)
470
471 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000472 self.debug_print("prune " + dir_pattern)
473 num = self.exclude_pattern(
474 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000475 if num == 0:
476 template.warn \
477 (("no previously-included directories found " +
478 "matching '%s'") %
479 dir_pattern)
480 else:
481 raise RuntimeError, \
482 "this cannot happen: invalid action '%s'" % action
483
484 # while loop over lines of template file
485
Greg Warde5b267c2000-05-27 03:03:23 +0000486 # Prune away the build and source distribution directories
Greg Ward4fb29e52000-05-27 17:27:23 +0000487 build = self.get_finalized_command ('build')
Greg Wardf8b9e202000-06-08 00:08:14 +0000488 self.exclude_pattern (self.files, None, prefix=build.build_base)
Greg Warde5b267c2000-05-27 03:03:23 +0000489
490 base_dir = self.distribution.get_fullname()
Greg Wardf8b9e202000-06-08 00:08:14 +0000491 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Warde5b267c2000-05-27 03:03:23 +0000492
Greg Warda82122b2000-02-17 23:56:15 +0000493 # read_template ()
494
495
Greg Wardf8b9e202000-06-08 00:08:14 +0000496 def select_pattern (self, files, pattern, anchor=1, prefix=None):
497 """Select strings (presumably filenames) from 'files' that match
498 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
499 quite the same as implemented by the 'fnmatch' module: '*' and '?'
500 match non-special characters, where "special" is platform-dependent:
501 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
502 Mac OS.
503
504 If 'anchor' is true (the default), then the pattern match is more
505 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
506 'anchor' is false, both of these will match.
507
508 If 'prefix' is supplied, then only filenames starting with 'prefix'
509 (itself a pattern) and ending with 'pattern', with anything in between
510 them, will match. 'anchor' is ignored in this case.
511
512 Return the list of matching strings, possibly empty.
513 """
514 matches = []
515 pattern_re = translate_pattern (pattern, anchor, prefix)
516 self.debug_print("select_pattern: applying regex r'%s'" %
517 pattern_re.pattern)
518 for name in files:
519 if pattern_re.search (name):
520 matches.append (name)
521 self.debug_print(" adding " + name)
522
523 return matches
524
525 # select_pattern ()
526
527
528 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
529 """Remove strings (presumably filenames) from 'files' that match
530 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
531 as for 'select_pattern()', above. The list 'files' is modified
532 in place.
533 """
534 pattern_re = translate_pattern (pattern, anchor, prefix)
535 self.debug_print("exclude_pattern: applying regex r'%s'" %
536 pattern_re.pattern)
537 for i in range (len(files)-1, -1, -1):
538 if pattern_re.search (files[i]):
539 self.debug_print(" removing " + files[i])
540 del files[i]
541
542 # exclude_pattern ()
543
544
Greg Warda82122b2000-02-17 23:56:15 +0000545 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000546 """Write the file list in 'self.files' (presumably as filled in by
547 'find_defaults()' and 'read_template()') to the manifest file named
548 by 'self.manifest'.
549 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000550 self.execute(write_file,
551 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000552 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000553
554 # write_manifest ()
555
556
557 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000558 """Read the manifest file (named by 'self.manifest') and use it to
559 fill in 'self.files', the list of files to include in the source
560 distribution.
561 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000562 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000563 manifest = open (self.manifest)
564 while 1:
565 line = manifest.readline ()
566 if line == '': # end of file
567 break
568 if line[-1] == '\n':
569 line = line[0:-1]
570 self.files.append (line)
571
572 # read_manifest ()
573
574
Greg Warda82122b2000-02-17 23:56:15 +0000575 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000576 """Create the directory tree that will become the source
577 distribution archive. All directories implied by the filenames in
578 'files' are created under 'base_dir', and then we hard link or copy
579 (if hard linking is unavailable) those files into place.
580 Essentially, this duplicates the developer's source tree, but in a
581 directory named after the distribution, containing only the files
582 to be distributed.
583 """
Greg Ward578c10d2000-03-31 02:50:04 +0000584 # Create all the directories under 'base_dir' necessary to
585 # put 'files' there.
586 create_tree (base_dir, files,
587 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000588
589 # And walk over the list of files, either making a hard link (if
590 # os.link exists) to each one that doesn't already exist in its
591 # corresponding location under 'base_dir', or copying each file
592 # that's out-of-date in 'base_dir'. (Usually, all files will be
593 # out-of-date, because by default we blow away 'base_dir' when
594 # we're done making the distribution archives.)
595
Greg Ward578c10d2000-03-31 02:50:04 +0000596 if hasattr (os, 'link'): # can make hard links on this system
597 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000598 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000599 else: # nope, have to copy
600 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000601 msg = "copying files to %s..." % base_dir
602
603 self.announce (msg)
604 for file in files:
605 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000606 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000607
608 # make_release_tree ()
609
610
Greg Warda82122b2000-02-17 23:56:15 +0000611 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000612 """Create the source distribution(s). First, we create the release
613 tree with 'make_release_tree()'; then, we create all required
614 archive files (according to 'self.formats') from the release tree.
615 Finally, we clean up by blowing away the release tree (unless
616 'self.keep_tree' is true). The list of archive files created is
617 stored so it can be retrieved later by 'get_archive_files()'.
618 """
Greg Ward578c10d2000-03-31 02:50:04 +0000619 # Don't warn about missing meta-data here -- should be (and is!)
620 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000621 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000622
623 # Remove any files that match "base_dir" from the fileset -- we
624 # don't want to go distributing the distribution inside itself!
Greg Wardf8b9e202000-06-08 00:08:14 +0000625 self.exclude_pattern (self.files, base_dir + "*")
Greg Warda82122b2000-02-17 23:56:15 +0000626
627 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000628 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000629 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000630 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
631 archive_files.append(file)
632
633 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000634
635 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000636 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000637
Greg Wardd87eb732000-06-01 01:10:56 +0000638 def get_archive_files (self):
639 """Return the list of archive files created when the command
640 was run, or None if the command hasn't run yet.
641 """
642 return self.archive_files
643
Greg Wardfcd974e2000-05-25 01:10:04 +0000644# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000645
646
647# ----------------------------------------------------------------------
648# Utility functions
649
650def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000651 """Find all files under 'dir' and return the list of full filenames
652 (relative to 'dir').
653 """
Greg Warda82122b2000-02-17 23:56:15 +0000654 list = []
655 stack = [dir]
656 pop = stack.pop
657 push = stack.append
658
659 while stack:
660 dir = pop()
661 names = os.listdir (dir)
662
663 for name in names:
664 if dir != os.curdir: # avoid the dreaded "./" syndrome
665 fullname = os.path.join (dir, name)
666 else:
667 fullname = name
668 list.append (fullname)
669 if os.path.isdir (fullname) and not os.path.islink(fullname):
670 push (fullname)
671
672 return list
673
674
Greg Warda82122b2000-02-17 23:56:15 +0000675def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000676 """Translate a shell-like glob pattern to a regular expression; return
677 a string containing the regex. Differs from 'fnmatch.translate()' in
678 that '*' does not match "special characters" (which are
679 platform-specific).
680 """
Greg Warda82122b2000-02-17 23:56:15 +0000681 pattern_re = fnmatch.translate (pattern)
682
683 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
684 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
685 # and by extension they shouldn't match such "special characters" under
686 # any OS. So change all non-escaped dots in the RE to match any
687 # character except the special characters.
688 # XXX currently the "special characters" are just slash -- i.e. this is
689 # Unix-only.
690 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
691 return pattern_re
692
693# glob_to_re ()
694
695
696def translate_pattern (pattern, anchor=1, prefix=None):
697 """Translate a shell-like wildcard pattern to a compiled regular
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000698 expression. Return the compiled regex.
699 """
Greg Warda82122b2000-02-17 23:56:15 +0000700 if pattern:
701 pattern_re = glob_to_re (pattern)
702 else:
703 pattern_re = ''
704
705 if prefix is not None:
706 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
707 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
708 else: # no prefix -- respect anchor flag
709 if anchor:
710 pattern_re = "^" + pattern_re
711
712 return re.compile (pattern_re)
713
714# translate_pattern ()