blob: 62150b0dddddb0b0e0377309b565627bd0861cad [file] [log] [blame]
Greg Wardaebf7062000-04-04 02:05:59 +00001"""distutils.archive_util
2
3Utility functions for creating archive files (tarballs, zip files,
4that sort of thing)."""
5
Greg Wardaebf7062000-04-04 02:05:59 +00006__revision__ = "$Id$"
7
8import os
Tarek Ziadé9e5d2dc2009-05-28 12:53:54 +00009from warnings import warn
10import sys
11
Greg Wardaebf7062000-04-04 02:05:59 +000012from distutils.errors import DistutilsExecError
13from distutils.spawn import spawn
Greg Ward04e25a12000-08-22 01:48:54 +000014from distutils.dir_util import mkpath
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000015from distutils import log
Greg Wardaebf7062000-04-04 02:05:59 +000016
Tarek Ziadé6f826ed2009-05-17 12:04:57 +000017def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +000018 """Create a (possibly compressed) tar file from all the files under
Tarek Ziadé6f826ed2009-05-17 12:04:57 +000019 'base_dir'.
20
21 'compress' must be "gzip" (the default), "compress", "bzip2", or None.
22 Both "tar" and the compression utility named by 'compress' must be on
23 the default program search path, so this is probably Unix-specific.
24 The output tar file will be named 'base_dir' + ".tar", possibly plus
25 the appropriate compression extension (".gz", ".bz2" or ".Z").
26 Returns the output filename.
Greg Wardca4289f2000-09-26 02:13:49 +000027 """
Tarek Ziadé9e5d2dc2009-05-28 12:53:54 +000028 tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
29 compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'}
Fred Drakeb94b8492001-12-06 20:51:35 +000030
Greg Wardf1948782000-04-25 01:38:20 +000031 # flags for compression program, each element of list will be an argument
Greg Wardf1948782000-04-25 01:38:20 +000032 if compress is not None and compress not in compress_ext.keys():
Greg Wardaebf7062000-04-04 02:05:59 +000033 raise ValueError, \
Tarek Ziadé9e5d2dc2009-05-28 12:53:54 +000034 ("bad value for 'compress': must be None, 'gzip', 'bzip2' "
35 "or 'compress'")
Greg Wardaebf7062000-04-04 02:05:59 +000036
Tarek Ziadé9e5d2dc2009-05-28 12:53:54 +000037 archive_name = base_name + '.tar'
38 if compress != 'compress':
39 archive_name += compress_ext.get(compress, '')
40
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000041 mkpath(os.path.dirname(archive_name), dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000042
Tarek Ziadé9e5d2dc2009-05-28 12:53:54 +000043 # creating the tarball
44 import tarfile # late import so Python build itself doesn't break
45
46 log.info('Creating tar archive')
47 if not dry_run:
48 tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
49 try:
50 tar.add(base_dir)
51 finally:
52 tar.close()
53
54 # compression using `compress`
55 if compress == 'compress':
56 warn("'compress' will be deprecated.", PendingDeprecationWarning)
57 # the option varies depending on the platform
58 compressed_name = archive_name + compress_ext[compress]
59 if sys.platform == 'win32':
60 cmd = [compress, archive_name, compressed_name]
61 else:
62 cmd = [compress, '-f', archive_name]
63 spawn(cmd, dry_run=dry_run)
64 return compressed_name
65
66 return archive_name
Greg Wardaebf7062000-04-04 02:05:59 +000067
Tarek Ziadé6f826ed2009-05-17 12:04:57 +000068def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
69 """Create a zip file from all the files under 'base_dir'.
Greg Wardaebf7062000-04-04 02:05:59 +000070
Tarek Ziadé6f826ed2009-05-17 12:04:57 +000071 The output zip file will be named 'base_dir' + ".zip". Uses either the
72 "zipfile" Python module (if available) or the InfoZIP "zip" utility
73 (if installed and found on the default search path). If neither tool is
74 available, raises DistutilsExecError. Returns the name of the output zip
75 file.
Greg Wardca4289f2000-09-26 02:13:49 +000076 """
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000077 try:
78 import zipfile
79 except ImportError:
80 zipfile = None
Tim Peters182b5ac2004-07-18 06:16:08 +000081
Greg Wardaebf7062000-04-04 02:05:59 +000082 zip_filename = base_name + ".zip"
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000083 mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000084
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000085 # If zipfile module is not available, try spawning an external
86 # 'zip' command.
87 if zipfile is None:
88 if verbose:
89 zipoptions = "-r"
90 else:
91 zipoptions = "-rq"
Tim Peters182b5ac2004-07-18 06:16:08 +000092
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000093 try:
94 spawn(["zip", zipoptions, zip_filename, base_dir],
95 dry_run=dry_run)
96 except DistutilsExecError:
97 # XXX really should distinguish between "couldn't find
98 # external 'zip' command" and "zip failed".
99 raise DistutilsExecError, \
100 ("unable to create zip file '%s': "
101 "could neither import the 'zipfile' module nor "
102 "find a standalone zip utility") % zip_filename
103
104 else:
105 log.info("creating '%s' and adding '%s' to it",
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000106 zip_filename, base_dir)
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +0000107
Greg Wardaebf7062000-04-04 02:05:59 +0000108 if not dry_run:
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000109 zip = zipfile.ZipFile(zip_filename, "w",
110 compression=zipfile.ZIP_DEFLATED)
Greg Wardaebf7062000-04-04 02:05:59 +0000111
Benjamin Peterson9ec4aa02008-05-08 22:09:54 +0000112 for dirpath, dirnames, filenames in os.walk(base_dir):
113 for name in filenames:
114 path = os.path.normpath(os.path.join(dirpath, name))
115 if os.path.isfile(path):
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000116 zip.write(path, path)
Benjamin Peterson9ec4aa02008-05-08 22:09:54 +0000117 log.info("adding '%s'" % path)
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000118 zip.close()
Greg Wardaebf7062000-04-04 02:05:59 +0000119
120 return zip_filename
121
Greg Warddb807542000-04-22 03:09:56 +0000122ARCHIVE_FORMATS = {
Greg Ward2ff78872000-06-24 00:23:20 +0000123 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
124 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
125 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
126 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
Greg Ward04e25a12000-08-22 01:48:54 +0000127 'zip': (make_zipfile, [],"ZIP file")
Greg Warddb807542000-04-22 03:09:56 +0000128 }
129
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000130def check_archive_formats(formats):
131 """Returns the first format from the 'format' list that is unknown.
132
133 If all formats are known, returns None
134 """
Greg Warddb807542000-04-22 03:09:56 +0000135 for format in formats:
Guido van Rossum8bc09652008-02-21 18:18:37 +0000136 if format not in ARCHIVE_FORMATS:
Greg Warddb807542000-04-22 03:09:56 +0000137 return format
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000138 return None
Greg Warddb807542000-04-22 03:09:56 +0000139
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000140def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
141 dry_run=0):
142 """Create an archive file (eg. zip or tar).
143
144 'base_name' is the name of the file to create, minus any format-specific
145 extension; 'format' is the archive format: one of "zip", "tar", "ztar",
146 or "gztar".
147
Greg Wardaebf7062000-04-04 02:05:59 +0000148 'root_dir' is a directory that will be the root directory of the
149 archive; ie. we typically chdir into 'root_dir' before creating the
150 archive. 'base_dir' is the directory where we start archiving from;
151 ie. 'base_dir' will be the common prefix of all files and
152 directories in the archive. 'root_dir' and 'base_dir' both default
Greg Ward87909612000-06-01 01:07:55 +0000153 to the current directory. Returns the name of the archive file.
154 """
Greg Wardaebf7062000-04-04 02:05:59 +0000155 save_cwd = os.getcwd()
156 if root_dir is not None:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000157 log.debug("changing into '%s'", root_dir)
Greg Wardca4289f2000-09-26 02:13:49 +0000158 base_name = os.path.abspath(base_name)
Greg Wardaebf7062000-04-04 02:05:59 +0000159 if not dry_run:
Greg Wardca4289f2000-09-26 02:13:49 +0000160 os.chdir(root_dir)
Greg Wardaebf7062000-04-04 02:05:59 +0000161
162 if base_dir is None:
163 base_dir = os.curdir
164
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000165 kwargs = {'dry_run': dry_run}
Fred Drakeb94b8492001-12-06 20:51:35 +0000166
Greg Warddb807542000-04-22 03:09:56 +0000167 try:
168 format_info = ARCHIVE_FORMATS[format]
169 except KeyError:
170 raise ValueError, "unknown archive format '%s'" % format
Greg Wardaebf7062000-04-04 02:05:59 +0000171
Greg Warddb807542000-04-22 03:09:56 +0000172 func = format_info[0]
Tarek Ziadé6f826ed2009-05-17 12:04:57 +0000173 for arg, val in format_info[1]:
Greg Warddb807542000-04-22 03:09:56 +0000174 kwargs[arg] = val
Greg Wardca4289f2000-09-26 02:13:49 +0000175 filename = apply(func, (base_name, base_dir), kwargs)
Greg Wardaebf7062000-04-04 02:05:59 +0000176
177 if root_dir is not None:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000178 log.debug("changing back to '%s'", save_cwd)
Greg Wardca4289f2000-09-26 02:13:49 +0000179 os.chdir(save_cwd)
Greg Wardaebf7062000-04-04 02:05:59 +0000180
Greg Ward87909612000-06-01 01:07:55 +0000181 return filename