python-2.5.2/win32/Lib/distutils/command/sdist.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     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 38697 2005-03-23 18:54:36Z loewis $"
       
     8 
       
     9 import sys, 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 and .svn 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)/.*', 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 
       
   387     # read_manifest ()
       
   388 
       
   389 
       
   390     def make_release_tree (self, base_dir, files):
       
   391         """Create the directory tree that will become the source
       
   392         distribution archive.  All directories implied by the filenames in
       
   393         'files' are created under 'base_dir', and then we hard link or copy
       
   394         (if hard linking is unavailable) those files into place.
       
   395         Essentially, this duplicates the developer's source tree, but in a
       
   396         directory named after the distribution, containing only the files
       
   397         to be distributed.
       
   398         """
       
   399         # Create all the directories under 'base_dir' necessary to
       
   400         # put 'files' there; the 'mkpath()' is just so we don't die
       
   401         # if the manifest happens to be empty.
       
   402         self.mkpath(base_dir)
       
   403         dir_util.create_tree(base_dir, files, dry_run=self.dry_run)
       
   404 
       
   405         # And walk over the list of files, either making a hard link (if
       
   406         # os.link exists) to each one that doesn't already exist in its
       
   407         # corresponding location under 'base_dir', or copying each file
       
   408         # that's out-of-date in 'base_dir'.  (Usually, all files will be
       
   409         # out-of-date, because by default we blow away 'base_dir' when
       
   410         # we're done making the distribution archives.)
       
   411 
       
   412         if hasattr(os, 'link'):        # can make hard links on this system
       
   413             link = 'hard'
       
   414             msg = "making hard links in %s..." % base_dir
       
   415         else:                           # nope, have to copy
       
   416             link = None
       
   417             msg = "copying files to %s..." % base_dir
       
   418 
       
   419         if not files:
       
   420             log.warn("no files to distribute -- empty manifest?")
       
   421         else:
       
   422             log.info(msg)
       
   423         for file in files:
       
   424             if not os.path.isfile(file):
       
   425                 log.warn("'%s' not a regular file -- skipping" % file)
       
   426             else:
       
   427                 dest = os.path.join(base_dir, file)
       
   428                 self.copy_file(file, dest, link=link)
       
   429 
       
   430         self.distribution.metadata.write_pkg_info(base_dir)
       
   431 
       
   432     # make_release_tree ()
       
   433 
       
   434     def make_distribution (self):
       
   435         """Create the source distribution(s).  First, we create the release
       
   436         tree with 'make_release_tree()'; then, we create all required
       
   437         archive files (according to 'self.formats') from the release tree.
       
   438         Finally, we clean up by blowing away the release tree (unless
       
   439         'self.keep_temp' is true).  The list of archive files created is
       
   440         stored so it can be retrieved later by 'get_archive_files()'.
       
   441         """
       
   442         # Don't warn about missing meta-data here -- should be (and is!)
       
   443         # done elsewhere.
       
   444         base_dir = self.distribution.get_fullname()
       
   445         base_name = os.path.join(self.dist_dir, base_dir)
       
   446 
       
   447         self.make_release_tree(base_dir, self.filelist.files)
       
   448         archive_files = []              # remember names of files we create
       
   449         for fmt in self.formats:
       
   450             file = self.make_archive(base_name, fmt, base_dir=base_dir)
       
   451             archive_files.append(file)
       
   452             self.distribution.dist_files.append(('sdist', '', file))
       
   453 
       
   454         self.archive_files = archive_files
       
   455 
       
   456         if not self.keep_temp:
       
   457             dir_util.remove_tree(base_dir, dry_run=self.dry_run)
       
   458 
       
   459     def get_archive_files (self):
       
   460         """Return the list of archive files created when the command
       
   461         was run, or None if the command hasn't run yet.
       
   462         """
       
   463         return self.archive_files
       
   464 
       
   465 # class sdist