blob: b06c5156566f6a1312101ef697f3945647c9df98 [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 Wardd8dfb4c2000-05-31 02:32:10 +000014from distutils.util import newer, create_tree, remove_tree, convert_path, \
Greg Ward1b8e1d42000-04-26 01:12:40 +000015 write_file
Greg Ward9d17a7a2000-06-07 03:00:06 +000016from distutils.archive_util import 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 Ward9d17a7a2000-06-07 03:00:06 +000043 # prints all possible arguments to --formats
44 def show_formats():
45 from distutils.fancy_getopt import FancyGetopt
46 list_of_formats=[]
47 for format in ARCHIVE_FORMATS.keys():
48 list_of_formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2]))
49 list_of_formats.sort()
50 pretty_printer=FancyGetopt(list_of_formats)
51 pretty_printer.print_help("List of available distribution formats:")
52
53 help_options = [
54 ('help-formats', None,
55 "lists available distribution formats",show_formats),
56 ]
57
Greg Warda82122b2000-02-17 23:56:15 +000058 negative_opts = {'use-defaults': 'no-defaults'}
59
60 default_format = { 'posix': 'gztar',
61 'nt': 'zip' }
62
63 exclude_re = re.compile (r'\s*!\s*(\S+)') # for manifest lines
64
65
Greg Warde01149c2000-02-18 00:35:22 +000066 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000067 # 'template' and 'manifest' are, respectively, the names of
68 # the manifest template and manifest file.
69 self.template = None
70 self.manifest = None
71
72 # 'use_defaults': if true, we will include the default file set
73 # in the manifest
74 self.use_defaults = 1
75
76 self.manifest_only = 0
77 self.force_manifest = 0
78
79 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000080 self.keep_tree = 0
81
Greg Wardd87eb732000-06-01 01:10:56 +000082 self.archive_files = None
83
Greg Warda82122b2000-02-17 23:56:15 +000084
Greg Warde01149c2000-02-18 00:35:22 +000085 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000086 if self.manifest is None:
87 self.manifest = "MANIFEST"
88 if self.template is None:
89 self.template = "MANIFEST.in"
90
Greg Ward62d5a572000-06-04 15:12:51 +000091 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +000092 if self.formats is None:
93 try:
94 self.formats = [self.default_format[os.name]]
95 except KeyError:
96 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +000097 "don't know how to create source distributions " + \
98 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +000099
Greg Ward6a9a5452000-04-22 03:11:55 +0000100 bad_format = check_archive_formats (self.formats)
101 if bad_format:
102 raise DistutilsOptionError, \
103 "unknown archive format '%s'" % bad_format
104
Greg Warda82122b2000-02-17 23:56:15 +0000105
106 def run (self):
107
108 # 'files' is the list of files that will make up the manifest
109 self.files = []
110
111 # Ensure that all required meta-data is given; warn if not (but
112 # don't die, it's not *that* serious!)
113 self.check_metadata ()
114
115 # Do whatever it takes to get the list of files to process
116 # (process the manifest template, read an existing manifest,
117 # whatever). File list is put into 'self.files'.
118 self.get_file_list ()
119
120 # If user just wanted us to regenerate the manifest, stop now.
121 if self.manifest_only:
122 return
123
124 # Otherwise, go ahead and create the source distribution tarball,
125 # or zipfile, or whatever.
126 self.make_distribution ()
127
128
129 def check_metadata (self):
130
Greg Ward535f2d92000-04-21 04:37:12 +0000131 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000132
133 missing = []
134 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000135 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000136 missing.append (attr)
137
138 if missing:
139 self.warn ("missing required meta-data: " +
140 string.join (missing, ", "))
141
Greg Ward535f2d92000-04-21 04:37:12 +0000142 if metadata.author:
143 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000144 self.warn ("missing meta-data: if 'author' supplied, " +
145 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000146 elif metadata.maintainer:
147 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000148 self.warn ("missing meta-data: if 'maintainer' supplied, " +
149 "'maintainer_email' must be supplied too")
150 else:
151 self.warn ("missing meta-data: either (author and author_email) " +
152 "or (maintainer and maintainer_email) " +
153 "must be supplied")
154
155 # check_metadata ()
156
157
158 def get_file_list (self):
159 """Figure out the list of files to include in the source
160 distribution, and put it in 'self.files'. This might
161 involve reading the manifest template (and writing the
162 manifest), or just reading the manifest, or just using
163 the default file set -- it all depends on the user's
164 options and the state of the filesystem."""
165
166
167 template_exists = os.path.isfile (self.template)
168 if template_exists:
169 template_newer = newer (self.template, self.manifest)
170
171 # Regenerate the manifest if necessary (or if explicitly told to)
172 if ((template_exists and template_newer) or
173 self.force_manifest or
174 self.manifest_only):
175
176 if not template_exists:
177 self.warn (("manifest template '%s' does not exist " +
178 "(using default file list)") %
179 self.template)
180
181 # Add default file set to 'files'
182 if self.use_defaults:
183 self.find_defaults ()
184
185 # Read manifest template if it exists
186 if template_exists:
187 self.read_template ()
188
189 # File list now complete -- sort it so that higher-level files
190 # come first
191 sortable_files = map (os.path.split, self.files)
192 sortable_files.sort ()
193 self.files = []
194 for sort_tuple in sortable_files:
195 self.files.append (apply (os.path.join, sort_tuple))
196
197 # Remove duplicates from the file list
198 for i in range (len(self.files)-1, 0, -1):
199 if self.files[i] == self.files[i-1]:
200 del self.files[i]
201
202 # And write complete file list (including default file set) to
203 # the manifest.
204 self.write_manifest ()
205
206 # Don't regenerate the manifest, just read it in.
207 else:
208 self.read_manifest ()
209
210 # get_file_list ()
211
212
213 def find_defaults (self):
214
215 standards = [('README', 'README.txt'), 'setup.py']
216 for fn in standards:
217 if type (fn) is TupleType:
218 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000219 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000220 for fn in alts:
221 if os.path.exists (fn):
222 got_it = 1
223 self.files.append (fn)
224 break
225
226 if not got_it:
227 self.warn ("standard file not found: should have one of " +
228 string.join (alts, ', '))
229 else:
230 if os.path.exists (fn):
231 self.files.append (fn)
232 else:
233 self.warn ("standard file '%s' not found" % fn)
234
235 optional = ['test/test*.py']
236 for pattern in optional:
237 files = filter (os.path.isfile, glob (pattern))
238 if files:
239 self.files.extend (files)
240
Greg Ward578c10d2000-03-31 02:50:04 +0000241 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000242 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000243 self.files.extend (build_py.get_source_files ())
244
Greg Ward578c10d2000-03-31 02:50:04 +0000245 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000246 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000247 self.files.extend (build_ext.get_source_files ())
248
Greg Ward60908f12000-04-09 03:51:40 +0000249 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000250 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000251 self.files.extend (build_clib.get_source_files ())
252
Greg Warda82122b2000-02-17 23:56:15 +0000253
Greg Warda82122b2000-02-17 23:56:15 +0000254 def search_dir (self, dir, pattern=None):
255 """Recursively find files under 'dir' matching 'pattern' (a string
256 containing a Unix-style glob pattern). If 'pattern' is None,
257 find all files under 'dir'. Return the list of found
258 filenames."""
259
260 allfiles = findall (dir)
261 if pattern is None:
262 return allfiles
263
264 pattern_re = translate_pattern (pattern)
265 files = []
266 for file in allfiles:
267 if pattern_re.match (os.path.basename (file)):
268 files.append (file)
269
270 return files
271
272 # search_dir ()
273
274
Greg Wardf8b9e202000-06-08 00:08:14 +0000275# def exclude_pattern (self, pattern):
276# """Remove filenames from 'self.files' that match 'pattern'."""
277# self.debug_print("exclude_pattern: pattern=%s" % pattern)
278# pattern_re = translate_pattern (pattern)
279# for i in range (len (self.files)-1, -1, -1):
280# if pattern_re.match (self.files[i]):
281# self.debug_print("removing %s" % self.files[i])
282# del self.files[i]
Greg Warda82122b2000-02-17 23:56:15 +0000283
284
285 def recursive_exclude_pattern (self, dir, pattern=None):
286 """Remove filenames from 'self.files' that are under 'dir'
287 and whose basenames match 'pattern'."""
288
Greg Wardf8b9e202000-06-08 00:08:14 +0000289 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
290 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000291 if pattern is None:
292 pattern_re = None
293 else:
294 pattern_re = translate_pattern (pattern)
295
296 for i in range (len (self.files)-1, -1, -1):
297 (cur_dir, cur_base) = os.path.split (self.files[i])
298 if (cur_dir == dir and
299 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000300 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000301 del self.files[i]
302
303
304 def read_template (self):
305 """Read and parse the manifest template file named by
306 'self.template' (usually "MANIFEST.in"). Process all file
307 specifications (include and exclude) in the manifest template
308 and add the resulting filenames to 'self.files'."""
309
310 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000311 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000312
313 template = TextFile (self.template,
314 strip_comments=1,
315 skip_blanks=1,
316 join_lines=1,
317 lstrip_ws=1,
318 rstrip_ws=1,
319 collapse_ws=1)
320
321 all_files = findall ()
322
323 while 1:
324
325 line = template.readline()
326 if line is None: # end of file
327 break
328
329 words = string.split (line)
330 action = words[0]
331
332 # First, check that the right number of words are present
333 # for the given action (which is the first word)
334 if action in ('include','exclude',
335 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000336 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000337 template.warn \
338 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000339 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000340 action)
341 continue
342
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000343 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000344
345 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000346 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000347 template.warn \
348 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000349 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000350 action)
351 continue
352
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000353 dir = convert_path(words[1])
354 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000355
356 elif action in ('graft','prune'):
357 if len (words) != 2:
358 template.warn \
359 ("invalid manifest template line: " +
360 "'%s' expects a single <dir_pattern>" %
361 action)
362 continue
363
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000364 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000365
366 else:
367 template.warn ("invalid manifest template line: " +
368 "unknown action '%s'" % action)
369 continue
370
371 # OK, now we know that the action is valid and we have the
372 # right number of words on the line for that action -- so we
373 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000374 # defined either (pattern), (dir and pattern), or
375 # (dir_pattern) -- so we don't have to spend any time
376 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000377
378 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000379 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000380 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000381 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000382 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000383 template.warn ("no files found matching '%s'" %
384 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000385 else:
386 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000387
388 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000389 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000390 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000391 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000392 if num == 0:
393 template.warn (
394 "no previously-included files found matching '%s'"%
395 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000396
397 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000398 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000399 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000400 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000401 if not files:
402 template.warn (("no files found matching '%s' " +
403 "anywhere in distribution") %
404 pattern)
405 else:
406 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000407
408 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000409 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000410 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000411 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000412 if num == 0:
413 template.warn \
414 (("no previously-included files matching '%s' " +
415 "found anywhere in distribution") %
416 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000417
418 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000419 self.debug_print("recursive-include %s %s" %
420 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000421 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000422 files = self.select_pattern (
423 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000424 if not files:
425 template.warn (("no files found matching '%s' " +
426 "under directory '%s'") %
427 (pattern, dir))
428 else:
429 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000430
431 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000432 self.debug_print("recursive-exclude %s %s" %
433 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000434 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000435 num = self.exclude_pattern(
436 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000437 if num == 0:
438 template.warn \
439 (("no previously-included files matching '%s' " +
440 "found under directory '%s'") %
441 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000442
443 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000444 self.debug_print("graft " + dir_pattern)
445 files = self.select_pattern(
446 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000447 if not files:
448 template.warn ("no directories found matching '%s'" %
449 dir_pattern)
450 else:
451 self.files.extend (files)
452
453 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000454 self.debug_print("prune " + dir_pattern)
455 num = self.exclude_pattern(
456 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000457 if num == 0:
458 template.warn \
459 (("no previously-included directories found " +
460 "matching '%s'") %
461 dir_pattern)
462 else:
463 raise RuntimeError, \
464 "this cannot happen: invalid action '%s'" % action
465
466 # while loop over lines of template file
467
Greg Warde5b267c2000-05-27 03:03:23 +0000468 # Prune away the build and source distribution directories
Greg Ward4fb29e52000-05-27 17:27:23 +0000469 build = self.get_finalized_command ('build')
Greg Wardf8b9e202000-06-08 00:08:14 +0000470 self.exclude_pattern (self.files, None, prefix=build.build_base)
Greg Warde5b267c2000-05-27 03:03:23 +0000471
472 base_dir = self.distribution.get_fullname()
Greg Wardf8b9e202000-06-08 00:08:14 +0000473 self.exclude_pattern (self.files, None, prefix=base_dir)
Greg Warde5b267c2000-05-27 03:03:23 +0000474
Greg Warda82122b2000-02-17 23:56:15 +0000475 # read_template ()
476
477
Greg Wardf8b9e202000-06-08 00:08:14 +0000478 def select_pattern (self, files, pattern, anchor=1, prefix=None):
479 """Select strings (presumably filenames) from 'files' that match
480 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
481 quite the same as implemented by the 'fnmatch' module: '*' and '?'
482 match non-special characters, where "special" is platform-dependent:
483 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
484 Mac OS.
485
486 If 'anchor' is true (the default), then the pattern match is more
487 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
488 'anchor' is false, both of these will match.
489
490 If 'prefix' is supplied, then only filenames starting with 'prefix'
491 (itself a pattern) and ending with 'pattern', with anything in between
492 them, will match. 'anchor' is ignored in this case.
493
494 Return the list of matching strings, possibly empty.
495 """
496 matches = []
497 pattern_re = translate_pattern (pattern, anchor, prefix)
498 self.debug_print("select_pattern: applying regex r'%s'" %
499 pattern_re.pattern)
500 for name in files:
501 if pattern_re.search (name):
502 matches.append (name)
503 self.debug_print(" adding " + name)
504
505 return matches
506
507 # select_pattern ()
508
509
510 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
511 """Remove strings (presumably filenames) from 'files' that match
512 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
513 as for 'select_pattern()', above. The list 'files' is modified
514 in place.
515 """
516 pattern_re = translate_pattern (pattern, anchor, prefix)
517 self.debug_print("exclude_pattern: applying regex r'%s'" %
518 pattern_re.pattern)
519 for i in range (len(files)-1, -1, -1):
520 if pattern_re.search (files[i]):
521 self.debug_print(" removing " + files[i])
522 del files[i]
523
524 # exclude_pattern ()
525
526
Greg Warda82122b2000-02-17 23:56:15 +0000527 def write_manifest (self):
528 """Write the file list in 'self.files' (presumably as filled in
529 by 'find_defaults()' and 'read_template()') to the manifest file
530 named by 'self.manifest'."""
531
Greg Ward1b8e1d42000-04-26 01:12:40 +0000532 self.execute(write_file,
533 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000534 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000535
536 # write_manifest ()
537
538
539 def read_manifest (self):
540 """Read the manifest file (named by 'self.manifest') and use
541 it to fill in 'self.files', the list of files to include
542 in the source distribution."""
543
Greg Wardf8b9e202000-06-08 00:08:14 +0000544 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000545 manifest = open (self.manifest)
546 while 1:
547 line = manifest.readline ()
548 if line == '': # end of file
549 break
550 if line[-1] == '\n':
551 line = line[0:-1]
552 self.files.append (line)
553
554 # read_manifest ()
555
556
Greg Warda82122b2000-02-17 23:56:15 +0000557 def make_release_tree (self, base_dir, files):
558
Greg Ward578c10d2000-03-31 02:50:04 +0000559 # Create all the directories under 'base_dir' necessary to
560 # put 'files' there.
561 create_tree (base_dir, files,
562 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000563
564 # And walk over the list of files, either making a hard link (if
565 # os.link exists) to each one that doesn't already exist in its
566 # corresponding location under 'base_dir', or copying each file
567 # that's out-of-date in 'base_dir'. (Usually, all files will be
568 # out-of-date, because by default we blow away 'base_dir' when
569 # we're done making the distribution archives.)
570
Greg Ward578c10d2000-03-31 02:50:04 +0000571 if hasattr (os, 'link'): # can make hard links on this system
572 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000573 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000574 else: # nope, have to copy
575 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000576 msg = "copying files to %s..." % base_dir
577
578 self.announce (msg)
579 for file in files:
580 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000581 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000582
583 # make_release_tree ()
584
585
Greg Warda82122b2000-02-17 23:56:15 +0000586 def make_distribution (self):
587
Greg Ward578c10d2000-03-31 02:50:04 +0000588 # Don't warn about missing meta-data here -- should be (and is!)
589 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000590 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000591
592 # Remove any files that match "base_dir" from the fileset -- we
593 # don't want to go distributing the distribution inside itself!
Greg Wardf8b9e202000-06-08 00:08:14 +0000594 self.exclude_pattern (self.files, base_dir + "*")
Greg Warda82122b2000-02-17 23:56:15 +0000595
596 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000597 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000598 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000599 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
600 archive_files.append(file)
601
602 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000603
604 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000605 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000606
Greg Wardd87eb732000-06-01 01:10:56 +0000607 def get_archive_files (self):
608 """Return the list of archive files created when the command
609 was run, or None if the command hasn't run yet.
610 """
611 return self.archive_files
612
Greg Wardfcd974e2000-05-25 01:10:04 +0000613# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000614
615
616# ----------------------------------------------------------------------
617# Utility functions
618
619def findall (dir = os.curdir):
620 """Find all files under 'dir' and return the list of full
621 filenames (relative to 'dir')."""
622
623 list = []
624 stack = [dir]
625 pop = stack.pop
626 push = stack.append
627
628 while stack:
629 dir = pop()
630 names = os.listdir (dir)
631
632 for name in names:
633 if dir != os.curdir: # avoid the dreaded "./" syndrome
634 fullname = os.path.join (dir, name)
635 else:
636 fullname = name
637 list.append (fullname)
638 if os.path.isdir (fullname) and not os.path.islink(fullname):
639 push (fullname)
640
641 return list
642
643
Greg Warda82122b2000-02-17 23:56:15 +0000644def glob_to_re (pattern):
645 """Translate a shell-like glob pattern to a regular expression;
646 return a string containing the regex. Differs from
647 'fnmatch.translate()' in that '*' does not match "special
648 characters" (which are platform-specific)."""
649 pattern_re = fnmatch.translate (pattern)
650
651 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
652 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
653 # and by extension they shouldn't match such "special characters" under
654 # any OS. So change all non-escaped dots in the RE to match any
655 # character except the special characters.
656 # XXX currently the "special characters" are just slash -- i.e. this is
657 # Unix-only.
658 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
659 return pattern_re
660
661# glob_to_re ()
662
663
664def translate_pattern (pattern, anchor=1, prefix=None):
665 """Translate a shell-like wildcard pattern to a compiled regular
666 expression. Return the compiled regex."""
667
668 if pattern:
669 pattern_re = glob_to_re (pattern)
670 else:
671 pattern_re = ''
672
673 if prefix is not None:
674 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
675 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
676 else: # no prefix -- respect anchor flag
677 if anchor:
678 pattern_re = "^" + pattern_re
679
680 return re.compile (pattern_re)
681
682# translate_pattern ()