blob: 5627ebb9e5fce1ac2627eba1d79d35d19fbf15be [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, \
Greg Ward34593812000-06-24 01:23:37 +000016 check_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 Ward34593812000-06-24 01:23:37 +000021def show_formats ():
22 """Print all possible values for the 'formats' option (used by
23 the "--help-formats" command-line option).
24 """
25 from distutils.fancy_getopt import FancyGetopt
26 from distutils.archive_util import ARCHIVE_FORMATS
27 formats=[]
28 for format in ARCHIVE_FORMATS.keys():
29 formats.append(("formats=" + format, None,
30 ARCHIVE_FORMATS[format][2]))
31 formats.sort()
32 pretty_printer = FancyGetopt(formats)
33 pretty_printer.print_help(
34 "List of available source distribution formats:")
35
36
Greg Ward1993f9a2000-02-18 00:13:53 +000037class sdist (Command):
Greg Warda82122b2000-02-17 23:56:15 +000038
39 description = "create a source distribution (tarball, zip file, etc.)"
40
Greg Wardbbeceea2000-02-18 00:25:39 +000041 user_options = [
42 ('template=', 't',
43 "name of manifest template file [default: MANIFEST.in]"),
44 ('manifest=', 'm',
45 "name of manifest file [default: MANIFEST]"),
46 ('use-defaults', None,
47 "include the default file set in the manifest "
48 "[default; disable with --no-defaults]"),
Greg Ward839d5322000-04-26 01:14:33 +000049 ('manifest-only', 'o',
Greg Wardc3c8c6e2000-06-08 00:46:45 +000050 "just regenerate the manifest and then stop "
51 "(implies --force-manifest)"),
Greg Ward839d5322000-04-26 01:14:33 +000052 ('force-manifest', 'f',
Greg Wardbbeceea2000-02-18 00:25:39 +000053 "forcibly regenerate the manifest and carry on as usual"),
Greg Wardbbeceea2000-02-18 00:25:39 +000054 ('formats=', None,
Greg Ward2ff78872000-06-24 00:23:20 +000055 "formats for source distribution (comma-separated list)"),
Greg Wardbbeceea2000-02-18 00:25:39 +000056 ('keep-tree', 'k',
57 "keep the distribution tree around after creating " +
58 "archive file(s)"),
59 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000060
61
Greg Ward9d17a7a2000-06-07 03:00:06 +000062 help_options = [
63 ('help-formats', None,
Greg Ward2ff78872000-06-24 00:23:20 +000064 "list available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000065 ]
66
Greg Warda82122b2000-02-17 23:56:15 +000067 negative_opts = {'use-defaults': 'no-defaults'}
68
69 default_format = { 'posix': 'gztar',
70 'nt': 'zip' }
71
Greg 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):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000136 """Ensure that all required elements of meta-data (name, version,
137 URL, (author and author_email) or (maintainer and
138 maintainer_email)) are supplied by the Distribution object; warn if
139 any are missing.
140 """
Greg Ward535f2d92000-04-21 04:37:12 +0000141 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000142
143 missing = []
144 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000145 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000146 missing.append (attr)
147
148 if missing:
149 self.warn ("missing required meta-data: " +
150 string.join (missing, ", "))
151
Greg Ward535f2d92000-04-21 04:37:12 +0000152 if metadata.author:
153 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000154 self.warn ("missing meta-data: if 'author' supplied, " +
155 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000156 elif metadata.maintainer:
157 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000158 self.warn ("missing meta-data: if 'maintainer' supplied, " +
159 "'maintainer_email' must be supplied too")
160 else:
161 self.warn ("missing meta-data: either (author and author_email) " +
162 "or (maintainer and maintainer_email) " +
163 "must be supplied")
164
165 # check_metadata ()
166
167
168 def get_file_list (self):
169 """Figure out the list of files to include in the source
Greg Warde0c8c2f2000-06-08 00:24:01 +0000170 distribution, and put it in 'self.files'. This might involve
171 reading the manifest template (and writing the manifest), or just
172 reading the manifest, or just using the default file set -- it all
173 depends on the user's options and the state of the filesystem.
174 """
Greg Wardb2db0eb2000-06-21 03:29:57 +0000175
176 # If we have a manifest template, see if it's newer than the
177 # manifest; if so, we'll regenerate the manifest.
Greg Warda82122b2000-02-17 23:56:15 +0000178 template_exists = os.path.isfile (self.template)
179 if template_exists:
180 template_newer = newer (self.template, self.manifest)
181
Greg Wardb2db0eb2000-06-21 03:29:57 +0000182 # The contents of the manifest file almost certainly depend on the
183 # setup script as well as the manifest template -- so if the setup
184 # script is newer than the manifest, we'll regenerate the manifest
185 # from the template. (Well, not quite: if we already have a
186 # manifest, but there's no template -- which will happen if the
187 # developer elects to generate a manifest some other way -- then we
188 # can't regenerate the manifest, so we don't.)
189 setup_newer = newer(sys.argv[0], self.manifest)
190
191 # cases:
192 # 1) no manifest, template exists: generate manifest
193 # (covered by 2a: no manifest == template newer)
194 # 2) manifest & template exist:
195 # 2a) template or setup script newer than manifest:
196 # regenerate manifest
197 # 2b) manifest newer than both:
198 # do nothing (unless --force or --manifest-only)
199 # 3) manifest exists, no template:
200 # do nothing (unless --force or --manifest-only)
201 # 4) no manifest, no template: generate w/ warning ("defaults only")
202
Greg Warda82122b2000-02-17 23:56:15 +0000203 # Regenerate the manifest if necessary (or if explicitly told to)
Greg Wardb2db0eb2000-06-21 03:29:57 +0000204 if ((template_exists and (template_newer or setup_newer)) or
205 self.force_manifest or self.manifest_only):
Greg Warda82122b2000-02-17 23:56:15 +0000206
207 if not template_exists:
208 self.warn (("manifest template '%s' does not exist " +
209 "(using default file list)") %
210 self.template)
211
212 # Add default file set to 'files'
213 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000214 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000215
216 # Read manifest template if it exists
217 if template_exists:
218 self.read_template ()
219
Greg Wardce15c6c2000-06-08 01:06:02 +0000220 # Prune away the build and source distribution directories
221 self.prune_file_list()
222
Greg Warda82122b2000-02-17 23:56:15 +0000223 # File list now complete -- sort it so that higher-level files
224 # come first
225 sortable_files = map (os.path.split, self.files)
226 sortable_files.sort ()
227 self.files = []
228 for sort_tuple in sortable_files:
229 self.files.append (apply (os.path.join, sort_tuple))
230
231 # Remove duplicates from the file list
232 for i in range (len(self.files)-1, 0, -1):
233 if self.files[i] == self.files[i-1]:
234 del self.files[i]
235
236 # And write complete file list (including default file set) to
237 # the manifest.
238 self.write_manifest ()
239
240 # Don't regenerate the manifest, just read it in.
241 else:
242 self.read_manifest ()
243
244 # get_file_list ()
245
246
Greg Ward4a7319c2000-06-08 00:52:52 +0000247 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000248 """Add all the default files to self.files:
249 - README or README.txt
250 - setup.py
251 - test/test*.py
252 - all pure Python modules mentioned in setup script
253 - all C sources listed as part of extensions or C libraries
254 in the setup script (doesn't catch C headers!)
255 Warns if (README or README.txt) or setup.py are missing; everything
256 else is optional.
257 """
Greg Ward14c8d052000-06-08 01:22:48 +0000258
259 # XXX name of setup script and config file should be taken
260 # programmatically from the Distribution object (except
261 # it doesn't have that capability... yet!)
Greg Warda82122b2000-02-17 23:56:15 +0000262 standards = [('README', 'README.txt'), 'setup.py']
263 for fn in standards:
264 if type (fn) is TupleType:
265 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000266 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000267 for fn in alts:
268 if os.path.exists (fn):
269 got_it = 1
270 self.files.append (fn)
271 break
272
273 if not got_it:
274 self.warn ("standard file not found: should have one of " +
275 string.join (alts, ', '))
276 else:
277 if os.path.exists (fn):
278 self.files.append (fn)
279 else:
280 self.warn ("standard file '%s' not found" % fn)
281
Greg Ward14c8d052000-06-08 01:22:48 +0000282 optional = ['test/test*.py', 'setup.cfg']
Greg Warda82122b2000-02-17 23:56:15 +0000283 for pattern in optional:
284 files = filter (os.path.isfile, glob (pattern))
285 if files:
286 self.files.extend (files)
287
Greg Ward578c10d2000-03-31 02:50:04 +0000288 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000289 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000290 self.files.extend (build_py.get_source_files ())
291
Greg Ward578c10d2000-03-31 02:50:04 +0000292 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000293 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000294 self.files.extend (build_ext.get_source_files ())
295
Greg Ward60908f12000-04-09 03:51:40 +0000296 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000297 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000298 self.files.extend (build_clib.get_source_files ())
299
Greg Ward4a7319c2000-06-08 00:52:52 +0000300 # add_defaults ()
301
Greg Warda82122b2000-02-17 23:56:15 +0000302
Greg Warda82122b2000-02-17 23:56:15 +0000303 def search_dir (self, dir, pattern=None):
304 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000305 containing a Unix-style glob pattern). If 'pattern' is None, find
306 all files under 'dir'. Return the list of found filenames.
307 """
Greg Warda82122b2000-02-17 23:56:15 +0000308 allfiles = findall (dir)
309 if pattern is None:
310 return allfiles
311
312 pattern_re = translate_pattern (pattern)
313 files = []
314 for file in allfiles:
315 if pattern_re.match (os.path.basename (file)):
316 files.append (file)
317
318 return files
319
320 # search_dir ()
321
322
Greg Warda82122b2000-02-17 23:56:15 +0000323 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000324 """Remove filenames from 'self.files' that are under 'dir' and
325 whose basenames match 'pattern'.
326 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000327 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
328 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000329 if pattern is None:
330 pattern_re = None
331 else:
332 pattern_re = translate_pattern (pattern)
333
334 for i in range (len (self.files)-1, -1, -1):
335 (cur_dir, cur_base) = os.path.split (self.files[i])
336 if (cur_dir == dir and
337 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000338 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000339 del self.files[i]
340
341
342 def read_template (self):
343 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000344 'self.template' (usually "MANIFEST.in"). Process all file
345 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000346 update 'self.files' accordingly (filenames may be added to
347 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000348 """
Greg Warda82122b2000-02-17 23:56:15 +0000349 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000350 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000351
352 template = TextFile (self.template,
353 strip_comments=1,
354 skip_blanks=1,
355 join_lines=1,
356 lstrip_ws=1,
357 rstrip_ws=1,
358 collapse_ws=1)
359
360 all_files = findall ()
361
362 while 1:
363
364 line = template.readline()
365 if line is None: # end of file
366 break
367
368 words = string.split (line)
369 action = words[0]
370
371 # First, check that the right number of words are present
372 # for the given action (which is the first word)
373 if action in ('include','exclude',
374 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000375 if len (words) < 2:
Greg Warda82122b2000-02-17 23:56:15 +0000376 template.warn \
377 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000378 "'%s' expects <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000379 action)
380 continue
381
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000382 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000383
384 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000385 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000386 template.warn \
387 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000388 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000389 action)
390 continue
391
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000392 dir = convert_path(words[1])
393 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000394
395 elif action in ('graft','prune'):
396 if len (words) != 2:
397 template.warn \
398 ("invalid manifest template line: " +
399 "'%s' expects a single <dir_pattern>" %
400 action)
401 continue
402
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000403 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000404
405 else:
406 template.warn ("invalid manifest template line: " +
407 "unknown action '%s'" % action)
408 continue
409
410 # OK, now we know that the action is valid and we have the
411 # right number of words on the line for that action -- so we
412 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000413 # defined either (pattern), (dir and pattern), or
414 # (dir_pattern) -- so we don't have to spend any time
415 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000416
417 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000418 self.debug_print("include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000419 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000420 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000421 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000422 template.warn ("no files found matching '%s'" %
423 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000424 else:
425 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000426
427 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000428 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000429 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000430 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000431 if num == 0:
432 template.warn (
433 "no previously-included files found matching '%s'"%
434 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000435
436 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000437 self.debug_print("global-include " + 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 (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000440 if not files:
441 template.warn (("no files found matching '%s' " +
442 "anywhere in distribution") %
443 pattern)
444 else:
445 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000446
447 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000448 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000449 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000450 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000451 if num == 0:
452 template.warn \
453 (("no previously-included files matching '%s' " +
454 "found anywhere in distribution") %
455 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000456
457 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000458 self.debug_print("recursive-include %s %s" %
459 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000460 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000461 files = self.select_pattern (
462 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000463 if not files:
464 template.warn (("no files found matching '%s' " +
465 "under directory '%s'") %
466 (pattern, dir))
467 else:
468 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000469
470 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000471 self.debug_print("recursive-exclude %s %s" %
472 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000473 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000474 num = self.exclude_pattern(
475 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000476 if num == 0:
477 template.warn \
478 (("no previously-included files matching '%s' " +
479 "found under directory '%s'") %
480 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000481
482 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000483 self.debug_print("graft " + dir_pattern)
484 files = self.select_pattern(
485 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000486 if not files:
487 template.warn ("no directories found matching '%s'" %
488 dir_pattern)
489 else:
490 self.files.extend (files)
491
492 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000493 self.debug_print("prune " + dir_pattern)
494 num = self.exclude_pattern(
495 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000496 if num == 0:
497 template.warn \
498 (("no previously-included directories found " +
499 "matching '%s'") %
500 dir_pattern)
501 else:
502 raise RuntimeError, \
503 "this cannot happen: invalid action '%s'" % action
504
505 # while loop over lines of template file
506
507 # read_template ()
508
509
Greg Wardce15c6c2000-06-08 01:06:02 +0000510 def prune_file_list (self):
511 """Prune off branches that might slip into the file list as created
512 by 'read_template()', but really don't belong there: specifically,
513 the build tree (typically "build") and the release tree itself
514 (only an issue if we ran "sdist" previously with --keep-tree, or it
515 aborted).
516 """
517 build = self.get_finalized_command('build')
518 base_dir = self.distribution.get_fullname()
519 self.exclude_pattern (self.files, None, prefix=build.build_base)
520 self.exclude_pattern (self.files, None, prefix=base_dir)
521
522
Greg Wardf8b9e202000-06-08 00:08:14 +0000523 def select_pattern (self, files, pattern, anchor=1, prefix=None):
524 """Select strings (presumably filenames) from 'files' that match
525 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
526 quite the same as implemented by the 'fnmatch' module: '*' and '?'
527 match non-special characters, where "special" is platform-dependent:
528 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
529 Mac OS.
530
531 If 'anchor' is true (the default), then the pattern match is more
532 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
533 'anchor' is false, both of these will match.
534
535 If 'prefix' is supplied, then only filenames starting with 'prefix'
536 (itself a pattern) and ending with 'pattern', with anything in between
537 them, will match. 'anchor' is ignored in this case.
538
539 Return the list of matching strings, possibly empty.
540 """
541 matches = []
542 pattern_re = translate_pattern (pattern, anchor, prefix)
543 self.debug_print("select_pattern: applying regex r'%s'" %
544 pattern_re.pattern)
545 for name in files:
546 if pattern_re.search (name):
547 matches.append (name)
548 self.debug_print(" adding " + name)
549
550 return matches
551
552 # select_pattern ()
553
554
555 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
556 """Remove strings (presumably filenames) from 'files' that match
557 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
558 as for 'select_pattern()', above. The list 'files' is modified
559 in place.
560 """
561 pattern_re = translate_pattern (pattern, anchor, prefix)
562 self.debug_print("exclude_pattern: applying regex r'%s'" %
563 pattern_re.pattern)
564 for i in range (len(files)-1, -1, -1):
565 if pattern_re.search (files[i]):
566 self.debug_print(" removing " + files[i])
567 del files[i]
568
569 # exclude_pattern ()
570
571
Greg Warda82122b2000-02-17 23:56:15 +0000572 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000573 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000574 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000575 by 'self.manifest'.
576 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000577 self.execute(write_file,
578 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000579 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000580
581 # write_manifest ()
582
583
584 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000585 """Read the manifest file (named by 'self.manifest') and use it to
586 fill in 'self.files', the list of files to include in the source
587 distribution.
588 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000589 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000590 manifest = open (self.manifest)
591 while 1:
592 line = manifest.readline ()
593 if line == '': # end of file
594 break
595 if line[-1] == '\n':
596 line = line[0:-1]
597 self.files.append (line)
598
599 # read_manifest ()
600
601
Greg Warda82122b2000-02-17 23:56:15 +0000602 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000603 """Create the directory tree that will become the source
604 distribution archive. All directories implied by the filenames in
605 'files' are created under 'base_dir', and then we hard link or copy
606 (if hard linking is unavailable) those files into place.
607 Essentially, this duplicates the developer's source tree, but in a
608 directory named after the distribution, containing only the files
609 to be distributed.
610 """
Greg Ward578c10d2000-03-31 02:50:04 +0000611 # Create all the directories under 'base_dir' necessary to
612 # put 'files' there.
613 create_tree (base_dir, files,
614 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000615
616 # And walk over the list of files, either making a hard link (if
617 # os.link exists) to each one that doesn't already exist in its
618 # corresponding location under 'base_dir', or copying each file
619 # that's out-of-date in 'base_dir'. (Usually, all files will be
620 # out-of-date, because by default we blow away 'base_dir' when
621 # we're done making the distribution archives.)
622
Greg Ward578c10d2000-03-31 02:50:04 +0000623 if hasattr (os, 'link'): # can make hard links on this system
624 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000625 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000626 else: # nope, have to copy
627 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000628 msg = "copying files to %s..." % base_dir
629
630 self.announce (msg)
631 for file in files:
632 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000633 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000634
635 # make_release_tree ()
636
637
Greg Warda82122b2000-02-17 23:56:15 +0000638 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000639 """Create the source distribution(s). First, we create the release
640 tree with 'make_release_tree()'; then, we create all required
641 archive files (according to 'self.formats') from the release tree.
642 Finally, we clean up by blowing away the release tree (unless
643 'self.keep_tree' is true). The list of archive files created is
644 stored so it can be retrieved later by 'get_archive_files()'.
645 """
Greg Ward578c10d2000-03-31 02:50:04 +0000646 # Don't warn about missing meta-data here -- should be (and is!)
647 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000648 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000649
Greg Warda82122b2000-02-17 23:56:15 +0000650 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000651 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000652 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000653 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
654 archive_files.append(file)
655
656 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000657
658 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000659 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000660
Greg Wardd87eb732000-06-01 01:10:56 +0000661 def get_archive_files (self):
662 """Return the list of archive files created when the command
663 was run, or None if the command hasn't run yet.
664 """
665 return self.archive_files
666
Greg Wardfcd974e2000-05-25 01:10:04 +0000667# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000668
669
670# ----------------------------------------------------------------------
671# Utility functions
672
673def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000674 """Find all files under 'dir' and return the list of full filenames
675 (relative to 'dir').
676 """
Greg Warda82122b2000-02-17 23:56:15 +0000677 list = []
678 stack = [dir]
679 pop = stack.pop
680 push = stack.append
681
682 while stack:
683 dir = pop()
684 names = os.listdir (dir)
685
686 for name in names:
687 if dir != os.curdir: # avoid the dreaded "./" syndrome
688 fullname = os.path.join (dir, name)
689 else:
690 fullname = name
691 list.append (fullname)
692 if os.path.isdir (fullname) and not os.path.islink(fullname):
693 push (fullname)
694
695 return list
696
697
Greg Warda82122b2000-02-17 23:56:15 +0000698def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000699 """Translate a shell-like glob pattern to a regular expression; return
700 a string containing the regex. Differs from 'fnmatch.translate()' in
701 that '*' does not match "special characters" (which are
702 platform-specific).
703 """
Greg Warda82122b2000-02-17 23:56:15 +0000704 pattern_re = fnmatch.translate (pattern)
705
706 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
707 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
708 # and by extension they shouldn't match such "special characters" under
709 # any OS. So change all non-escaped dots in the RE to match any
710 # character except the special characters.
711 # XXX currently the "special characters" are just slash -- i.e. this is
712 # Unix-only.
713 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
714 return pattern_re
715
716# glob_to_re ()
717
718
719def translate_pattern (pattern, anchor=1, prefix=None):
720 """Translate a shell-like wildcard pattern to a compiled regular
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000721 expression. Return the compiled regex.
722 """
Greg Warda82122b2000-02-17 23:56:15 +0000723 if pattern:
724 pattern_re = glob_to_re (pattern)
725 else:
726 pattern_re = ''
727
728 if prefix is not None:
729 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
730 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
731 else: # no prefix -- respect anchor flag
732 if anchor:
733 pattern_re = "^" + pattern_re
734
735 return re.compile (pattern_re)
736
737# translate_pattern ()