blob: cd7f258b535546fd90ab4a2c6e94578da7bdc4ff [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 Wardbbeceea2000-02-18 00:25:39 +000034 "just regenerate the manifest and then stop"),
Greg Ward839d5322000-04-26 01:14:33 +000035 ('force-manifest', 'f',
Greg Wardbbeceea2000-02-18 00:25:39 +000036 "forcibly regenerate the manifest and carry on as usual"),
Greg Wardbbeceea2000-02-18 00:25:39 +000037 ('formats=', None,
Greg Ward9d17a7a2000-06-07 03:00:06 +000038 "formats for source distribution"),
Greg Wardbbeceea2000-02-18 00:25:39 +000039 ('keep-tree', 'k',
40 "keep the distribution tree around after creating " +
41 "archive file(s)"),
42 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000043
44
45 # XXX ugh: this has to precede the 'help_options' list, because
46 # it is mentioned there -- also, this is not a method, even though
47 # it's defined in a class: double-ugh!
48 def show_formats ():
49 """Print all possible values for the 'formats' option -- used by
50 the "--help-formats" command-line option.
51 """
Greg Ward9d17a7a2000-06-07 03:00:06 +000052 from distutils.fancy_getopt import FancyGetopt
Greg Wardf1fe1032000-06-08 00:14:18 +000053 formats=[]
Greg Ward9d17a7a2000-06-07 03:00:06 +000054 for format in ARCHIVE_FORMATS.keys():
Greg Wardf1fe1032000-06-08 00:14:18 +000055 formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2]))
56 formats.sort()
57 pretty_printer = FancyGetopt(formats)
58 pretty_printer.print_help(
59 "List of available source distribution formats:")
Greg Ward9d17a7a2000-06-07 03:00:06 +000060
61 help_options = [
62 ('help-formats', None,
Greg Wardf1fe1032000-06-08 00:14:18 +000063 "lists available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000064 ]
65
Greg Warda82122b2000-02-17 23:56:15 +000066 negative_opts = {'use-defaults': 'no-defaults'}
67
68 default_format = { 'posix': 'gztar',
69 'nt': 'zip' }
70
Greg Warda82122b2000-02-17 23:56:15 +000071
Greg Warde01149c2000-02-18 00:35:22 +000072 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000073 # 'template' and 'manifest' are, respectively, the names of
74 # the manifest template and manifest file.
75 self.template = None
76 self.manifest = None
77
78 # 'use_defaults': if true, we will include the default file set
79 # in the manifest
80 self.use_defaults = 1
81
82 self.manifest_only = 0
83 self.force_manifest = 0
84
85 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000086 self.keep_tree = 0
87
Greg Wardd87eb732000-06-01 01:10:56 +000088 self.archive_files = None
89
Greg Warda82122b2000-02-17 23:56:15 +000090
Greg Warde01149c2000-02-18 00:35:22 +000091 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000092 if self.manifest is None:
93 self.manifest = "MANIFEST"
94 if self.template is None:
95 self.template = "MANIFEST.in"
96
Greg Ward62d5a572000-06-04 15:12:51 +000097 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +000098 if self.formats is None:
99 try:
100 self.formats = [self.default_format[os.name]]
101 except KeyError:
102 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +0000103 "don't know how to create source distributions " + \
104 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +0000105
Greg Ward6a9a5452000-04-22 03:11:55 +0000106 bad_format = check_archive_formats (self.formats)
107 if bad_format:
108 raise DistutilsOptionError, \
109 "unknown archive format '%s'" % bad_format
110
Greg Warda82122b2000-02-17 23:56:15 +0000111
112 def run (self):
113
114 # 'files' is the list of files that will make up the manifest
115 self.files = []
116
117 # Ensure that all required meta-data is given; warn if not (but
118 # don't die, it's not *that* serious!)
119 self.check_metadata ()
120
121 # Do whatever it takes to get the list of files to process
122 # (process the manifest template, read an existing manifest,
123 # whatever). File list is put into 'self.files'.
124 self.get_file_list ()
125
126 # If user just wanted us to regenerate the manifest, stop now.
127 if self.manifest_only:
128 return
129
130 # Otherwise, go ahead and create the source distribution tarball,
131 # or zipfile, or whatever.
132 self.make_distribution ()
133
134
135 def check_metadata (self):
136
Greg Ward535f2d92000-04-21 04:37:12 +0000137 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000138
139 missing = []
140 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000141 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000142 missing.append (attr)
143
144 if missing:
145 self.warn ("missing required meta-data: " +
146 string.join (missing, ", "))
147
Greg Ward535f2d92000-04-21 04:37:12 +0000148 if metadata.author:
149 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000150 self.warn ("missing meta-data: if 'author' supplied, " +
151 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000152 elif metadata.maintainer:
153 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000154 self.warn ("missing meta-data: if 'maintainer' supplied, " +
155 "'maintainer_email' must be supplied too")
156 else:
157 self.warn ("missing meta-data: either (author and author_email) " +
158 "or (maintainer and maintainer_email) " +
159 "must be supplied")
160
161 # check_metadata ()
162
163
164 def get_file_list (self):
165 """Figure out the list of files to include in the source
Greg Warde0c8c2f2000-06-08 00:24:01 +0000166 distribution, and put it in 'self.files'. This might involve
167 reading the manifest template (and writing the manifest), or just
168 reading the manifest, or just using the default file set -- it all
169 depends on the user's options and the state of the filesystem.
170 """
Greg Warda82122b2000-02-17 23:56:15 +0000171 template_exists = os.path.isfile (self.template)
172 if template_exists:
173 template_newer = newer (self.template, self.manifest)
174
175 # Regenerate the manifest if necessary (or if explicitly told to)
176 if ((template_exists and template_newer) or
177 self.force_manifest or
178 self.manifest_only):
179
180 if not template_exists:
181 self.warn (("manifest template '%s' does not exist " +
182 "(using default file list)") %
183 self.template)
184
185 # Add default file set to 'files'
186 if self.use_defaults:
187 self.find_defaults ()
188
189 # Read manifest template if it exists
190 if template_exists:
191 self.read_template ()
192
193 # File list now complete -- sort it so that higher-level files
194 # come first
195 sortable_files = map (os.path.split, self.files)
196 sortable_files.sort ()
197 self.files = []
198 for sort_tuple in sortable_files:
199 self.files.append (apply (os.path.join, sort_tuple))
200
201 # Remove duplicates from the file list
202 for i in range (len(self.files)-1, 0, -1):
203 if self.files[i] == self.files[i-1]:
204 del self.files[i]
205
206 # And write complete file list (including default file set) to
207 # the manifest.
208 self.write_manifest ()
209
210 # Don't regenerate the manifest, just read it in.
211 else:
212 self.read_manifest ()
213
214 # get_file_list ()
215
216
217 def find_defaults (self):
218
219 standards = [('README', 'README.txt'), 'setup.py']
220 for fn in standards:
221 if type (fn) is TupleType:
222 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000223 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000224 for fn in alts:
225 if os.path.exists (fn):
226 got_it = 1
227 self.files.append (fn)
228 break
229
230 if not got_it:
231 self.warn ("standard file not found: should have one of " +
232 string.join (alts, ', '))
233 else:
234 if os.path.exists (fn):
235 self.files.append (fn)
236 else:
237 self.warn ("standard file '%s' not found" % fn)
238
239 optional = ['test/test*.py']
240 for pattern in optional:
241 files = filter (os.path.isfile, glob (pattern))
242 if files:
243 self.files.extend (files)
244
Greg Ward578c10d2000-03-31 02:50:04 +0000245 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000246 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000247 self.files.extend (build_py.get_source_files ())
248
Greg Ward578c10d2000-03-31 02:50:04 +0000249 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000250 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000251 self.files.extend (build_ext.get_source_files ())
252
Greg Ward60908f12000-04-09 03:51:40 +0000253 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000254 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000255 self.files.extend (build_clib.get_source_files ())
256
Greg Warda82122b2000-02-17 23:56:15 +0000257
Greg Warda82122b2000-02-17 23:56:15 +0000258 def search_dir (self, dir, pattern=None):
259 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000260 containing a Unix-style glob pattern). If 'pattern' is None, find
261 all files under 'dir'. Return the list of found filenames.
262 """
Greg Warda82122b2000-02-17 23:56:15 +0000263 allfiles = findall (dir)
264 if pattern is None:
265 return allfiles
266
267 pattern_re = translate_pattern (pattern)
268 files = []
269 for file in allfiles:
270 if pattern_re.match (os.path.basename (file)):
271 files.append (file)
272
273 return files
274
275 # search_dir ()
276
277
Greg Wardf8b9e202000-06-08 00:08:14 +0000278# def exclude_pattern (self, pattern):
279# """Remove filenames from 'self.files' that match 'pattern'."""
280# self.debug_print("exclude_pattern: pattern=%s" % pattern)
281# pattern_re = translate_pattern (pattern)
282# for i in range (len (self.files)-1, -1, -1):
283# if pattern_re.match (self.files[i]):
284# self.debug_print("removing %s" % self.files[i])
285# del self.files[i]
Greg Warda82122b2000-02-17 23:56:15 +0000286
287
288 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000289 """Remove filenames from 'self.files' that are under 'dir' and
290 whose basenames match 'pattern'.
291 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000292 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
293 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000294 if pattern is None:
295 pattern_re = None
296 else:
297 pattern_re = translate_pattern (pattern)
298
299 for i in range (len (self.files)-1, -1, -1):
300 (cur_dir, cur_base) = os.path.split (self.files[i])
301 if (cur_dir == dir and
302 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000303 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000304 del self.files[i]
305
306
307 def read_template (self):
308 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000309 'self.template' (usually "MANIFEST.in"). Process all file
310 specifications (include and exclude) in the manifest template and
311 add the resulting filenames to 'self.files'.
312 """
Greg Warda82122b2000-02-17 23:56:15 +0000313 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000314 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000315
316 template = TextFile (self.template,
317 strip_comments=1,
318 skip_blanks=1,
319 join_lines=1,
320 lstrip_ws=1,
321 rstrip_ws=1,
322 collapse_ws=1)
323
324 all_files = findall ()
325
326 while 1:
327
328 line = template.readline()
329 if line is None: # end of file
330 break
331
332 words = string.split (line)
333 action = words[0]
334
335 # First, check that the right number of words are present
336 # for the given action (which is the first word)
337 if action in ('include','exclude',
338 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000339 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000340 template.warn \
341 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000342 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000343 action)
344 continue
345
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000346 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000347
348 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000349 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000350 template.warn \
351 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000352 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000353 action)
354 continue
355
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000356 dir = convert_path(words[1])
357 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000358
359 elif action in ('graft','prune'):
360 if len (words) != 2:
361 template.warn \
362 ("invalid manifest template line: " +
363 "'%s' expects a single <dir_pattern>" %
364 action)
365 continue
366
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000367 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000368
369 else:
370 template.warn ("invalid manifest template line: " +
371 "unknown action '%s'" % action)
372 continue
373
374 # OK, now we know that the action is valid and we have the
375 # right number of words on the line for that action -- so we
376 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000377 # defined either (pattern), (dir and pattern), or
378 # (dir_pattern) -- so we don't have to spend any time
379 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000380
381 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000382 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000383 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000384 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000385 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000386 template.warn ("no files found matching '%s'" %
387 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000388 else:
389 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000390
391 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000392 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000393 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000394 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000395 if num == 0:
396 template.warn (
397 "no previously-included files found matching '%s'"%
398 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000399
400 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000401 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000402 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000403 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000404 if not files:
405 template.warn (("no files found matching '%s' " +
406 "anywhere in distribution") %
407 pattern)
408 else:
409 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000410
411 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000412 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000413 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000414 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000415 if num == 0:
416 template.warn \
417 (("no previously-included files matching '%s' " +
418 "found anywhere in distribution") %
419 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000420
421 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000422 self.debug_print("recursive-include %s %s" %
423 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000424 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000425 files = self.select_pattern (
426 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000427 if not files:
428 template.warn (("no files found matching '%s' " +
429 "under directory '%s'") %
430 (pattern, dir))
431 else:
432 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000433
434 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000435 self.debug_print("recursive-exclude %s %s" %
436 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000437 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000438 num = self.exclude_pattern(
439 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000440 if num == 0:
441 template.warn \
442 (("no previously-included files matching '%s' " +
443 "found under directory '%s'") %
444 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000445
446 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000447 self.debug_print("graft " + dir_pattern)
448 files = self.select_pattern(
449 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000450 if not files:
451 template.warn ("no directories found matching '%s'" %
452 dir_pattern)
453 else:
454 self.files.extend (files)
455
456 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000457 self.debug_print("prune " + dir_pattern)
458 num = self.exclude_pattern(
459 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000460 if num == 0:
461 template.warn \
462 (("no previously-included directories found " +
463 "matching '%s'") %
464 dir_pattern)
465 else:
466 raise RuntimeError, \
467 "this cannot happen: invalid action '%s'" % action
468
469 # while loop over lines of template file
470
Greg Warde5b267c2000-05-27 03:03:23 +0000471 # Prune away the build and source distribution directories
Greg Ward4fb29e52000-05-27 17:27:23 +0000472 build = self.get_finalized_command ('build')
Greg Wardf8b9e202000-06-08 00:08:14 +0000473 self.exclude_pattern (self.files, None, prefix=build.build_base)
Greg Warde5b267c2000-05-27 03:03:23 +0000474
475 base_dir = self.distribution.get_fullname()
Greg Wardf8b9e202000-06-08 00:08:14 +0000476 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Warde5b267c2000-05-27 03:03:23 +0000477
Greg Warda82122b2000-02-17 23:56:15 +0000478 # read_template ()
479
480
Greg Wardf8b9e202000-06-08 00:08:14 +0000481 def select_pattern (self, files, pattern, anchor=1, prefix=None):
482 """Select strings (presumably filenames) from 'files' that match
483 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
484 quite the same as implemented by the 'fnmatch' module: '*' and '?'
485 match non-special characters, where "special" is platform-dependent:
486 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
487 Mac OS.
488
489 If 'anchor' is true (the default), then the pattern match is more
490 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
491 'anchor' is false, both of these will match.
492
493 If 'prefix' is supplied, then only filenames starting with 'prefix'
494 (itself a pattern) and ending with 'pattern', with anything in between
495 them, will match. 'anchor' is ignored in this case.
496
497 Return the list of matching strings, possibly empty.
498 """
499 matches = []
500 pattern_re = translate_pattern (pattern, anchor, prefix)
501 self.debug_print("select_pattern: applying regex r'%s'" %
502 pattern_re.pattern)
503 for name in files:
504 if pattern_re.search (name):
505 matches.append (name)
506 self.debug_print(" adding " + name)
507
508 return matches
509
510 # select_pattern ()
511
512
513 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
514 """Remove strings (presumably filenames) from 'files' that match
515 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
516 as for 'select_pattern()', above. The list 'files' is modified
517 in place.
518 """
519 pattern_re = translate_pattern (pattern, anchor, prefix)
520 self.debug_print("exclude_pattern: applying regex r'%s'" %
521 pattern_re.pattern)
522 for i in range (len(files)-1, -1, -1):
523 if pattern_re.search (files[i]):
524 self.debug_print(" removing " + files[i])
525 del files[i]
526
527 # exclude_pattern ()
528
529
Greg Warda82122b2000-02-17 23:56:15 +0000530 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000531 """Write the file list in 'self.files' (presumably as filled in by
532 'find_defaults()' and 'read_template()') to the manifest file named
533 by 'self.manifest'.
534 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000535 self.execute(write_file,
536 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000537 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000538
539 # write_manifest ()
540
541
542 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000543 """Read the manifest file (named by 'self.manifest') and use it to
544 fill in 'self.files', the list of files to include in the source
545 distribution.
546 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000547 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000548 manifest = open (self.manifest)
549 while 1:
550 line = manifest.readline ()
551 if line == '': # end of file
552 break
553 if line[-1] == '\n':
554 line = line[0:-1]
555 self.files.append (line)
556
557 # read_manifest ()
558
559
Greg Warda82122b2000-02-17 23:56:15 +0000560 def make_release_tree (self, base_dir, files):
561
Greg Ward578c10d2000-03-31 02:50:04 +0000562 # Create all the directories under 'base_dir' necessary to
563 # put 'files' there.
564 create_tree (base_dir, files,
565 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000566
567 # And walk over the list of files, either making a hard link (if
568 # os.link exists) to each one that doesn't already exist in its
569 # corresponding location under 'base_dir', or copying each file
570 # that's out-of-date in 'base_dir'. (Usually, all files will be
571 # out-of-date, because by default we blow away 'base_dir' when
572 # we're done making the distribution archives.)
573
Greg Ward578c10d2000-03-31 02:50:04 +0000574 if hasattr (os, 'link'): # can make hard links on this system
575 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000576 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000577 else: # nope, have to copy
578 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000579 msg = "copying files to %s..." % base_dir
580
581 self.announce (msg)
582 for file in files:
583 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000584 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000585
586 # make_release_tree ()
587
588
Greg Warda82122b2000-02-17 23:56:15 +0000589 def make_distribution (self):
590
Greg Ward578c10d2000-03-31 02:50:04 +0000591 # Don't warn about missing meta-data here -- should be (and is!)
592 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000593 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000594
595 # Remove any files that match "base_dir" from the fileset -- we
596 # don't want to go distributing the distribution inside itself!
Greg Wardf8b9e202000-06-08 00:08:14 +0000597 self.exclude_pattern (self.files, base_dir + "*")
Greg Warda82122b2000-02-17 23:56:15 +0000598
599 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000600 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000601 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000602 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
603 archive_files.append(file)
604
605 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000606
607 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000608 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000609
Greg Wardd87eb732000-06-01 01:10:56 +0000610 def get_archive_files (self):
611 """Return the list of archive files created when the command
612 was run, or None if the command hasn't run yet.
613 """
614 return self.archive_files
615
Greg Wardfcd974e2000-05-25 01:10:04 +0000616# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000617
618
619# ----------------------------------------------------------------------
620# Utility functions
621
622def findall (dir = os.curdir):
623 """Find all files under 'dir' and return the list of full
624 filenames (relative to 'dir')."""
625
626 list = []
627 stack = [dir]
628 pop = stack.pop
629 push = stack.append
630
631 while stack:
632 dir = pop()
633 names = os.listdir (dir)
634
635 for name in names:
636 if dir != os.curdir: # avoid the dreaded "./" syndrome
637 fullname = os.path.join (dir, name)
638 else:
639 fullname = name
640 list.append (fullname)
641 if os.path.isdir (fullname) and not os.path.islink(fullname):
642 push (fullname)
643
644 return list
645
646
Greg Warda82122b2000-02-17 23:56:15 +0000647def glob_to_re (pattern):
648 """Translate a shell-like glob pattern to a regular expression;
649 return a string containing the regex. Differs from
650 'fnmatch.translate()' in that '*' does not match "special
651 characters" (which are platform-specific)."""
652 pattern_re = fnmatch.translate (pattern)
653
654 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
655 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
656 # and by extension they shouldn't match such "special characters" under
657 # any OS. So change all non-escaped dots in the RE to match any
658 # character except the special characters.
659 # XXX currently the "special characters" are just slash -- i.e. this is
660 # Unix-only.
661 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
662 return pattern_re
663
664# glob_to_re ()
665
666
667def translate_pattern (pattern, anchor=1, prefix=None):
668 """Translate a shell-like wildcard pattern to a compiled regular
669 expression. Return the compiled regex."""
670
671 if pattern:
672 pattern_re = glob_to_re (pattern)
673 else:
674 pattern_re = ''
675
676 if prefix is not None:
677 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
678 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
679 else: # no prefix -- respect anchor flag
680 if anchor:
681 pattern_re = "^" + pattern_re
682
683 return re.compile (pattern_re)
684
685# translate_pattern ()