blob: 5a78cc58d29ae677b7ac4c61c3a1bf16b9682d9d [file] [log] [blame]
Greg Warda82122b2000-02-17 23:56:15 +00001"""distutils.command.sdist
2
3Implements the Distutils 'sdist' command (create a source distribution)."""
4
5# created 1999/09/22, Greg Ward
6
Greg Ward3ce77fd2000-03-02 01:49:45 +00007__revision__ = "$Id$"
Greg Warda82122b2000-02-17 23:56:15 +00008
9import sys, os, string, re
10import fnmatch
11from types import *
12from glob import glob
Greg Warda82122b2000-02-17 23:56:15 +000013from distutils.core import Command
Greg Wardf1fe1032000-06-08 00:14:18 +000014from distutils.util import \
15 convert_path, create_tree, remove_tree, newer, write_file, \
16 check_archive_formats, ARCHIVE_FORMATS
Greg Warda82122b2000-02-17 23:56:15 +000017from distutils.text_file import TextFile
Greg Ward6a9a5452000-04-22 03:11:55 +000018from distutils.errors import DistutilsExecError, DistutilsOptionError
Greg Warda82122b2000-02-17 23:56:15 +000019
20
Greg Ward1993f9a2000-02-18 00:13:53 +000021class sdist (Command):
Greg Warda82122b2000-02-17 23:56:15 +000022
23 description = "create a source distribution (tarball, zip file, etc.)"
24
Greg Wardbbeceea2000-02-18 00:25:39 +000025 user_options = [
26 ('template=', 't',
27 "name of manifest template file [default: MANIFEST.in]"),
28 ('manifest=', 'm',
29 "name of manifest file [default: MANIFEST]"),
30 ('use-defaults', None,
31 "include the default file set in the manifest "
32 "[default; disable with --no-defaults]"),
Greg Ward839d5322000-04-26 01:14:33 +000033 ('manifest-only', 'o',
Greg Wardc3c8c6e2000-06-08 00:46:45 +000034 "just regenerate the manifest and then stop "
35 "(implies --force-manifest)"),
Greg Ward839d5322000-04-26 01:14:33 +000036 ('force-manifest', 'f',
Greg Wardbbeceea2000-02-18 00:25:39 +000037 "forcibly regenerate the manifest and carry on as usual"),
Greg Wardbbeceea2000-02-18 00:25:39 +000038 ('formats=', None,
Greg Ward9d17a7a2000-06-07 03:00:06 +000039 "formats for source distribution"),
Greg Wardbbeceea2000-02-18 00:25:39 +000040 ('keep-tree', 'k',
41 "keep the distribution tree around after creating " +
42 "archive file(s)"),
43 ]
Greg Wardf1fe1032000-06-08 00:14:18 +000044
45
46 # XXX ugh: this has to precede the 'help_options' list, because
47 # it is mentioned there -- also, this is not a method, even though
48 # it's defined in a class: double-ugh!
49 def show_formats ():
50 """Print all possible values for the 'formats' option -- used by
51 the "--help-formats" command-line option.
52 """
Greg Ward9d17a7a2000-06-07 03:00:06 +000053 from distutils.fancy_getopt import FancyGetopt
Greg Wardf1fe1032000-06-08 00:14:18 +000054 formats=[]
Greg Ward9d17a7a2000-06-07 03:00:06 +000055 for format in ARCHIVE_FORMATS.keys():
Greg Wardf1fe1032000-06-08 00:14:18 +000056 formats.append(("formats="+format,None,ARCHIVE_FORMATS[format][2]))
57 formats.sort()
58 pretty_printer = FancyGetopt(formats)
59 pretty_printer.print_help(
60 "List of available source distribution formats:")
Greg Ward9d17a7a2000-06-07 03:00:06 +000061
62 help_options = [
63 ('help-formats', None,
Greg Wardf1fe1032000-06-08 00:14:18 +000064 "lists available distribution formats", show_formats),
Greg Ward9d17a7a2000-06-07 03:00:06 +000065 ]
66
Greg Warda82122b2000-02-17 23:56:15 +000067 negative_opts = {'use-defaults': 'no-defaults'}
68
69 default_format = { 'posix': 'gztar',
70 'nt': 'zip' }
71
Greg Warda82122b2000-02-17 23:56:15 +000072
Greg Warde01149c2000-02-18 00:35:22 +000073 def initialize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000074 # 'template' and 'manifest' are, respectively, the names of
75 # the manifest template and manifest file.
76 self.template = None
77 self.manifest = None
78
79 # 'use_defaults': if true, we will include the default file set
80 # in the manifest
81 self.use_defaults = 1
82
83 self.manifest_only = 0
84 self.force_manifest = 0
85
86 self.formats = None
Greg Warda82122b2000-02-17 23:56:15 +000087 self.keep_tree = 0
88
Greg Wardd87eb732000-06-01 01:10:56 +000089 self.archive_files = None
90
Greg Warda82122b2000-02-17 23:56:15 +000091
Greg Warde01149c2000-02-18 00:35:22 +000092 def finalize_options (self):
Greg Warda82122b2000-02-17 23:56:15 +000093 if self.manifest is None:
94 self.manifest = "MANIFEST"
95 if self.template is None:
96 self.template = "MANIFEST.in"
97
Greg Ward62d5a572000-06-04 15:12:51 +000098 self.ensure_string_list('formats')
Greg Warda82122b2000-02-17 23:56:15 +000099 if self.formats is None:
100 try:
101 self.formats = [self.default_format[os.name]]
102 except KeyError:
103 raise DistutilsPlatformError, \
Greg Ward578c10d2000-03-31 02:50:04 +0000104 "don't know how to create source distributions " + \
105 "on platform %s" % os.name
Greg Warda82122b2000-02-17 23:56:15 +0000106
Greg Ward6a9a5452000-04-22 03:11:55 +0000107 bad_format = check_archive_formats (self.formats)
108 if bad_format:
109 raise DistutilsOptionError, \
110 "unknown archive format '%s'" % bad_format
111
Greg Warda82122b2000-02-17 23:56:15 +0000112
113 def run (self):
114
115 # 'files' is the list of files that will make up the manifest
116 self.files = []
117
118 # Ensure that all required meta-data is given; warn if not (but
119 # don't die, it's not *that* serious!)
120 self.check_metadata ()
121
122 # Do whatever it takes to get the list of files to process
123 # (process the manifest template, read an existing manifest,
124 # whatever). File list is put into 'self.files'.
125 self.get_file_list ()
126
127 # If user just wanted us to regenerate the manifest, stop now.
128 if self.manifest_only:
129 return
130
131 # Otherwise, go ahead and create the source distribution tarball,
132 # or zipfile, or whatever.
133 self.make_distribution ()
134
135
136 def check_metadata (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000137 """Ensure that all required elements of meta-data (name, version,
138 URL, (author and author_email) or (maintainer and
139 maintainer_email)) are supplied by the Distribution object; warn if
140 any are missing.
141 """
Greg Ward535f2d92000-04-21 04:37:12 +0000142 metadata = self.distribution.metadata
Greg Warda82122b2000-02-17 23:56:15 +0000143
144 missing = []
145 for attr in ('name', 'version', 'url'):
Greg Ward535f2d92000-04-21 04:37:12 +0000146 if not (hasattr (metadata, attr) and getattr (metadata, attr)):
Greg Warda82122b2000-02-17 23:56:15 +0000147 missing.append (attr)
148
149 if missing:
150 self.warn ("missing required meta-data: " +
151 string.join (missing, ", "))
152
Greg Ward535f2d92000-04-21 04:37:12 +0000153 if metadata.author:
154 if not metadata.author_email:
Greg Warda82122b2000-02-17 23:56:15 +0000155 self.warn ("missing meta-data: if 'author' supplied, " +
156 "'author_email' must be supplied too")
Greg Ward535f2d92000-04-21 04:37:12 +0000157 elif metadata.maintainer:
158 if not metadata.maintainer_email:
Greg Warda82122b2000-02-17 23:56:15 +0000159 self.warn ("missing meta-data: if 'maintainer' supplied, " +
160 "'maintainer_email' must be supplied too")
161 else:
162 self.warn ("missing meta-data: either (author and author_email) " +
163 "or (maintainer and maintainer_email) " +
164 "must be supplied")
165
166 # check_metadata ()
167
168
169 def get_file_list (self):
170 """Figure out the list of files to include in the source
Greg Warde0c8c2f2000-06-08 00:24:01 +0000171 distribution, and put it in 'self.files'. This might involve
172 reading the manifest template (and writing the manifest), or just
173 reading the manifest, or just using the default file set -- it all
174 depends on the user's options and the state of the filesystem.
175 """
Greg Warda82122b2000-02-17 23:56:15 +0000176 template_exists = os.path.isfile (self.template)
177 if template_exists:
178 template_newer = newer (self.template, self.manifest)
179
180 # Regenerate the manifest if necessary (or if explicitly told to)
181 if ((template_exists and template_newer) or
182 self.force_manifest or
183 self.manifest_only):
184
185 if not template_exists:
186 self.warn (("manifest template '%s' does not exist " +
187 "(using default file list)") %
188 self.template)
189
190 # Add default file set to 'files'
191 if self.use_defaults:
Greg Ward4a7319c2000-06-08 00:52:52 +0000192 self.add_defaults ()
Greg Warda82122b2000-02-17 23:56:15 +0000193
194 # Read manifest template if it exists
195 if template_exists:
196 self.read_template ()
197
Greg Wardce15c6c2000-06-08 01:06:02 +0000198 # Prune away the build and source distribution directories
199 self.prune_file_list()
200
Greg Warda82122b2000-02-17 23:56:15 +0000201 # File list now complete -- sort it so that higher-level files
202 # come first
203 sortable_files = map (os.path.split, self.files)
204 sortable_files.sort ()
205 self.files = []
206 for sort_tuple in sortable_files:
207 self.files.append (apply (os.path.join, sort_tuple))
208
209 # Remove duplicates from the file list
210 for i in range (len(self.files)-1, 0, -1):
211 if self.files[i] == self.files[i-1]:
212 del self.files[i]
213
214 # And write complete file list (including default file set) to
215 # the manifest.
216 self.write_manifest ()
217
218 # Don't regenerate the manifest, just read it in.
219 else:
220 self.read_manifest ()
221
222 # get_file_list ()
223
224
Greg Ward4a7319c2000-06-08 00:52:52 +0000225 def add_defaults (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000226 """Add all the default files to self.files:
227 - README or README.txt
228 - setup.py
229 - test/test*.py
230 - all pure Python modules mentioned in setup script
231 - all C sources listed as part of extensions or C libraries
232 in the setup script (doesn't catch C headers!)
233 Warns if (README or README.txt) or setup.py are missing; everything
234 else is optional.
235 """
Greg Warda82122b2000-02-17 23:56:15 +0000236 standards = [('README', 'README.txt'), 'setup.py']
237 for fn in standards:
238 if type (fn) is TupleType:
239 alts = fn
Greg Ward48401122000-02-24 03:17:43 +0000240 got_it = 0
Greg Warda82122b2000-02-17 23:56:15 +0000241 for fn in alts:
242 if os.path.exists (fn):
243 got_it = 1
244 self.files.append (fn)
245 break
246
247 if not got_it:
248 self.warn ("standard file not found: should have one of " +
249 string.join (alts, ', '))
250 else:
251 if os.path.exists (fn):
252 self.files.append (fn)
253 else:
254 self.warn ("standard file '%s' not found" % fn)
255
256 optional = ['test/test*.py']
257 for pattern in optional:
258 files = filter (os.path.isfile, glob (pattern))
259 if files:
260 self.files.extend (files)
261
Greg Ward578c10d2000-03-31 02:50:04 +0000262 if self.distribution.has_pure_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000263 build_py = self.get_finalized_command ('build_py')
Greg Warda82122b2000-02-17 23:56:15 +0000264 self.files.extend (build_py.get_source_files ())
265
Greg Ward578c10d2000-03-31 02:50:04 +0000266 if self.distribution.has_ext_modules():
Greg Ward4fb29e52000-05-27 17:27:23 +0000267 build_ext = self.get_finalized_command ('build_ext')
Greg Warda82122b2000-02-17 23:56:15 +0000268 self.files.extend (build_ext.get_source_files ())
269
Greg Ward60908f12000-04-09 03:51:40 +0000270 if self.distribution.has_c_libraries():
Greg Ward4fb29e52000-05-27 17:27:23 +0000271 build_clib = self.get_finalized_command ('build_clib')
Greg Ward60908f12000-04-09 03:51:40 +0000272 self.files.extend (build_clib.get_source_files ())
273
Greg Ward4a7319c2000-06-08 00:52:52 +0000274 # add_defaults ()
275
Greg Warda82122b2000-02-17 23:56:15 +0000276
Greg Warda82122b2000-02-17 23:56:15 +0000277 def search_dir (self, dir, pattern=None):
278 """Recursively find files under 'dir' matching 'pattern' (a string
Greg Warde0c8c2f2000-06-08 00:24:01 +0000279 containing a Unix-style glob pattern). If 'pattern' is None, find
280 all files under 'dir'. Return the list of found filenames.
281 """
Greg Warda82122b2000-02-17 23:56:15 +0000282 allfiles = findall (dir)
283 if pattern is None:
284 return allfiles
285
286 pattern_re = translate_pattern (pattern)
287 files = []
288 for file in allfiles:
289 if pattern_re.match (os.path.basename (file)):
290 files.append (file)
291
292 return files
293
294 # search_dir ()
295
296
Greg Warda82122b2000-02-17 23:56:15 +0000297 def recursive_exclude_pattern (self, dir, pattern=None):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000298 """Remove filenames from 'self.files' that are under 'dir' and
299 whose basenames match 'pattern'.
300 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000301 self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" %
302 (dir, pattern))
Greg Warda82122b2000-02-17 23:56:15 +0000303 if pattern is None:
304 pattern_re = None
305 else:
306 pattern_re = translate_pattern (pattern)
307
308 for i in range (len (self.files)-1, -1, -1):
309 (cur_dir, cur_base) = os.path.split (self.files[i])
310 if (cur_dir == dir and
311 (pattern_re is None or pattern_re.match (cur_base))):
Greg Wardf8b9e202000-06-08 00:08:14 +0000312 self.debug_print("removing %s" % self.files[i])
Greg Warda82122b2000-02-17 23:56:15 +0000313 del self.files[i]
314
315
316 def read_template (self):
317 """Read and parse the manifest template file named by
Greg Warde0c8c2f2000-06-08 00:24:01 +0000318 'self.template' (usually "MANIFEST.in"). Process all file
319 specifications (include and exclude) in the manifest template and
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000320 update 'self.files' accordingly (filenames may be added to
321 or removed from 'self.files' based on the manifest template).
Greg Warde0c8c2f2000-06-08 00:24:01 +0000322 """
Greg Warda82122b2000-02-17 23:56:15 +0000323 assert self.files is not None and type (self.files) is ListType
Greg Wardf8b9e202000-06-08 00:08:14 +0000324 self.announce("reading manifest template '%s'" % self.template)
Greg Warda82122b2000-02-17 23:56:15 +0000325
326 template = TextFile (self.template,
327 strip_comments=1,
328 skip_blanks=1,
329 join_lines=1,
330 lstrip_ws=1,
331 rstrip_ws=1,
332 collapse_ws=1)
333
334 all_files = findall ()
335
336 while 1:
337
338 line = template.readline()
339 if line is None: # end of file
340 break
341
342 words = string.split (line)
343 action = words[0]
344
345 # First, check that the right number of words are present
346 # for the given action (which is the first word)
347 if action in ('include','exclude',
348 'global-include','global-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000349 if len (words) < 2:
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 <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000353 action)
354 continue
355
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000356 pattern_list = map(convert_path, words[1:])
Greg Warda82122b2000-02-17 23:56:15 +0000357
358 elif action in ('recursive-include','recursive-exclude'):
Greg Ward9d5afa92000-04-21 04:31:10 +0000359 if len (words) < 3:
Greg Warda82122b2000-02-17 23:56:15 +0000360 template.warn \
361 ("invalid manifest template line: " +
Greg Ward9d5afa92000-04-21 04:31:10 +0000362 "'%s' expects <dir> <pattern1> <pattern2> ..." %
Greg Warda82122b2000-02-17 23:56:15 +0000363 action)
364 continue
365
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000366 dir = convert_path(words[1])
367 pattern_list = map (convert_path, words[2:])
Greg Warda82122b2000-02-17 23:56:15 +0000368
369 elif action in ('graft','prune'):
370 if len (words) != 2:
371 template.warn \
372 ("invalid manifest template line: " +
373 "'%s' expects a single <dir_pattern>" %
374 action)
375 continue
376
Greg Wardd8dfb4c2000-05-31 02:32:10 +0000377 dir_pattern = convert_path (words[1])
Greg Warda82122b2000-02-17 23:56:15 +0000378
379 else:
380 template.warn ("invalid manifest template line: " +
381 "unknown action '%s'" % action)
382 continue
383
384 # OK, now we know that the action is valid and we have the
385 # right number of words on the line for that action -- so we
386 # can proceed with minimal error-checking. Also, we have
Greg Ward2b9e43f2000-04-14 00:49:30 +0000387 # defined either (pattern), (dir and pattern), or
388 # (dir_pattern) -- so we don't have to spend any time
389 # digging stuff up out of 'words'.
Greg Warda82122b2000-02-17 23:56:15 +0000390
391 if action == 'include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000392 self.debug_print("include " + 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 files = self.select_pattern (all_files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000395 if not files:
Greg Wardf8b9e202000-06-08 00:08:14 +0000396 template.warn ("no files found matching '%s'" %
397 pattern)
Greg Ward9d5afa92000-04-21 04:31:10 +0000398 else:
399 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000400
401 elif action == 'exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000402 self.debug_print("exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000403 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000404 num = self.exclude_pattern (self.files, pattern, anchor=1)
Greg Ward9d5afa92000-04-21 04:31:10 +0000405 if num == 0:
406 template.warn (
407 "no previously-included files found matching '%s'"%
408 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000409
410 elif action == 'global-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000411 self.debug_print("global-include " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000412 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000413 files = self.select_pattern (all_files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000414 if not files:
415 template.warn (("no files found matching '%s' " +
416 "anywhere in distribution") %
417 pattern)
418 else:
419 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000420
421 elif action == 'global-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000422 self.debug_print("global-exclude " + string.join(pattern_list))
Greg Ward9d5afa92000-04-21 04:31:10 +0000423 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000424 num = self.exclude_pattern (self.files, pattern, anchor=0)
Greg Ward9d5afa92000-04-21 04:31:10 +0000425 if num == 0:
426 template.warn \
427 (("no previously-included files matching '%s' " +
428 "found anywhere in distribution") %
429 pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000430
431 elif action == 'recursive-include':
Greg Wardf8b9e202000-06-08 00:08:14 +0000432 self.debug_print("recursive-include %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 files = self.select_pattern (
436 all_files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000437 if not files:
438 template.warn (("no files found matching '%s' " +
439 "under directory '%s'") %
440 (pattern, dir))
441 else:
442 self.files.extend (files)
Greg Warda82122b2000-02-17 23:56:15 +0000443
444 elif action == 'recursive-exclude':
Greg Wardf8b9e202000-06-08 00:08:14 +0000445 self.debug_print("recursive-exclude %s %s" %
446 (dir, string.join(pattern_list)))
Greg Ward9d5afa92000-04-21 04:31:10 +0000447 for pattern in pattern_list:
Greg Wardf8b9e202000-06-08 00:08:14 +0000448 num = self.exclude_pattern(
449 self.files, pattern, prefix=dir)
Greg Ward9d5afa92000-04-21 04:31:10 +0000450 if num == 0:
451 template.warn \
452 (("no previously-included files matching '%s' " +
453 "found under directory '%s'") %
454 (pattern, dir))
Greg Warda82122b2000-02-17 23:56:15 +0000455
456 elif action == 'graft':
Greg Wardf8b9e202000-06-08 00:08:14 +0000457 self.debug_print("graft " + dir_pattern)
458 files = self.select_pattern(
459 all_files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000460 if not files:
461 template.warn ("no directories found matching '%s'" %
462 dir_pattern)
463 else:
464 self.files.extend (files)
465
466 elif action == 'prune':
Greg Wardf8b9e202000-06-08 00:08:14 +0000467 self.debug_print("prune " + dir_pattern)
468 num = self.exclude_pattern(
469 self.files, None, prefix=dir_pattern)
Greg Warda82122b2000-02-17 23:56:15 +0000470 if num == 0:
471 template.warn \
472 (("no previously-included directories found " +
473 "matching '%s'") %
474 dir_pattern)
475 else:
476 raise RuntimeError, \
477 "this cannot happen: invalid action '%s'" % action
478
479 # while loop over lines of template file
480
481 # read_template ()
482
483
Greg Wardce15c6c2000-06-08 01:06:02 +0000484 def prune_file_list (self):
485 """Prune off branches that might slip into the file list as created
486 by 'read_template()', but really don't belong there: specifically,
487 the build tree (typically "build") and the release tree itself
488 (only an issue if we ran "sdist" previously with --keep-tree, or it
489 aborted).
490 """
491 build = self.get_finalized_command('build')
492 base_dir = self.distribution.get_fullname()
493 self.exclude_pattern (self.files, None, prefix=build.build_base)
494 self.exclude_pattern (self.files, None, prefix=base_dir)
495
496
Greg Wardf8b9e202000-06-08 00:08:14 +0000497 def select_pattern (self, files, pattern, anchor=1, prefix=None):
498 """Select strings (presumably filenames) from 'files' that match
499 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not
500 quite the same as implemented by the 'fnmatch' module: '*' and '?'
501 match non-special characters, where "special" is platform-dependent:
502 slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on
503 Mac OS.
504
505 If 'anchor' is true (the default), then the pattern match is more
506 stringent: "*.py" will match "foo.py" but not "foo/bar.py". If
507 'anchor' is false, both of these will match.
508
509 If 'prefix' is supplied, then only filenames starting with 'prefix'
510 (itself a pattern) and ending with 'pattern', with anything in between
511 them, will match. 'anchor' is ignored in this case.
512
513 Return the list of matching strings, possibly empty.
514 """
515 matches = []
516 pattern_re = translate_pattern (pattern, anchor, prefix)
517 self.debug_print("select_pattern: applying regex r'%s'" %
518 pattern_re.pattern)
519 for name in files:
520 if pattern_re.search (name):
521 matches.append (name)
522 self.debug_print(" adding " + name)
523
524 return matches
525
526 # select_pattern ()
527
528
529 def exclude_pattern (self, files, pattern, anchor=1, prefix=None):
530 """Remove strings (presumably filenames) from 'files' that match
531 'pattern'. 'pattern', 'anchor', 'and 'prefix' are the same
532 as for 'select_pattern()', above. The list 'files' is modified
533 in place.
534 """
535 pattern_re = translate_pattern (pattern, anchor, prefix)
536 self.debug_print("exclude_pattern: applying regex r'%s'" %
537 pattern_re.pattern)
538 for i in range (len(files)-1, -1, -1):
539 if pattern_re.search (files[i]):
540 self.debug_print(" removing " + files[i])
541 del files[i]
542
543 # exclude_pattern ()
544
545
Greg Warda82122b2000-02-17 23:56:15 +0000546 def write_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000547 """Write the file list in 'self.files' (presumably as filled in by
Greg Ward4a7319c2000-06-08 00:52:52 +0000548 'add_defaults()' and 'read_template()') to the manifest file named
Greg Warde0c8c2f2000-06-08 00:24:01 +0000549 by 'self.manifest'.
550 """
Greg Ward1b8e1d42000-04-26 01:12:40 +0000551 self.execute(write_file,
552 (self.manifest, self.files),
Greg Wardf8b9e202000-06-08 00:08:14 +0000553 "writing manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000554
555 # write_manifest ()
556
557
558 def read_manifest (self):
Greg Warde0c8c2f2000-06-08 00:24:01 +0000559 """Read the manifest file (named by 'self.manifest') and use it to
560 fill in 'self.files', the list of files to include in the source
561 distribution.
562 """
Greg Wardf8b9e202000-06-08 00:08:14 +0000563 self.announce("reading manifest file '%s'" % self.manifest)
Greg Warda82122b2000-02-17 23:56:15 +0000564 manifest = open (self.manifest)
565 while 1:
566 line = manifest.readline ()
567 if line == '': # end of file
568 break
569 if line[-1] == '\n':
570 line = line[0:-1]
571 self.files.append (line)
572
573 # read_manifest ()
574
575
Greg Warda82122b2000-02-17 23:56:15 +0000576 def make_release_tree (self, base_dir, files):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000577 """Create the directory tree that will become the source
578 distribution archive. All directories implied by the filenames in
579 'files' are created under 'base_dir', and then we hard link or copy
580 (if hard linking is unavailable) those files into place.
581 Essentially, this duplicates the developer's source tree, but in a
582 directory named after the distribution, containing only the files
583 to be distributed.
584 """
Greg Ward578c10d2000-03-31 02:50:04 +0000585 # Create all the directories under 'base_dir' necessary to
586 # put 'files' there.
587 create_tree (base_dir, files,
588 verbose=self.verbose, dry_run=self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000589
590 # And walk over the list of files, either making a hard link (if
591 # os.link exists) to each one that doesn't already exist in its
592 # corresponding location under 'base_dir', or copying each file
593 # that's out-of-date in 'base_dir'. (Usually, all files will be
594 # out-of-date, because by default we blow away 'base_dir' when
595 # we're done making the distribution archives.)
596
Greg Ward578c10d2000-03-31 02:50:04 +0000597 if hasattr (os, 'link'): # can make hard links on this system
598 link = 'hard'
Greg Warda82122b2000-02-17 23:56:15 +0000599 msg = "making hard links in %s..." % base_dir
Greg Ward578c10d2000-03-31 02:50:04 +0000600 else: # nope, have to copy
601 link = None
Greg Warda82122b2000-02-17 23:56:15 +0000602 msg = "copying files to %s..." % base_dir
603
604 self.announce (msg)
605 for file in files:
606 dest = os.path.join (base_dir, file)
Greg Ward578c10d2000-03-31 02:50:04 +0000607 self.copy_file (file, dest, link=link)
Greg Warda82122b2000-02-17 23:56:15 +0000608
609 # make_release_tree ()
610
611
Greg Warda82122b2000-02-17 23:56:15 +0000612 def make_distribution (self):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000613 """Create the source distribution(s). First, we create the release
614 tree with 'make_release_tree()'; then, we create all required
615 archive files (according to 'self.formats') from the release tree.
616 Finally, we clean up by blowing away the release tree (unless
617 'self.keep_tree' is true). The list of archive files created is
618 stored so it can be retrieved later by 'get_archive_files()'.
619 """
Greg Ward578c10d2000-03-31 02:50:04 +0000620 # Don't warn about missing meta-data here -- should be (and is!)
621 # done elsewhere.
Greg Ward0ae7f762000-04-22 02:51:25 +0000622 base_dir = self.distribution.get_fullname()
Greg Warda82122b2000-02-17 23:56:15 +0000623
Greg Warda82122b2000-02-17 23:56:15 +0000624 self.make_release_tree (base_dir, self.files)
Greg Wardd87eb732000-06-01 01:10:56 +0000625 archive_files = [] # remember names of files we create
Greg Warda82122b2000-02-17 23:56:15 +0000626 for fmt in self.formats:
Greg Wardd87eb732000-06-01 01:10:56 +0000627 file = self.make_archive (base_dir, fmt, base_dir=base_dir)
628 archive_files.append(file)
629
630 self.archive_files = archive_files
Greg Warda82122b2000-02-17 23:56:15 +0000631
632 if not self.keep_tree:
Greg Ward2dc139c2000-03-18 15:43:42 +0000633 remove_tree (base_dir, self.verbose, self.dry_run)
Greg Warda82122b2000-02-17 23:56:15 +0000634
Greg Wardd87eb732000-06-01 01:10:56 +0000635 def get_archive_files (self):
636 """Return the list of archive files created when the command
637 was run, or None if the command hasn't run yet.
638 """
639 return self.archive_files
640
Greg Wardfcd974e2000-05-25 01:10:04 +0000641# class sdist
Greg Warda82122b2000-02-17 23:56:15 +0000642
643
644# ----------------------------------------------------------------------
645# Utility functions
646
647def findall (dir = os.curdir):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000648 """Find all files under 'dir' and return the list of full filenames
649 (relative to 'dir').
650 """
Greg Warda82122b2000-02-17 23:56:15 +0000651 list = []
652 stack = [dir]
653 pop = stack.pop
654 push = stack.append
655
656 while stack:
657 dir = pop()
658 names = os.listdir (dir)
659
660 for name in names:
661 if dir != os.curdir: # avoid the dreaded "./" syndrome
662 fullname = os.path.join (dir, name)
663 else:
664 fullname = name
665 list.append (fullname)
666 if os.path.isdir (fullname) and not os.path.islink(fullname):
667 push (fullname)
668
669 return list
670
671
Greg Warda82122b2000-02-17 23:56:15 +0000672def glob_to_re (pattern):
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000673 """Translate a shell-like glob pattern to a regular expression; return
674 a string containing the regex. Differs from 'fnmatch.translate()' in
675 that '*' does not match "special characters" (which are
676 platform-specific).
677 """
Greg Warda82122b2000-02-17 23:56:15 +0000678 pattern_re = fnmatch.translate (pattern)
679
680 # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which
681 # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix,
682 # and by extension they shouldn't match such "special characters" under
683 # any OS. So change all non-escaped dots in the RE to match any
684 # character except the special characters.
685 # XXX currently the "special characters" are just slash -- i.e. this is
686 # Unix-only.
687 pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re)
688 return pattern_re
689
690# glob_to_re ()
691
692
693def translate_pattern (pattern, anchor=1, prefix=None):
694 """Translate a shell-like wildcard pattern to a compiled regular
Greg Wardc3c8c6e2000-06-08 00:46:45 +0000695 expression. Return the compiled regex.
696 """
Greg Warda82122b2000-02-17 23:56:15 +0000697 if pattern:
698 pattern_re = glob_to_re (pattern)
699 else:
700 pattern_re = ''
701
702 if prefix is not None:
703 prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $
704 pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re)
705 else: # no prefix -- respect anchor flag
706 if anchor:
707 pattern_re = "^" + pattern_re
708
709 return re.compile (pattern_re)
710
711# translate_pattern ()