~malept/ubuntu/lucid/python2.6/dev-dependency-fix

« back to all changes in this revision

Viewing changes to Lib/distutils/command/sdist.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2009-02-13 12:51:00 UTC
  • Revision ID: james.westby@ubuntu.com-20090213125100-uufgcb9yeqzujpqw
Tags: upstream-2.6.1
ImportĀ upstreamĀ versionĀ 2.6.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""distutils.command.sdist
 
2
 
 
3
Implements the Distutils 'sdist' command (create a source distribution)."""
 
4
 
 
5
# This module should be kept compatible with Python 2.1.
 
6
 
 
7
__revision__ = "$Id: sdist.py 61263 2008-03-06 06:47:18Z georg.brandl $"
 
8
 
 
9
import os, string
 
10
from types import *
 
11
from glob import glob
 
12
from distutils.core import Command
 
13
from distutils import dir_util, dep_util, file_util, archive_util
 
14
from distutils.text_file import TextFile
 
15
from distutils.errors import *
 
16
from distutils.filelist import FileList
 
17
from distutils import log
 
18
 
 
19
 
 
20
def show_formats ():
 
21
    """Print all possible values for the 'formats' option (used by
 
22
    the "--help-formats" command-line option).
 
23
    """
 
24
    from distutils.fancy_getopt import FancyGetopt
 
25
    from distutils.archive_util import ARCHIVE_FORMATS
 
26
    formats=[]
 
27
    for format in ARCHIVE_FORMATS.keys():
 
28
        formats.append(("formats=" + format, None,
 
29
                        ARCHIVE_FORMATS[format][2]))
 
30
    formats.sort()
 
31
    pretty_printer = FancyGetopt(formats)
 
32
    pretty_printer.print_help(
 
33
        "List of available source distribution formats:")
 
34
 
 
35
class sdist (Command):
 
36
 
 
37
    description = "create a source distribution (tarball, zip file, etc.)"
 
38
 
 
39
    user_options = [
 
40
        ('template=', 't',
 
41
         "name of manifest template file [default: MANIFEST.in]"),
 
42
        ('manifest=', 'm',
 
43
         "name of manifest file [default: MANIFEST]"),
 
44
        ('use-defaults', None,
 
45
         "include the default file set in the manifest "
 
46
         "[default; disable with --no-defaults]"),
 
47
        ('no-defaults', None,
 
48
         "don't include the default file set"),
 
49
        ('prune', None,
 
50
         "specifically exclude files/directories that should not be "
 
51
         "distributed (build tree, RCS/CVS dirs, etc.) "
 
52
         "[default; disable with --no-prune]"),
 
53
        ('no-prune', None,
 
54
         "don't automatically exclude anything"),
 
55
        ('manifest-only', 'o',
 
56
         "just regenerate the manifest and then stop "
 
57
         "(implies --force-manifest)"),
 
58
        ('force-manifest', 'f',
 
59
         "forcibly regenerate the manifest and carry on as usual"),
 
60
        ('formats=', None,
 
61
         "formats for source distribution (comma-separated list)"),
 
62
        ('keep-temp', 'k',
 
63
         "keep the distribution tree around after creating " +
 
64
         "archive file(s)"),
 
65
        ('dist-dir=', 'd',
 
66
         "directory to put the source distribution archive(s) in "
 
67
         "[default: dist]"),
 
68
        ]
 
69
 
 
70
    boolean_options = ['use-defaults', 'prune',
 
71
                       'manifest-only', 'force-manifest',
 
72
                       'keep-temp']
 
73
 
 
74
    help_options = [
 
75
        ('help-formats', None,
 
76
         "list available distribution formats", show_formats),
 
77
        ]
 
78
 
 
79
    negative_opt = {'no-defaults': 'use-defaults',
 
80
                    'no-prune': 'prune' }
 
81
 
 
82
    default_format = { 'posix': 'gztar',
 
83
                       'nt': 'zip' }
 
84
 
 
85
    def initialize_options (self):
 
86
        # 'template' and 'manifest' are, respectively, the names of
 
87
        # the manifest template and manifest file.
 
88
        self.template = None
 
89
        self.manifest = None
 
90
 
 
91
        # 'use_defaults': if true, we will include the default file set
 
92
        # in the manifest
 
93
        self.use_defaults = 1
 
94
        self.prune = 1
 
95
 
 
96
        self.manifest_only = 0
 
97
        self.force_manifest = 0
 
98
 
 
99
        self.formats = None
 
100
        self.keep_temp = 0
 
101
        self.dist_dir = None
 
102
 
 
103
        self.archive_files = None
 
104
 
 
105
 
 
106
    def finalize_options (self):
 
107
        if self.manifest is None:
 
108
            self.manifest = "MANIFEST"
 
109
        if self.template is None:
 
110
            self.template = "MANIFEST.in"
 
111
 
 
112
        self.ensure_string_list('formats')
 
113
        if self.formats is None:
 
114
            try:
 
115
                self.formats = [self.default_format[os.name]]
 
116
            except KeyError:
 
117
                raise DistutilsPlatformError, \
 
118
                      "don't know how to create source distributions " + \
 
119
                      "on platform %s" % os.name
 
120
 
 
121
        bad_format = archive_util.check_archive_formats(self.formats)
 
122
        if bad_format:
 
123
            raise DistutilsOptionError, \
 
124
                  "unknown archive format '%s'" % bad_format
 
125
 
 
126
        if self.dist_dir is None:
 
127
            self.dist_dir = "dist"
 
128
 
 
129
 
 
130
    def run (self):
 
131
 
 
132
        # 'filelist' contains the list of files that will make up the
 
133
        # manifest
 
134
        self.filelist = FileList()
 
135
 
 
136
        # Ensure that all required meta-data is given; warn if not (but
 
137
        # don't die, it's not *that* serious!)
 
138
        self.check_metadata()
 
139
 
 
140
        # Do whatever it takes to get the list of files to process
 
141
        # (process the manifest template, read an existing manifest,
 
142
        # whatever).  File list is accumulated in 'self.filelist'.
 
143
        self.get_file_list()
 
144
 
 
145
        # If user just wanted us to regenerate the manifest, stop now.
 
146
        if self.manifest_only:
 
147
            return
 
148
 
 
149
        # Otherwise, go ahead and create the source distribution tarball,
 
150
        # or zipfile, or whatever.
 
151
        self.make_distribution()
 
152
 
 
153
 
 
154
    def check_metadata (self):
 
155
        """Ensure that all required elements of meta-data (name, version,
 
156
        URL, (author and author_email) or (maintainer and
 
157
        maintainer_email)) are supplied by the Distribution object; warn if
 
158
        any are missing.
 
159
        """
 
160
        metadata = self.distribution.metadata
 
161
 
 
162
        missing = []
 
163
        for attr in ('name', 'version', 'url'):
 
164
            if not (hasattr(metadata, attr) and getattr(metadata, attr)):
 
165
                missing.append(attr)
 
166
 
 
167
        if missing:
 
168
            self.warn("missing required meta-data: " +
 
169
                      string.join(missing, ", "))
 
170
 
 
171
        if metadata.author:
 
172
            if not metadata.author_email:
 
173
                self.warn("missing meta-data: if 'author' supplied, " +
 
174
                          "'author_email' must be supplied too")
 
175
        elif metadata.maintainer:
 
176
            if not metadata.maintainer_email:
 
177
                self.warn("missing meta-data: if 'maintainer' supplied, " +
 
178
                          "'maintainer_email' must be supplied too")
 
179
        else:
 
180
            self.warn("missing meta-data: either (author and author_email) " +
 
181
                      "or (maintainer and maintainer_email) " +
 
182
                      "must be supplied")
 
183
 
 
184
    # check_metadata ()
 
185
 
 
186
 
 
187
    def get_file_list (self):
 
188
        """Figure out the list of files to include in the source
 
189
        distribution, and put it in 'self.filelist'.  This might involve
 
190
        reading the manifest template (and writing the manifest), or just
 
191
        reading the manifest, or just using the default file set -- it all
 
192
        depends on the user's options and the state of the filesystem.
 
193
        """
 
194
 
 
195
        # If we have a manifest template, see if it's newer than the
 
196
        # manifest; if so, we'll regenerate the manifest.
 
197
        template_exists = os.path.isfile(self.template)
 
198
        if template_exists:
 
199
            template_newer = dep_util.newer(self.template, self.manifest)
 
200
 
 
201
        # The contents of the manifest file almost certainly depend on the
 
202
        # setup script as well as the manifest template -- so if the setup
 
203
        # script is newer than the manifest, we'll regenerate the manifest
 
204
        # from the template.  (Well, not quite: if we already have a
 
205
        # manifest, but there's no template -- which will happen if the
 
206
        # developer elects to generate a manifest some other way -- then we
 
207
        # can't regenerate the manifest, so we don't.)
 
208
        self.debug_print("checking if %s newer than %s" %
 
209
                         (self.distribution.script_name, self.manifest))
 
210
        setup_newer = dep_util.newer(self.distribution.script_name,
 
211
                                     self.manifest)
 
212
 
 
213
        # cases:
 
214
        #   1) no manifest, template exists: generate manifest
 
215
        #      (covered by 2a: no manifest == template newer)
 
216
        #   2) manifest & template exist:
 
217
        #      2a) template or setup script newer than manifest:
 
218
        #          regenerate manifest
 
219
        #      2b) manifest newer than both:
 
220
        #          do nothing (unless --force or --manifest-only)
 
221
        #   3) manifest exists, no template:
 
222
        #      do nothing (unless --force or --manifest-only)
 
223
        #   4) no manifest, no template: generate w/ warning ("defaults only")
 
224
 
 
225
        manifest_outofdate = (template_exists and
 
226
                              (template_newer or setup_newer))
 
227
        force_regen = self.force_manifest or self.manifest_only
 
228
        manifest_exists = os.path.isfile(self.manifest)
 
229
        neither_exists = (not template_exists and not manifest_exists)
 
230
 
 
231
        # Regenerate the manifest if necessary (or if explicitly told to)
 
232
        if manifest_outofdate or neither_exists or force_regen:
 
233
            if not template_exists:
 
234
                self.warn(("manifest template '%s' does not exist " +
 
235
                           "(using default file list)") %
 
236
                          self.template)
 
237
            self.filelist.findall()
 
238
 
 
239
            if self.use_defaults:
 
240
                self.add_defaults()
 
241
            if template_exists:
 
242
                self.read_template()
 
243
            if self.prune:
 
244
                self.prune_file_list()
 
245
 
 
246
            self.filelist.sort()
 
247
            self.filelist.remove_duplicates()
 
248
            self.write_manifest()
 
249
 
 
250
        # Don't regenerate the manifest, just read it in.
 
251
        else:
 
252
            self.read_manifest()
 
253
 
 
254
    # get_file_list ()
 
255
 
 
256
 
 
257
    def add_defaults (self):
 
258
        """Add all the default files to self.filelist:
 
259
          - README or README.txt
 
260
          - setup.py
 
261
          - test/test*.py
 
262
          - all pure Python modules mentioned in setup script
 
263
          - all C sources listed as part of extensions or C libraries
 
264
            in the setup script (doesn't catch C headers!)
 
265
        Warns if (README or README.txt) or setup.py are missing; everything
 
266
        else is optional.
 
267
        """
 
268
 
 
269
        standards = [('README', 'README.txt'), self.distribution.script_name]
 
270
        for fn in standards:
 
271
            if type(fn) is TupleType:
 
272
                alts = fn
 
273
                got_it = 0
 
274
                for fn in alts:
 
275
                    if os.path.exists(fn):
 
276
                        got_it = 1
 
277
                        self.filelist.append(fn)
 
278
                        break
 
279
 
 
280
                if not got_it:
 
281
                    self.warn("standard file not found: should have one of " +
 
282
                              string.join(alts, ', '))
 
283
            else:
 
284
                if os.path.exists(fn):
 
285
                    self.filelist.append(fn)
 
286
                else:
 
287
                    self.warn("standard file '%s' not found" % fn)
 
288
 
 
289
        optional = ['test/test*.py', 'setup.cfg']
 
290
        for pattern in optional:
 
291
            files = filter(os.path.isfile, glob(pattern))
 
292
            if files:
 
293
                self.filelist.extend(files)
 
294
 
 
295
        if self.distribution.has_pure_modules():
 
296
            build_py = self.get_finalized_command('build_py')
 
297
            self.filelist.extend(build_py.get_source_files())
 
298
 
 
299
        if self.distribution.has_ext_modules():
 
300
            build_ext = self.get_finalized_command('build_ext')
 
301
            self.filelist.extend(build_ext.get_source_files())
 
302
 
 
303
        if self.distribution.has_c_libraries():
 
304
            build_clib = self.get_finalized_command('build_clib')
 
305
            self.filelist.extend(build_clib.get_source_files())
 
306
 
 
307
        if self.distribution.has_scripts():
 
308
            build_scripts = self.get_finalized_command('build_scripts')
 
309
            self.filelist.extend(build_scripts.get_source_files())
 
310
 
 
311
    # add_defaults ()
 
312
 
 
313
 
 
314
    def read_template (self):
 
315
        """Read and parse manifest template file named by self.template.
 
316
 
 
317
        (usually "MANIFEST.in") The parsing and processing is done by
 
318
        'self.filelist', which updates itself accordingly.
 
319
        """
 
320
        log.info("reading manifest template '%s'", self.template)
 
321
        template = TextFile(self.template,
 
322
                            strip_comments=1,
 
323
                            skip_blanks=1,
 
324
                            join_lines=1,
 
325
                            lstrip_ws=1,
 
326
                            rstrip_ws=1,
 
327
                            collapse_join=1)
 
328
 
 
329
        while 1:
 
330
            line = template.readline()
 
331
            if line is None:            # end of file
 
332
                break
 
333
 
 
334
            try:
 
335
                self.filelist.process_template_line(line)
 
336
            except DistutilsTemplateError, msg:
 
337
                self.warn("%s, line %d: %s" % (template.filename,
 
338
                                               template.current_line,
 
339
                                               msg))
 
340
 
 
341
    # read_template ()
 
342
 
 
343
 
 
344
    def prune_file_list (self):
 
345
        """Prune off branches that might slip into the file list as created
 
346
        by 'read_template()', but really don't belong there:
 
347
          * the build tree (typically "build")
 
348
          * the release tree itself (only an issue if we ran "sdist"
 
349
            previously with --keep-temp, or it aborted)
 
350
          * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories
 
351
        """
 
352
        build = self.get_finalized_command('build')
 
353
        base_dir = self.distribution.get_fullname()
 
354
 
 
355
        self.filelist.exclude_pattern(None, prefix=build.build_base)
 
356
        self.filelist.exclude_pattern(None, prefix=base_dir)
 
357
        self.filelist.exclude_pattern(r'(^|/)(RCS|CVS|\.svn|\.hg|\.git|\.bzr|_darcs)/.*', is_regex=1)
 
358
 
 
359
 
 
360
    def write_manifest (self):
 
361
        """Write the file list in 'self.filelist' (presumably as filled in
 
362
        by 'add_defaults()' and 'read_template()') to the manifest file
 
363
        named by 'self.manifest'.
 
364
        """
 
365
        self.execute(file_util.write_file,
 
366
                     (self.manifest, self.filelist.files),
 
367
                     "writing manifest file '%s'" % self.manifest)
 
368
 
 
369
    # write_manifest ()
 
370
 
 
371
 
 
372
    def read_manifest (self):
 
373
        """Read the manifest file (named by 'self.manifest') and use it to
 
374
        fill in 'self.filelist', the list of files to include in the source
 
375
        distribution.
 
376
        """
 
377
        log.info("reading manifest file '%s'", self.manifest)
 
378
        manifest = open(self.manifest)
 
379
        while 1:
 
380
            line = manifest.readline()
 
381
            if line == '':              # end of file
 
382
                break
 
383
            if line[-1] == '\n':
 
384
                line = line[0:-1]
 
385
            self.filelist.append(line)
 
386
        manifest.close()
 
387
 
 
388
    # read_manifest ()
 
389
 
 
390
 
 
391
    def make_release_tree (self, base_dir, files):
 
392
        """Create the directory tree that will become the source
 
393
        distribution archive.  All directories implied by the filenames in
 
394
        'files' are created under 'base_dir', and then we hard link or copy
 
395
        (if hard linking is unavailable) those files into place.
 
396
        Essentially, this duplicates the developer's source tree, but in a
 
397
        directory named after the distribution, containing only the files
 
398
        to be distributed.
 
399
        """
 
400
        # Create all the directories under 'base_dir' necessary to
 
401
        # put 'files' there; the 'mkpath()' is just so we don't die
 
402
        # if the manifest happens to be empty.
 
403
        self.mkpath(base_dir)
 
404
        dir_util.create_tree(base_dir, files, dry_run=self.dry_run)
 
405
 
 
406
        # And walk over the list of files, either making a hard link (if
 
407
        # os.link exists) to each one that doesn't already exist in its
 
408
        # corresponding location under 'base_dir', or copying each file
 
409
        # that's out-of-date in 'base_dir'.  (Usually, all files will be
 
410
        # out-of-date, because by default we blow away 'base_dir' when
 
411
        # we're done making the distribution archives.)
 
412
 
 
413
        if hasattr(os, 'link'):        # can make hard links on this system
 
414
            link = 'hard'
 
415
            msg = "making hard links in %s..." % base_dir
 
416
        else:                           # nope, have to copy
 
417
            link = None
 
418
            msg = "copying files to %s..." % base_dir
 
419
 
 
420
        if not files:
 
421
            log.warn("no files to distribute -- empty manifest?")
 
422
        else:
 
423
            log.info(msg)
 
424
        for file in files:
 
425
            if not os.path.isfile(file):
 
426
                log.warn("'%s' not a regular file -- skipping" % file)
 
427
            else:
 
428
                dest = os.path.join(base_dir, file)
 
429
                self.copy_file(file, dest, link=link)
 
430
 
 
431
        self.distribution.metadata.write_pkg_info(base_dir)
 
432
 
 
433
    # make_release_tree ()
 
434
 
 
435
    def make_distribution (self):
 
436
        """Create the source distribution(s).  First, we create the release
 
437
        tree with 'make_release_tree()'; then, we create all required
 
438
        archive files (according to 'self.formats') from the release tree.
 
439
        Finally, we clean up by blowing away the release tree (unless
 
440
        'self.keep_temp' is true).  The list of archive files created is
 
441
        stored so it can be retrieved later by 'get_archive_files()'.
 
442
        """
 
443
        # Don't warn about missing meta-data here -- should be (and is!)
 
444
        # done elsewhere.
 
445
        base_dir = self.distribution.get_fullname()
 
446
        base_name = os.path.join(self.dist_dir, base_dir)
 
447
 
 
448
        self.make_release_tree(base_dir, self.filelist.files)
 
449
        archive_files = []              # remember names of files we create
 
450
        for fmt in self.formats:
 
451
            file = self.make_archive(base_name, fmt, base_dir=base_dir)
 
452
            archive_files.append(file)
 
453
            self.distribution.dist_files.append(('sdist', '', file))
 
454
 
 
455
        self.archive_files = archive_files
 
456
 
 
457
        if not self.keep_temp:
 
458
            dir_util.remove_tree(base_dir, dry_run=self.dry_run)
 
459
 
 
460
    def get_archive_files (self):
 
461
        """Return the list of archive files created when the command
 
462
        was run, or None if the command hasn't run yet.
 
463
        """
 
464
        return self.archive_files
 
465
 
 
466
# class sdist