| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 1 | """distutils.command.sdist | 
 | 2 |  | 
 | 3 | Implements the Distutils 'sdist' command (create a source distribution).""" | 
 | 4 |  | 
 | 5 | # created 1999/09/22, Greg Ward | 
 | 6 |  | 
| Greg Ward | 3ce77fd | 2000-03-02 01:49:45 +0000 | [diff] [blame] | 7 | __revision__ = "$Id$" | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 8 |  | 
 | 9 | import sys, os, string, re | 
 | 10 | import fnmatch | 
 | 11 | from types import * | 
 | 12 | from glob import glob | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 13 | from distutils.core import Command | 
| Greg Ward | f1fe103 | 2000-06-08 00:14:18 +0000 | [diff] [blame] | 14 | from distutils.util import \ | 
 | 15 |      convert_path, create_tree, remove_tree, newer, write_file, \ | 
| Greg Ward | 3459381 | 2000-06-24 01:23:37 +0000 | [diff] [blame] | 16 |      check_archive_formats | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 17 | from distutils.text_file import TextFile | 
| Greg Ward | 6a9a545 | 2000-04-22 03:11:55 +0000 | [diff] [blame] | 18 | from distutils.errors import DistutilsExecError, DistutilsOptionError | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 19 |  | 
 | 20 |  | 
| Greg Ward | 3459381 | 2000-06-24 01:23:37 +0000 | [diff] [blame] | 21 | def 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 Ward | 1993f9a | 2000-02-18 00:13:53 +0000 | [diff] [blame] | 37 | class sdist (Command): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 38 |  | 
 | 39 |     description = "create a source distribution (tarball, zip file, etc.)" | 
 | 40 |  | 
| Greg Ward | bbeceea | 2000-02-18 00:25:39 +0000 | [diff] [blame] | 41 |     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 Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 49 |         ('no-defaults', None, | 
 | 50 |          "don't include the default file set"), | 
 | 51 |         ('prune', None, | 
 | 52 |          "specifically exclude files/directories that should not be " | 
 | 53 |          "distributed (build tree, RCS/CVS dirs, etc.) " | 
 | 54 |          "[default; disable with --no-prune]"), | 
 | 55 |         ('no-prune', None, | 
 | 56 |          "don't automatically exclude anything"), | 
| Greg Ward | 839d532 | 2000-04-26 01:14:33 +0000 | [diff] [blame] | 57 |         ('manifest-only', 'o', | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 58 |          "just regenerate the manifest and then stop " | 
 | 59 |          "(implies --force-manifest)"), | 
| Greg Ward | 839d532 | 2000-04-26 01:14:33 +0000 | [diff] [blame] | 60 |         ('force-manifest', 'f', | 
| Greg Ward | bbeceea | 2000-02-18 00:25:39 +0000 | [diff] [blame] | 61 |          "forcibly regenerate the manifest and carry on as usual"), | 
| Greg Ward | bbeceea | 2000-02-18 00:25:39 +0000 | [diff] [blame] | 62 |         ('formats=', None, | 
| Greg Ward | 2ff7887 | 2000-06-24 00:23:20 +0000 | [diff] [blame] | 63 |          "formats for source distribution (comma-separated list)"), | 
| Greg Ward | bbeceea | 2000-02-18 00:25:39 +0000 | [diff] [blame] | 64 |         ('keep-tree', 'k', | 
 | 65 |          "keep the distribution tree around after creating " + | 
 | 66 |          "archive file(s)"), | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 67 |         ('dist-dir=', 'd', | 
 | 68 |          "directory to put the source distribution archive(s) in " | 
 | 69 |          "[default: dist]"), | 
| Greg Ward | bbeceea | 2000-02-18 00:25:39 +0000 | [diff] [blame] | 70 |         ] | 
| Greg Ward | f1fe103 | 2000-06-08 00:14:18 +0000 | [diff] [blame] | 71 |  | 
 | 72 |  | 
| Greg Ward | 9d17a7a | 2000-06-07 03:00:06 +0000 | [diff] [blame] | 73 |     help_options = [ | 
 | 74 |         ('help-formats', None, | 
| Greg Ward | 2ff7887 | 2000-06-24 00:23:20 +0000 | [diff] [blame] | 75 |          "list available distribution formats", show_formats), | 
| Greg Ward | 9d17a7a | 2000-06-07 03:00:06 +0000 | [diff] [blame] | 76 | 	] | 
 | 77 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 78 |     negative_opt = {'no-defaults': 'use-defaults', | 
 | 79 |                     'no-prune': 'prune' } | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 80 |  | 
 | 81 |     default_format = { 'posix': 'gztar', | 
 | 82 |                        'nt': 'zip' } | 
 | 83 |  | 
| Greg Ward | e01149c | 2000-02-18 00:35:22 +0000 | [diff] [blame] | 84 |     def initialize_options (self): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 85 |         # 'template' and 'manifest' are, respectively, the names of | 
 | 86 |         # the manifest template and manifest file. | 
 | 87 |         self.template = None | 
 | 88 |         self.manifest = None | 
 | 89 |  | 
 | 90 |         # 'use_defaults': if true, we will include the default file set | 
 | 91 |         # in the manifest | 
 | 92 |         self.use_defaults = 1 | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 93 |         self.prune = 1 | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 94 |  | 
 | 95 |         self.manifest_only = 0 | 
 | 96 |         self.force_manifest = 0 | 
 | 97 |  | 
 | 98 |         self.formats = None | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 99 |         self.keep_tree = 0 | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 100 |         self.dist_dir = None | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 101 |  | 
| Greg Ward | d87eb73 | 2000-06-01 01:10:56 +0000 | [diff] [blame] | 102 |         self.archive_files = None | 
 | 103 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 104 |  | 
| Greg Ward | e01149c | 2000-02-18 00:35:22 +0000 | [diff] [blame] | 105 |     def finalize_options (self): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 106 |         if self.manifest is None: | 
 | 107 |             self.manifest = "MANIFEST" | 
 | 108 |         if self.template is None: | 
 | 109 |             self.template = "MANIFEST.in" | 
 | 110 |  | 
| Greg Ward | 62d5a57 | 2000-06-04 15:12:51 +0000 | [diff] [blame] | 111 |         self.ensure_string_list('formats') | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 112 |         if self.formats is None: | 
 | 113 |             try: | 
 | 114 |                 self.formats = [self.default_format[os.name]] | 
 | 115 |             except KeyError: | 
 | 116 |                 raise DistutilsPlatformError, \ | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 117 |                       "don't know how to create source distributions " + \ | 
 | 118 |                       "on platform %s" % os.name | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 119 |  | 
| Greg Ward | 6a9a545 | 2000-04-22 03:11:55 +0000 | [diff] [blame] | 120 |         bad_format = check_archive_formats (self.formats) | 
 | 121 |         if bad_format: | 
 | 122 |             raise DistutilsOptionError, \ | 
 | 123 |                   "unknown archive format '%s'" % bad_format | 
 | 124 |  | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 125 |         if self.dist_dir is None: | 
 | 126 |             self.dist_dir = "dist" | 
 | 127 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 128 |  | 
 | 129 |     def run (self): | 
 | 130 |  | 
 | 131 |         # 'files' is the list of files that will make up the manifest | 
 | 132 |         self.files = [] | 
 | 133 |          | 
 | 134 |         # Ensure that all required meta-data is given; warn if not (but | 
 | 135 |         # don't die, it's not *that* serious!) | 
 | 136 |         self.check_metadata () | 
 | 137 |  | 
 | 138 |         # Do whatever it takes to get the list of files to process | 
 | 139 |         # (process the manifest template, read an existing manifest, | 
 | 140 |         # whatever).  File list is put into 'self.files'. | 
 | 141 |         self.get_file_list () | 
 | 142 |  | 
 | 143 |         # If user just wanted us to regenerate the manifest, stop now. | 
 | 144 |         if self.manifest_only: | 
 | 145 |             return | 
 | 146 |  | 
 | 147 |         # Otherwise, go ahead and create the source distribution tarball, | 
 | 148 |         # or zipfile, or whatever. | 
 | 149 |         self.make_distribution () | 
 | 150 |  | 
 | 151 |  | 
 | 152 |     def check_metadata (self): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 153 |         """Ensure that all required elements of meta-data (name, version, | 
 | 154 |         URL, (author and author_email) or (maintainer and | 
 | 155 |         maintainer_email)) are supplied by the Distribution object; warn if | 
 | 156 |         any are missing. | 
 | 157 |         """ | 
| Greg Ward | 535f2d9 | 2000-04-21 04:37:12 +0000 | [diff] [blame] | 158 |         metadata = self.distribution.metadata | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 159 |  | 
 | 160 |         missing = [] | 
 | 161 |         for attr in ('name', 'version', 'url'): | 
| Greg Ward | 535f2d9 | 2000-04-21 04:37:12 +0000 | [diff] [blame] | 162 |             if not (hasattr (metadata, attr) and getattr (metadata, attr)): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 163 |                 missing.append (attr) | 
 | 164 |  | 
 | 165 |         if missing: | 
 | 166 |             self.warn ("missing required meta-data: " + | 
 | 167 |                        string.join (missing, ", ")) | 
 | 168 |  | 
| Greg Ward | 535f2d9 | 2000-04-21 04:37:12 +0000 | [diff] [blame] | 169 |         if metadata.author: | 
 | 170 |             if not metadata.author_email: | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 171 |                 self.warn ("missing meta-data: if 'author' supplied, " + | 
 | 172 |                            "'author_email' must be supplied too") | 
| Greg Ward | 535f2d9 | 2000-04-21 04:37:12 +0000 | [diff] [blame] | 173 |         elif metadata.maintainer: | 
 | 174 |             if not metadata.maintainer_email: | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 175 |                 self.warn ("missing meta-data: if 'maintainer' supplied, " + | 
 | 176 |                            "'maintainer_email' must be supplied too") | 
 | 177 |         else: | 
 | 178 |             self.warn ("missing meta-data: either (author and author_email) " + | 
 | 179 |                        "or (maintainer and maintainer_email) " + | 
 | 180 |                        "must be supplied") | 
 | 181 |  | 
 | 182 |     # check_metadata () | 
 | 183 |  | 
 | 184 |  | 
 | 185 |     def get_file_list (self): | 
 | 186 |         """Figure out the list of files to include in the source | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 187 |         distribution, and put it in 'self.files'.  This might involve | 
 | 188 |         reading the manifest template (and writing the manifest), or just | 
 | 189 |         reading the manifest, or just using the default file set -- it all | 
 | 190 |         depends on the user's options and the state of the filesystem. | 
 | 191 |         """ | 
| Greg Ward | b2db0eb | 2000-06-21 03:29:57 +0000 | [diff] [blame] | 192 |  | 
 | 193 |         # If we have a manifest template, see if it's newer than the | 
 | 194 |         # manifest; if so, we'll regenerate the manifest. | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 195 |         template_exists = os.path.isfile (self.template) | 
 | 196 |         if template_exists: | 
 | 197 |             template_newer = newer (self.template, self.manifest) | 
 | 198 |  | 
| Greg Ward | b2db0eb | 2000-06-21 03:29:57 +0000 | [diff] [blame] | 199 |         # The contents of the manifest file almost certainly depend on the | 
 | 200 |         # setup script as well as the manifest template -- so if the setup | 
 | 201 |         # script is newer than the manifest, we'll regenerate the manifest | 
 | 202 |         # from the template.  (Well, not quite: if we already have a | 
 | 203 |         # manifest, but there's no template -- which will happen if the | 
 | 204 |         # developer elects to generate a manifest some other way -- then we | 
 | 205 |         # can't regenerate the manifest, so we don't.) | 
 | 206 |         setup_newer = newer(sys.argv[0], self.manifest) | 
 | 207 |  | 
 | 208 |         # cases: | 
 | 209 |         #   1) no manifest, template exists: generate manifest | 
 | 210 |         #      (covered by 2a: no manifest == template newer) | 
 | 211 |         #   2) manifest & template exist: | 
 | 212 |         #      2a) template or setup script newer than manifest: | 
 | 213 |         #          regenerate manifest | 
 | 214 |         #      2b) manifest newer than both: | 
 | 215 |         #          do nothing (unless --force or --manifest-only) | 
 | 216 |         #   3) manifest exists, no template: | 
 | 217 |         #      do nothing (unless --force or --manifest-only) | 
 | 218 |         #   4) no manifest, no template: generate w/ warning ("defaults only") | 
 | 219 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 220 |         # Regenerate the manifest if necessary (or if explicitly told to) | 
| Greg Ward | b2db0eb | 2000-06-21 03:29:57 +0000 | [diff] [blame] | 221 |         if ((template_exists and (template_newer or setup_newer)) or | 
 | 222 |             self.force_manifest or self.manifest_only): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 223 |  | 
 | 224 |             if not template_exists: | 
 | 225 |                 self.warn (("manifest template '%s' does not exist " + | 
 | 226 |                             "(using default file list)") % | 
 | 227 |                            self.template) | 
 | 228 |  | 
 | 229 |             # Add default file set to 'files' | 
 | 230 |             if self.use_defaults: | 
| Greg Ward | 4a7319c | 2000-06-08 00:52:52 +0000 | [diff] [blame] | 231 |                 self.add_defaults () | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 232 |  | 
 | 233 |             # Read manifest template if it exists | 
 | 234 |             if template_exists: | 
 | 235 |                 self.read_template () | 
 | 236 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 237 |             # Prune away any directories that don't belong in the source | 
 | 238 |             # distribution | 
 | 239 |             if self.prune: | 
 | 240 |                 self.prune_file_list() | 
| Greg Ward | ce15c6c | 2000-06-08 01:06:02 +0000 | [diff] [blame] | 241 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 242 |             # File list now complete -- sort it so that higher-level files | 
 | 243 |             # come first | 
 | 244 |             sortable_files = map (os.path.split, self.files) | 
 | 245 |             sortable_files.sort () | 
 | 246 |             self.files = [] | 
 | 247 |             for sort_tuple in sortable_files: | 
 | 248 |                 self.files.append (apply (os.path.join, sort_tuple)) | 
 | 249 |  | 
 | 250 |             # Remove duplicates from the file list | 
 | 251 |             for i in range (len(self.files)-1, 0, -1): | 
 | 252 |                 if self.files[i] == self.files[i-1]: | 
 | 253 |                     del self.files[i] | 
 | 254 |  | 
 | 255 |             # And write complete file list (including default file set) to | 
 | 256 |             # the manifest. | 
 | 257 |             self.write_manifest () | 
 | 258 |  | 
 | 259 |         # Don't regenerate the manifest, just read it in. | 
 | 260 |         else: | 
 | 261 |             self.read_manifest () | 
 | 262 |  | 
 | 263 |     # get_file_list () | 
 | 264 |  | 
 | 265 |  | 
| Greg Ward | 4a7319c | 2000-06-08 00:52:52 +0000 | [diff] [blame] | 266 |     def add_defaults (self): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 267 |         """Add all the default files to self.files: | 
 | 268 |           - README or README.txt | 
 | 269 |           - setup.py | 
 | 270 |           - test/test*.py | 
 | 271 |           - all pure Python modules mentioned in setup script | 
 | 272 |           - all C sources listed as part of extensions or C libraries | 
 | 273 |             in the setup script (doesn't catch C headers!) | 
 | 274 |         Warns if (README or README.txt) or setup.py are missing; everything | 
 | 275 |         else is optional. | 
 | 276 |         """ | 
| Greg Ward | 14c8d05 | 2000-06-08 01:22:48 +0000 | [diff] [blame] | 277 |  | 
 | 278 |         # XXX name of setup script and config file should be taken | 
 | 279 |         # programmatically from the Distribution object (except | 
 | 280 |         # it doesn't have that capability... yet!) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 281 |         standards = [('README', 'README.txt'), 'setup.py'] | 
 | 282 |         for fn in standards: | 
 | 283 |             if type (fn) is TupleType: | 
 | 284 |                 alts = fn | 
| Greg Ward | 4840112 | 2000-02-24 03:17:43 +0000 | [diff] [blame] | 285 |                 got_it = 0 | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 286 |                 for fn in alts: | 
 | 287 |                     if os.path.exists (fn): | 
 | 288 |                         got_it = 1 | 
 | 289 |                         self.files.append (fn) | 
 | 290 |                         break | 
 | 291 |  | 
 | 292 |                 if not got_it: | 
 | 293 |                     self.warn ("standard file not found: should have one of " + | 
 | 294 |                                string.join (alts, ', ')) | 
 | 295 |             else: | 
 | 296 |                 if os.path.exists (fn): | 
 | 297 |                     self.files.append (fn) | 
 | 298 |                 else: | 
 | 299 |                     self.warn ("standard file '%s' not found" % fn) | 
 | 300 |  | 
| Greg Ward | 14c8d05 | 2000-06-08 01:22:48 +0000 | [diff] [blame] | 301 |         optional = ['test/test*.py', 'setup.cfg'] | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 302 |         for pattern in optional: | 
 | 303 |             files = filter (os.path.isfile, glob (pattern)) | 
 | 304 |             if files: | 
 | 305 |                 self.files.extend (files) | 
 | 306 |  | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 307 |         if self.distribution.has_pure_modules(): | 
| Greg Ward | 4fb29e5 | 2000-05-27 17:27:23 +0000 | [diff] [blame] | 308 |             build_py = self.get_finalized_command ('build_py') | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 309 |             self.files.extend (build_py.get_source_files ()) | 
 | 310 |  | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 311 |         if self.distribution.has_ext_modules(): | 
| Greg Ward | 4fb29e5 | 2000-05-27 17:27:23 +0000 | [diff] [blame] | 312 |             build_ext = self.get_finalized_command ('build_ext') | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 313 |             self.files.extend (build_ext.get_source_files ()) | 
 | 314 |  | 
| Greg Ward | 60908f1 | 2000-04-09 03:51:40 +0000 | [diff] [blame] | 315 |         if self.distribution.has_c_libraries(): | 
| Greg Ward | 4fb29e5 | 2000-05-27 17:27:23 +0000 | [diff] [blame] | 316 |             build_clib = self.get_finalized_command ('build_clib') | 
| Greg Ward | 60908f1 | 2000-04-09 03:51:40 +0000 | [diff] [blame] | 317 |             self.files.extend (build_clib.get_source_files ()) | 
 | 318 |  | 
| Greg Ward | 4a7319c | 2000-06-08 00:52:52 +0000 | [diff] [blame] | 319 |     # add_defaults () | 
 | 320 |      | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 321 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 322 |     def search_dir (self, dir, pattern=None): | 
 | 323 |         """Recursively find files under 'dir' matching 'pattern' (a string | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 324 |         containing a Unix-style glob pattern).  If 'pattern' is None, find | 
 | 325 |         all files under 'dir'.  Return the list of found filenames. | 
 | 326 |         """ | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 327 |         allfiles = findall (dir) | 
 | 328 |         if pattern is None: | 
 | 329 |             return allfiles | 
 | 330 |  | 
 | 331 |         pattern_re = translate_pattern (pattern) | 
 | 332 |         files = [] | 
 | 333 |         for file in allfiles: | 
 | 334 |             if pattern_re.match (os.path.basename (file)): | 
 | 335 |                 files.append (file) | 
 | 336 |  | 
 | 337 |         return files | 
 | 338 |  | 
 | 339 |     # search_dir () | 
 | 340 |  | 
 | 341 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 342 |     def recursive_exclude_pattern (self, dir, pattern=None): | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 343 |         """Remove filenames from 'self.files' that are under 'dir' and | 
 | 344 |         whose basenames match 'pattern'. | 
 | 345 |         """ | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 346 |         self.debug_print("recursive_exclude_pattern: dir=%s, pattern=%s" % | 
 | 347 |                          (dir, pattern)) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 348 |         if pattern is None: | 
 | 349 |             pattern_re = None | 
 | 350 |         else: | 
 | 351 |             pattern_re = translate_pattern (pattern) | 
 | 352 |  | 
 | 353 |         for i in range (len (self.files)-1, -1, -1): | 
 | 354 |             (cur_dir, cur_base) = os.path.split (self.files[i]) | 
 | 355 |             if (cur_dir == dir and | 
 | 356 |                 (pattern_re is None or pattern_re.match (cur_base))): | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 357 |                 self.debug_print("removing %s" % self.files[i]) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 358 |                 del self.files[i] | 
 | 359 |  | 
 | 360 |  | 
 | 361 |     def read_template (self): | 
 | 362 |         """Read and parse the manifest template file named by | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 363 |         'self.template' (usually "MANIFEST.in").  Process all file | 
 | 364 |         specifications (include and exclude) in the manifest template and | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 365 |         update 'self.files' accordingly (filenames may be added to | 
 | 366 |         or removed from 'self.files' based on the manifest template). | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 367 |         """ | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 368 |         assert self.files is not None and type (self.files) is ListType | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 369 |         self.announce("reading manifest template '%s'" % self.template) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 370 |  | 
 | 371 |         template = TextFile (self.template, | 
 | 372 |                              strip_comments=1, | 
 | 373 |                              skip_blanks=1, | 
 | 374 |                              join_lines=1, | 
 | 375 |                              lstrip_ws=1, | 
 | 376 |                              rstrip_ws=1, | 
 | 377 |                              collapse_ws=1) | 
 | 378 |  | 
 | 379 |         all_files = findall () | 
 | 380 |  | 
 | 381 |         while 1: | 
 | 382 |  | 
 | 383 |             line = template.readline() | 
 | 384 |             if line is None:            # end of file | 
 | 385 |                 break | 
 | 386 |  | 
 | 387 |             words = string.split (line) | 
 | 388 |             action = words[0] | 
 | 389 |  | 
 | 390 |             # First, check that the right number of words are present | 
 | 391 |             # for the given action (which is the first word) | 
 | 392 |             if action in ('include','exclude', | 
 | 393 |                           'global-include','global-exclude'): | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 394 |                 if len (words) < 2: | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 395 |                     template.warn \ | 
 | 396 |                         ("invalid manifest template line: " + | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 397 |                          "'%s' expects <pattern1> <pattern2> ..." % | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 398 |                          action) | 
 | 399 |                     continue | 
 | 400 |  | 
| Greg Ward | d8dfb4c | 2000-05-31 02:32:10 +0000 | [diff] [blame] | 401 |                 pattern_list = map(convert_path, words[1:]) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 402 |  | 
 | 403 |             elif action in ('recursive-include','recursive-exclude'): | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 404 |                 if len (words) < 3: | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 405 |                     template.warn \ | 
 | 406 |                         ("invalid manifest template line: " + | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 407 |                          "'%s' expects <dir> <pattern1> <pattern2> ..." % | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 408 |                          action) | 
 | 409 |                     continue | 
 | 410 |  | 
| Greg Ward | d8dfb4c | 2000-05-31 02:32:10 +0000 | [diff] [blame] | 411 |                 dir = convert_path(words[1]) | 
 | 412 |                 pattern_list = map (convert_path, words[2:]) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 413 |  | 
 | 414 |             elif action in ('graft','prune'): | 
 | 415 |                 if len (words) != 2: | 
 | 416 |                     template.warn \ | 
 | 417 |                         ("invalid manifest template line: " + | 
 | 418 |                          "'%s' expects a single <dir_pattern>" % | 
 | 419 |                          action) | 
 | 420 |                     continue | 
 | 421 |  | 
| Greg Ward | d8dfb4c | 2000-05-31 02:32:10 +0000 | [diff] [blame] | 422 |                 dir_pattern = convert_path (words[1]) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 423 |  | 
 | 424 |             else: | 
 | 425 |                 template.warn ("invalid manifest template line: " + | 
 | 426 |                                "unknown action '%s'" % action) | 
 | 427 |                 continue | 
 | 428 |  | 
 | 429 |             # OK, now we know that the action is valid and we have the | 
 | 430 |             # right number of words on the line for that action -- so we | 
 | 431 |             # can proceed with minimal error-checking.  Also, we have | 
| Greg Ward | 2b9e43f | 2000-04-14 00:49:30 +0000 | [diff] [blame] | 432 |             # defined either (pattern), (dir and pattern), or | 
 | 433 |             # (dir_pattern) -- so we don't have to spend any time | 
 | 434 |             # digging stuff up out of 'words'. | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 435 |  | 
 | 436 |             if action == 'include': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 437 |                 self.debug_print("include " + string.join(pattern_list)) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 438 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 439 |                     files = self.select_pattern (all_files, pattern, anchor=1) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 440 |                     if not files: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 441 |                         template.warn ("no files found matching '%s'" % | 
 | 442 |                                        pattern) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 443 |                     else: | 
 | 444 |                         self.files.extend (files) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 445 |  | 
 | 446 |             elif action == 'exclude': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 447 |                 self.debug_print("exclude " + string.join(pattern_list)) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 448 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 449 |                     num = self.exclude_pattern (self.files, pattern, anchor=1) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 450 |                     if num == 0: | 
 | 451 |                         template.warn ( | 
 | 452 |                             "no previously-included files found matching '%s'"% | 
 | 453 |                             pattern) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 454 |  | 
 | 455 |             elif action == 'global-include': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 456 |                 self.debug_print("global-include " + string.join(pattern_list)) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 457 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 458 |                     files = self.select_pattern (all_files, pattern, anchor=0) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 459 |                     if not files: | 
 | 460 |                         template.warn (("no files found matching '%s' " + | 
 | 461 |                                         "anywhere in distribution") % | 
 | 462 |                                        pattern) | 
 | 463 |                     else: | 
 | 464 |                         self.files.extend (files) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 465 |  | 
 | 466 |             elif action == 'global-exclude': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 467 |                 self.debug_print("global-exclude " + string.join(pattern_list)) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 468 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 469 |                     num = self.exclude_pattern (self.files, pattern, anchor=0) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 470 |                     if num == 0: | 
 | 471 |                         template.warn \ | 
 | 472 |                             (("no previously-included files matching '%s' " + | 
 | 473 |                               "found anywhere in distribution") % | 
 | 474 |                              pattern) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 475 |  | 
 | 476 |             elif action == 'recursive-include': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 477 |                 self.debug_print("recursive-include %s %s" % | 
 | 478 |                                  (dir, string.join(pattern_list))) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 479 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 480 |                     files = self.select_pattern ( | 
 | 481 |                         all_files, pattern, prefix=dir) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 482 |                     if not files: | 
 | 483 |                         template.warn (("no files found matching '%s' " + | 
 | 484 |                                         "under directory '%s'") % | 
 | 485 |                                        (pattern, dir)) | 
 | 486 |                     else: | 
 | 487 |                         self.files.extend (files) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 488 |  | 
 | 489 |             elif action == 'recursive-exclude': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 490 |                 self.debug_print("recursive-exclude %s %s" % | 
 | 491 |                                  (dir, string.join(pattern_list))) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 492 |                 for pattern in pattern_list: | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 493 |                     num = self.exclude_pattern( | 
 | 494 |                         self.files, pattern, prefix=dir) | 
| Greg Ward | 9d5afa9 | 2000-04-21 04:31:10 +0000 | [diff] [blame] | 495 |                     if num == 0: | 
 | 496 |                         template.warn \ | 
 | 497 |                             (("no previously-included files matching '%s' " + | 
 | 498 |                               "found under directory '%s'") % | 
 | 499 |                              (pattern, dir)) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 500 |  | 
 | 501 |             elif action == 'graft': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 502 |                 self.debug_print("graft " + dir_pattern) | 
 | 503 |                 files = self.select_pattern( | 
 | 504 |                     all_files, None, prefix=dir_pattern) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 505 |                 if not files: | 
 | 506 |                     template.warn ("no directories found matching '%s'" % | 
 | 507 |                                    dir_pattern) | 
 | 508 |                 else: | 
 | 509 |                     self.files.extend (files) | 
 | 510 |  | 
 | 511 |             elif action == 'prune': | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 512 |                 self.debug_print("prune " + dir_pattern) | 
 | 513 |                 num = self.exclude_pattern( | 
 | 514 |                     self.files, None, prefix=dir_pattern) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 515 |                 if num == 0: | 
 | 516 |                     template.warn \ | 
 | 517 |                         (("no previously-included directories found " + | 
 | 518 |                           "matching '%s'") % | 
 | 519 |                          dir_pattern) | 
 | 520 |             else: | 
 | 521 |                 raise RuntimeError, \ | 
 | 522 |                       "this cannot happen: invalid action '%s'" % action | 
 | 523 |  | 
 | 524 |         # while loop over lines of template file | 
 | 525 |  | 
 | 526 |     # read_template () | 
 | 527 |  | 
 | 528 |  | 
| Greg Ward | ce15c6c | 2000-06-08 01:06:02 +0000 | [diff] [blame] | 529 |     def prune_file_list (self): | 
 | 530 |         """Prune off branches that might slip into the file list as created | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 531 |         by 'read_template()', but really don't belong there: | 
 | 532 |           * the build tree (typically "build") | 
 | 533 |           * the release tree itself (only an issue if we ran "sdist" | 
 | 534 |             previously with --keep-tree, or it aborted) | 
 | 535 |           * any RCS or CVS directories | 
| Greg Ward | ce15c6c | 2000-06-08 01:06:02 +0000 | [diff] [blame] | 536 |         """ | 
 | 537 |         build = self.get_finalized_command('build') | 
 | 538 |         base_dir = self.distribution.get_fullname() | 
 | 539 |         self.exclude_pattern (self.files, None, prefix=build.build_base) | 
 | 540 |         self.exclude_pattern (self.files, None, prefix=base_dir) | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 541 |         self.exclude_pattern (self.files, r'/(RCS|CVS)/.*', is_regex=1) | 
| Greg Ward | ce15c6c | 2000-06-08 01:06:02 +0000 | [diff] [blame] | 542 |  | 
 | 543 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 544 |     def select_pattern (self, files, pattern, | 
 | 545 |                         anchor=1, prefix=None, is_regex=0): | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 546 |         """Select strings (presumably filenames) from 'files' that match | 
 | 547 |         'pattern', a Unix-style wildcard (glob) pattern.  Patterns are not | 
 | 548 |         quite the same as implemented by the 'fnmatch' module: '*' and '?' | 
 | 549 |         match non-special characters, where "special" is platform-dependent: | 
 | 550 |         slash on Unix, colon, slash, and backslash on DOS/Windows, and colon on | 
 | 551 |         Mac OS. | 
 | 552 |  | 
 | 553 |         If 'anchor' is true (the default), then the pattern match is more | 
 | 554 |         stringent: "*.py" will match "foo.py" but not "foo/bar.py".  If | 
 | 555 |         'anchor' is false, both of these will match. | 
 | 556 |  | 
 | 557 |         If 'prefix' is supplied, then only filenames starting with 'prefix' | 
 | 558 |         (itself a pattern) and ending with 'pattern', with anything in between | 
 | 559 |         them, will match.  'anchor' is ignored in this case. | 
 | 560 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 561 |         If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and | 
 | 562 |         'pattern' is assumed to be either a string containing a regex or a | 
 | 563 |         regex object -- no translation is done, the regex is just compiled | 
 | 564 |         and used as-is. | 
 | 565 |  | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 566 |         Return the list of matching strings, possibly empty. | 
 | 567 |         """ | 
 | 568 |         matches = [] | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 569 |         pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 570 |         self.debug_print("select_pattern: applying regex r'%s'" % | 
 | 571 |                          pattern_re.pattern) | 
 | 572 |         for name in files: | 
 | 573 |             if pattern_re.search (name): | 
 | 574 |                 matches.append (name) | 
 | 575 |                 self.debug_print(" adding " + name) | 
 | 576 |  | 
 | 577 |         return matches | 
 | 578 |  | 
 | 579 |     # select_pattern () | 
 | 580 |  | 
 | 581 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 582 |     def exclude_pattern (self, files, pattern, | 
 | 583 |                          anchor=1, prefix=None, is_regex=0): | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 584 |         """Remove strings (presumably filenames) from 'files' that match | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 585 |         'pattern'.  Other parameters are the same as for | 
 | 586 |         'select_pattern()', above.  The list 'files' is modified in place. | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 587 |         """ | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 588 |  | 
 | 589 |         pattern_re = translate_pattern (pattern, anchor, prefix, is_regex) | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 590 |         self.debug_print("exclude_pattern: applying regex r'%s'" % | 
 | 591 |                          pattern_re.pattern) | 
 | 592 |         for i in range (len(files)-1, -1, -1): | 
 | 593 |             if pattern_re.search (files[i]): | 
 | 594 |                 self.debug_print(" removing " + files[i]) | 
 | 595 |                 del files[i] | 
 | 596 |  | 
 | 597 |     # exclude_pattern () | 
 | 598 |  | 
 | 599 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 600 |     def write_manifest (self): | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 601 |         """Write the file list in 'self.files' (presumably as filled in by | 
| Greg Ward | 4a7319c | 2000-06-08 00:52:52 +0000 | [diff] [blame] | 602 |         'add_defaults()' and 'read_template()') to the manifest file named | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 603 |         by 'self.manifest'. | 
 | 604 |         """ | 
| Greg Ward | 1b8e1d4 | 2000-04-26 01:12:40 +0000 | [diff] [blame] | 605 |         self.execute(write_file, | 
 | 606 |                      (self.manifest, self.files), | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 607 |                      "writing manifest file '%s'" % self.manifest) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 608 |  | 
 | 609 |     # write_manifest () | 
 | 610 |  | 
 | 611 |  | 
 | 612 |     def read_manifest (self): | 
| Greg Ward | e0c8c2f | 2000-06-08 00:24:01 +0000 | [diff] [blame] | 613 |         """Read the manifest file (named by 'self.manifest') and use it to | 
 | 614 |         fill in 'self.files', the list of files to include in the source | 
 | 615 |         distribution. | 
 | 616 |         """ | 
| Greg Ward | f8b9e20 | 2000-06-08 00:08:14 +0000 | [diff] [blame] | 617 |         self.announce("reading manifest file '%s'" % self.manifest) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 618 |         manifest = open (self.manifest) | 
 | 619 |         while 1: | 
 | 620 |             line = manifest.readline () | 
 | 621 |             if line == '':              # end of file | 
 | 622 |                 break | 
 | 623 |             if line[-1] == '\n': | 
 | 624 |                 line = line[0:-1] | 
 | 625 |             self.files.append (line) | 
 | 626 |  | 
 | 627 |     # read_manifest () | 
 | 628 |              | 
 | 629 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 630 |     def make_release_tree (self, base_dir, files): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 631 |         """Create the directory tree that will become the source | 
 | 632 |         distribution archive.  All directories implied by the filenames in | 
 | 633 |         'files' are created under 'base_dir', and then we hard link or copy | 
 | 634 |         (if hard linking is unavailable) those files into place. | 
 | 635 |         Essentially, this duplicates the developer's source tree, but in a | 
 | 636 |         directory named after the distribution, containing only the files | 
 | 637 |         to be distributed. | 
 | 638 |         """ | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 639 |         # Create all the directories under 'base_dir' necessary to | 
 | 640 |         # put 'files' there. | 
 | 641 |         create_tree (base_dir, files, | 
 | 642 |                      verbose=self.verbose, dry_run=self.dry_run) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 643 |  | 
 | 644 |         # And walk over the list of files, either making a hard link (if | 
 | 645 |         # os.link exists) to each one that doesn't already exist in its | 
 | 646 |         # corresponding location under 'base_dir', or copying each file | 
 | 647 |         # that's out-of-date in 'base_dir'.  (Usually, all files will be | 
 | 648 |         # out-of-date, because by default we blow away 'base_dir' when | 
 | 649 |         # we're done making the distribution archives.) | 
 | 650 |      | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 651 |         if hasattr (os, 'link'):        # can make hard links on this system | 
 | 652 |             link = 'hard' | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 653 |             msg = "making hard links in %s..." % base_dir | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 654 |         else:                           # nope, have to copy | 
 | 655 |             link = None | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 656 |             msg = "copying files to %s..." % base_dir | 
 | 657 |  | 
 | 658 |         self.announce (msg) | 
 | 659 |         for file in files: | 
 | 660 |             dest = os.path.join (base_dir, file) | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 661 |             self.copy_file (file, dest, link=link) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 662 |  | 
 | 663 |     # make_release_tree () | 
 | 664 |  | 
 | 665 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 666 |     def make_distribution (self): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 667 |         """Create the source distribution(s).  First, we create the release | 
 | 668 |         tree with 'make_release_tree()'; then, we create all required | 
 | 669 |         archive files (according to 'self.formats') from the release tree. | 
 | 670 |         Finally, we clean up by blowing away the release tree (unless | 
 | 671 |         'self.keep_tree' is true).  The list of archive files created is | 
 | 672 |         stored so it can be retrieved later by 'get_archive_files()'. | 
 | 673 |         """ | 
| Greg Ward | 578c10d | 2000-03-31 02:50:04 +0000 | [diff] [blame] | 674 |         # Don't warn about missing meta-data here -- should be (and is!) | 
 | 675 |         # done elsewhere. | 
| Greg Ward | 0ae7f76 | 2000-04-22 02:51:25 +0000 | [diff] [blame] | 676 |         base_dir = self.distribution.get_fullname() | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 677 |         base_name = os.path.join(self.dist_dir, base_dir) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 678 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 679 |         self.make_release_tree (base_dir, self.files) | 
| Greg Ward | d87eb73 | 2000-06-01 01:10:56 +0000 | [diff] [blame] | 680 |         archive_files = []              # remember names of files we create | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 681 |         if self.dist_dir: | 
 | 682 |             self.mkpath(self.dist_dir) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 683 |         for fmt in self.formats: | 
| Greg Ward | c061410 | 2000-07-05 03:06:46 +0000 | [diff] [blame^] | 684 |             file = self.make_archive (base_name, fmt, base_dir=base_dir) | 
| Greg Ward | d87eb73 | 2000-06-01 01:10:56 +0000 | [diff] [blame] | 685 |             archive_files.append(file) | 
 | 686 |  | 
 | 687 |         self.archive_files = archive_files | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 688 |  | 
 | 689 |         if not self.keep_tree: | 
| Greg Ward | 2dc139c | 2000-03-18 15:43:42 +0000 | [diff] [blame] | 690 |             remove_tree (base_dir, self.verbose, self.dry_run) | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 691 |  | 
| Greg Ward | d87eb73 | 2000-06-01 01:10:56 +0000 | [diff] [blame] | 692 |     def get_archive_files (self): | 
 | 693 |         """Return the list of archive files created when the command | 
 | 694 |         was run, or None if the command hasn't run yet. | 
 | 695 |         """ | 
 | 696 |         return self.archive_files | 
 | 697 |  | 
| Greg Ward | fcd974e | 2000-05-25 01:10:04 +0000 | [diff] [blame] | 698 | # class sdist | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 699 |  | 
 | 700 |  | 
 | 701 | # ---------------------------------------------------------------------- | 
 | 702 | # Utility functions | 
 | 703 |  | 
 | 704 | def findall (dir = os.curdir): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 705 |     """Find all files under 'dir' and return the list of full filenames | 
 | 706 |     (relative to 'dir'). | 
 | 707 |     """ | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 708 |     from stat import ST_MODE, S_ISREG, S_ISDIR, S_ISLNK | 
 | 709 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 710 |     list = [] | 
 | 711 |     stack = [dir] | 
 | 712 |     pop = stack.pop | 
 | 713 |     push = stack.append | 
 | 714 |  | 
 | 715 |     while stack: | 
 | 716 |         dir = pop() | 
 | 717 |         names = os.listdir (dir) | 
 | 718 |  | 
 | 719 |         for name in names: | 
 | 720 |             if dir != os.curdir:        # avoid the dreaded "./" syndrome | 
 | 721 |                 fullname = os.path.join (dir, name) | 
 | 722 |             else: | 
 | 723 |                 fullname = name | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 724 |  | 
 | 725 |             # Avoid excess stat calls -- just one will do, thank you! | 
 | 726 |             stat = os.stat(fullname) | 
 | 727 |             mode = stat[ST_MODE] | 
 | 728 |             if S_ISREG(mode): | 
 | 729 |                 list.append (fullname) | 
 | 730 |             elif S_ISDIR(mode) and not S_ISLNK(mode): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 731 |                 push (fullname) | 
 | 732 |  | 
 | 733 |     return list | 
 | 734 |  | 
 | 735 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 736 | def glob_to_re (pattern): | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 737 |     """Translate a shell-like glob pattern to a regular expression; return | 
 | 738 |     a string containing the regex.  Differs from 'fnmatch.translate()' in | 
 | 739 |     that '*' does not match "special characters" (which are | 
 | 740 |     platform-specific). | 
 | 741 |     """ | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 742 |     pattern_re = fnmatch.translate (pattern) | 
 | 743 |  | 
 | 744 |     # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which | 
 | 745 |     # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, | 
 | 746 |     # and by extension they shouldn't match such "special characters" under | 
 | 747 |     # any OS.  So change all non-escaped dots in the RE to match any | 
 | 748 |     # character except the special characters. | 
 | 749 |     # XXX currently the "special characters" are just slash -- i.e. this is | 
 | 750 |     # Unix-only. | 
 | 751 |     pattern_re = re.sub (r'(^|[^\\])\.', r'\1[^/]', pattern_re) | 
 | 752 |     return pattern_re | 
 | 753 |  | 
 | 754 | # glob_to_re () | 
 | 755 |  | 
 | 756 |  | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 757 | def translate_pattern (pattern, anchor=1, prefix=None, is_regex=0): | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 758 |     """Translate a shell-like wildcard pattern to a compiled regular | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 759 |     expression.  Return the compiled regex.  If 'is_regex' true, | 
 | 760 |     then 'pattern' is directly compiled to a regex (if it's a string) | 
 | 761 |     or just returned as-is (assumes it's a regex object). | 
| Greg Ward | c3c8c6e | 2000-06-08 00:46:45 +0000 | [diff] [blame] | 762 |     """ | 
| Greg Ward | 499822d | 2000-06-29 02:06:29 +0000 | [diff] [blame] | 763 |     if is_regex: | 
 | 764 |         if type(pattern) is StringType: | 
 | 765 |             return re.compile(pattern) | 
 | 766 |         else: | 
 | 767 |             return pattern | 
 | 768 |  | 
| Greg Ward | a82122b | 2000-02-17 23:56:15 +0000 | [diff] [blame] | 769 |     if pattern: | 
 | 770 |         pattern_re = glob_to_re (pattern) | 
 | 771 |     else: | 
 | 772 |         pattern_re = '' | 
 | 773 |          | 
 | 774 |     if prefix is not None: | 
 | 775 |         prefix_re = (glob_to_re (prefix))[0:-1] # ditch trailing $ | 
 | 776 |         pattern_re = "^" + os.path.join (prefix_re, ".*" + pattern_re) | 
 | 777 |     else:                               # no prefix -- respect anchor flag | 
 | 778 |         if anchor: | 
 | 779 |             pattern_re = "^" + pattern_re | 
 | 780 |          | 
 | 781 |     return re.compile (pattern_re) | 
 | 782 |  | 
 | 783 | # translate_pattern () |