mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 05:01:30 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			245 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
	
		
			7.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """distutils.archive_util
 | |
| 
 | |
| Utility functions for creating archive files (tarballs, zip files,
 | |
| that sort of thing)."""
 | |
| 
 | |
| import os
 | |
| from warnings import warn
 | |
| import sys
 | |
| 
 | |
| try:
 | |
|     import zipfile
 | |
| except ImportError:
 | |
|     zipfile = None
 | |
| 
 | |
| 
 | |
| from distutils.errors import DistutilsExecError
 | |
| from distutils.spawn import spawn
 | |
| from distutils.dir_util import mkpath
 | |
| from distutils import log
 | |
| 
 | |
| try:
 | |
|     from pwd import getpwnam
 | |
| except ImportError:
 | |
|     getpwnam = None
 | |
| 
 | |
| try:
 | |
|     from grp import getgrnam
 | |
| except ImportError:
 | |
|     getgrnam = None
 | |
| 
 | |
| def _get_gid(name):
 | |
|     """Returns a gid, given a group name."""
 | |
|     if getgrnam is None or name is None:
 | |
|         return None
 | |
|     try:
 | |
|         result = getgrnam(name)
 | |
|     except KeyError:
 | |
|         result = None
 | |
|     if result is not None:
 | |
|         return result[2]
 | |
|     return None
 | |
| 
 | |
| def _get_uid(name):
 | |
|     """Returns an uid, given a user name."""
 | |
|     if getpwnam is None or name is None:
 | |
|         return None
 | |
|     try:
 | |
|         result = getpwnam(name)
 | |
|     except KeyError:
 | |
|         result = None
 | |
|     if result is not None:
 | |
|         return result[2]
 | |
|     return None
 | |
| 
 | |
| def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
 | |
|                  owner=None, group=None):
 | |
|     """Create a (possibly compressed) tar file from all the files under
 | |
|     'base_dir'.
 | |
| 
 | |
|     'compress' must be "gzip" (the default), "compress", "bzip2", or None.
 | |
|     (compress will be deprecated in Python 3.2)
 | |
| 
 | |
|     'owner' and 'group' can be used to define an owner and a group for the
 | |
|     archive that is being built. If not provided, the current owner and group
 | |
|     will be used.
 | |
| 
 | |
|     The output tar file will be named 'base_dir' +  ".tar", possibly plus
 | |
|     the appropriate compression extension (".gz", ".bz2" or ".Z").
 | |
| 
 | |
|     Returns the output filename.
 | |
|     """
 | |
|     tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
 | |
|     compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'}
 | |
| 
 | |
|     # flags for compression program, each element of list will be an argument
 | |
|     if compress is not None and compress not in compress_ext.keys():
 | |
|         raise ValueError(
 | |
|               "bad value for 'compress': must be None, 'gzip', 'bzip2' "
 | |
|               "or 'compress'")
 | |
| 
 | |
|     archive_name = base_name + '.tar'
 | |
|     if compress != 'compress':
 | |
|         archive_name += compress_ext.get(compress, '')
 | |
| 
 | |
|     mkpath(os.path.dirname(archive_name), dry_run=dry_run)
 | |
| 
 | |
|     # creating the tarball
 | |
|     import tarfile  # late import so Python build itself doesn't break
 | |
| 
 | |
|     log.info('Creating tar archive')
 | |
| 
 | |
|     uid = _get_uid(owner)
 | |
|     gid = _get_gid(group)
 | |
| 
 | |
|     def _set_uid_gid(tarinfo):
 | |
|         if gid is not None:
 | |
|             tarinfo.gid = gid
 | |
|             tarinfo.gname = group
 | |
|         if uid is not None:
 | |
|             tarinfo.uid = uid
 | |
|             tarinfo.uname = owner
 | |
|         return tarinfo
 | |
| 
 | |
|     if not dry_run:
 | |
|         tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
 | |
|         try:
 | |
|             tar.add(base_dir, filter=_set_uid_gid)
 | |
|         finally:
 | |
|             tar.close()
 | |
| 
 | |
|     # compression using `compress`
 | |
|     if compress == 'compress':
 | |
|         warn("'compress' will be deprecated.", PendingDeprecationWarning)
 | |
|         # the option varies depending on the platform
 | |
|         compressed_name = archive_name + compress_ext[compress]
 | |
|         if sys.platform == 'win32':
 | |
|             cmd = [compress, archive_name, compressed_name]
 | |
|         else:
 | |
|             cmd = [compress, '-f', archive_name]
 | |
|         spawn(cmd, dry_run=dry_run)
 | |
|         return compressed_name
 | |
| 
 | |
|     return archive_name
 | |
| 
 | |
| def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
 | |
|     """Create a zip file from all the files under 'base_dir'.
 | |
| 
 | |
|     The output zip file will be named 'base_name' + ".zip".  Uses either the
 | |
|     "zipfile" Python module (if available) or the InfoZIP "zip" utility
 | |
|     (if installed and found on the default search path).  If neither tool is
 | |
|     available, raises DistutilsExecError.  Returns the name of the output zip
 | |
|     file.
 | |
|     """
 | |
|     zip_filename = base_name + ".zip"
 | |
|     mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
 | |
| 
 | |
|     # If zipfile module is not available, try spawning an external
 | |
|     # 'zip' command.
 | |
|     if zipfile is None:
 | |
|         if verbose:
 | |
|             zipoptions = "-r"
 | |
|         else:
 | |
|             zipoptions = "-rq"
 | |
| 
 | |
|         try:
 | |
|             spawn(["zip", zipoptions, zip_filename, base_dir],
 | |
|                   dry_run=dry_run)
 | |
|         except DistutilsExecError:
 | |
|             # XXX really should distinguish between "couldn't find
 | |
|             # external 'zip' command" and "zip failed".
 | |
|             raise DistutilsExecError(("unable to create zip file '%s': "
 | |
|                    "could neither import the 'zipfile' module nor "
 | |
|                    "find a standalone zip utility") % zip_filename)
 | |
| 
 | |
|     else:
 | |
|         log.info("creating '%s' and adding '%s' to it",
 | |
|                  zip_filename, base_dir)
 | |
| 
 | |
|         if not dry_run:
 | |
|             try:
 | |
|                 zip = zipfile.ZipFile(zip_filename, "w",
 | |
|                                       compression=zipfile.ZIP_DEFLATED)
 | |
|             except RuntimeError:
 | |
|                 zip = zipfile.ZipFile(zip_filename, "w",
 | |
|                                       compression=zipfile.ZIP_STORED)
 | |
| 
 | |
|             for dirpath, dirnames, filenames in os.walk(base_dir):
 | |
|                 for name in filenames:
 | |
|                     path = os.path.normpath(os.path.join(dirpath, name))
 | |
|                     if os.path.isfile(path):
 | |
|                         zip.write(path, path)
 | |
|                         log.info("adding '%s'" % path)
 | |
|             zip.close()
 | |
| 
 | |
|     return zip_filename
 | |
| 
 | |
| ARCHIVE_FORMATS = {
 | |
|     'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
 | |
|     'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
 | |
|     'ztar':  (make_tarball, [('compress', 'compress')], "compressed tar file"),
 | |
|     'tar':   (make_tarball, [('compress', None)], "uncompressed tar file"),
 | |
|     'zip':   (make_zipfile, [],"ZIP file")
 | |
|     }
 | |
| 
 | |
| def check_archive_formats(formats):
 | |
|     """Returns the first format from the 'format' list that is unknown.
 | |
| 
 | |
|     If all formats are known, returns None
 | |
|     """
 | |
|     for format in formats:
 | |
|         if format not in ARCHIVE_FORMATS:
 | |
|             return format
 | |
|     return None
 | |
| 
 | |
| def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
 | |
|                  dry_run=0, owner=None, group=None):
 | |
|     """Create an archive file (eg. zip or tar).
 | |
| 
 | |
|     'base_name' is the name of the file to create, minus any format-specific
 | |
|     extension; 'format' is the archive format: one of "zip", "tar", "ztar",
 | |
|     or "gztar".
 | |
| 
 | |
|     'root_dir' is a directory that will be the root directory of the
 | |
|     archive; ie. we typically chdir into 'root_dir' before creating the
 | |
|     archive.  'base_dir' is the directory where we start archiving from;
 | |
|     ie. 'base_dir' will be the common prefix of all files and
 | |
|     directories in the archive.  'root_dir' and 'base_dir' both default
 | |
|     to the current directory.  Returns the name of the archive file.
 | |
| 
 | |
|     'owner' and 'group' are used when creating a tar archive. By default,
 | |
|     uses the current owner and group.
 | |
|     """
 | |
|     save_cwd = os.getcwd()
 | |
|     if root_dir is not None:
 | |
|         log.debug("changing into '%s'", root_dir)
 | |
|         base_name = os.path.abspath(base_name)
 | |
|         if not dry_run:
 | |
|             os.chdir(root_dir)
 | |
| 
 | |
|     if base_dir is None:
 | |
|         base_dir = os.curdir
 | |
| 
 | |
|     kwargs = {'dry_run': dry_run}
 | |
| 
 | |
|     try:
 | |
|         format_info = ARCHIVE_FORMATS[format]
 | |
|     except KeyError:
 | |
|         raise ValueError("unknown archive format '%s'" % format)
 | |
| 
 | |
|     func = format_info[0]
 | |
|     for arg, val in format_info[1]:
 | |
|         kwargs[arg] = val
 | |
| 
 | |
|     if format != 'zip':
 | |
|         kwargs['owner'] = owner
 | |
|         kwargs['group'] = group
 | |
| 
 | |
|     try:
 | |
|         filename = func(base_name, base_dir, **kwargs)
 | |
|     finally:
 | |
|         if root_dir is not None:
 | |
|             log.debug("changing back to '%s'", save_cwd)
 | |
|             os.chdir(save_cwd)
 | |
| 
 | |
|     return filename
 | 
