blob: d5b30966179464c8c7f2ce5f76e4d5be884ac3f8 [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
Andrew M. Kuchlingd448f662002-11-19 13:12:28 +00006# This module should be kept compatible with Python 1.5.2.
7
Greg Wardaebf7062000-04-04 02:05:59 +00008__revision__ = "$Id$"
9
10import os
11from distutils.errors import DistutilsExecError
12from distutils.spawn import spawn
Greg Ward04e25a12000-08-22 01:48:54 +000013from distutils.dir_util import mkpath
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000014from distutils import log
Greg Wardaebf7062000-04-04 02:05:59 +000015
16def make_tarball (base_name, base_dir, compress="gzip",
17 verbose=0, dry_run=0):
18 """Create a (possibly compressed) tar file from all the files under
Greg Wardca4289f2000-09-26 02:13:49 +000019 'base_dir'. 'compress' must be "gzip" (the default), "compress",
20 "bzip2", or None. Both "tar" and the compression utility named by
21 'compress' must be on the default program search path, so this is
22 probably Unix-specific. The output tar file will be named 'base_dir' +
23 ".tar", possibly plus the appropriate compression extension (".gz",
24 ".bz2" or ".Z"). Return the output filename.
25 """
Greg Wardaebf7062000-04-04 02:05:59 +000026 # XXX GNU tar 1.13 has a nifty option to add a prefix directory.
27 # It's pretty new, though, so we certainly can't require it --
28 # but it would be nice to take advantage of it to skip the
29 # "create a tree of hardlinks" step! (Would also be nice to
30 # detect GNU tar to use its 'z' option and save a step.)
31
32 compress_ext = { 'gzip': ".gz",
Greg Wardf1948782000-04-25 01:38:20 +000033 'bzip2': '.bz2',
Greg Wardaebf7062000-04-04 02:05:59 +000034 'compress': ".Z" }
Fred Drakeb94b8492001-12-06 20:51:35 +000035
Greg Wardf1948782000-04-25 01:38:20 +000036 # flags for compression program, each element of list will be an argument
37 compress_flags = {'gzip': ["-f9"],
38 'compress': ["-f"],
39 'bzip2': ['-f9']}
Greg Wardaebf7062000-04-04 02:05:59 +000040
Greg Wardf1948782000-04-25 01:38:20 +000041 if compress is not None and compress not in compress_ext.keys():
Greg Wardaebf7062000-04-04 02:05:59 +000042 raise ValueError, \
43 "bad value for 'compress': must be None, 'gzip', or 'compress'"
44
45 archive_name = base_name + ".tar"
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000046 mkpath(os.path.dirname(archive_name), dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000047 cmd = ["tar", "-cf", archive_name, base_dir]
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000048 spawn(cmd, dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000049
50 if compress:
Greg Wardca4289f2000-09-26 02:13:49 +000051 spawn([compress] + compress_flags[compress] + [archive_name],
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000052 dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000053 return archive_name + compress_ext[compress]
54 else:
55 return archive_name
56
57# make_tarball ()
58
59
60def make_zipfile (base_name, base_dir, verbose=0, dry_run=0):
Greg Wardca4289f2000-09-26 02:13:49 +000061 """Create a zip file from all the files under 'base_dir'. The output
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000062 zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
63 Python module (if available) or the InfoZIP "zip" utility (if installed
64 and found on the default search path). If neither tool is available,
65 raises DistutilsExecError. Returns the name of the output zip file.
Greg Wardca4289f2000-09-26 02:13:49 +000066 """
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000067 try:
68 import zipfile
69 except ImportError:
70 zipfile = None
71
Greg Wardaebf7062000-04-04 02:05:59 +000072 zip_filename = base_name + ".zip"
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000073 mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
Greg Wardaebf7062000-04-04 02:05:59 +000074
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000075 # If zipfile module is not available, try spawning an external
76 # 'zip' command.
77 if zipfile is None:
78 if verbose:
79 zipoptions = "-r"
80 else:
81 zipoptions = "-rq"
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000082
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000083 try:
84 spawn(["zip", zipoptions, zip_filename, base_dir],
85 dry_run=dry_run)
86 except DistutilsExecError:
87 # XXX really should distinguish between "couldn't find
88 # external 'zip' command" and "zip failed".
89 raise DistutilsExecError, \
90 ("unable to create zip file '%s': "
91 "could neither import the 'zipfile' module nor "
92 "find a standalone zip utility") % zip_filename
93
94 else:
95 log.info("creating '%s' and adding '%s' to it",
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +000096 zip_filename, base_dir)
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +000097
Greg Wardaebf7062000-04-04 02:05:59 +000098 def visit (z, dirname, names):
99 for name in names:
Greg Ward65bc20c2000-05-31 02:17:19 +0000100 path = os.path.normpath(os.path.join(dirname, name))
Greg Wardca4289f2000-09-26 02:13:49 +0000101 if os.path.isfile(path):
102 z.write(path, path)
Andrew M. Kuchlingcdd21572002-11-21 18:33:28 +0000103 log.info("adding '%s'" % path)
Greg Wardaebf7062000-04-04 02:05:59 +0000104
105 if not dry_run:
Guido van Rossumb61914d2001-04-14 16:17:00 +0000106 z = zipfile.ZipFile(zip_filename, "w",
Greg Wardca4289f2000-09-26 02:13:49 +0000107 compression=zipfile.ZIP_DEFLATED)
Greg Wardaebf7062000-04-04 02:05:59 +0000108
Greg Wardca4289f2000-09-26 02:13:49 +0000109 os.path.walk(base_dir, visit, z)
Greg Wardaebf7062000-04-04 02:05:59 +0000110 z.close()
111
112 return zip_filename
113
114# make_zipfile ()
115
116
Greg Warddb807542000-04-22 03:09:56 +0000117ARCHIVE_FORMATS = {
Greg Ward2ff78872000-06-24 00:23:20 +0000118 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
119 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
120 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
121 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
Greg Ward04e25a12000-08-22 01:48:54 +0000122 'zip': (make_zipfile, [],"ZIP file")
Greg Warddb807542000-04-22 03:09:56 +0000123 }
124
125def check_archive_formats (formats):
126 for format in formats:
127 if not ARCHIVE_FORMATS.has_key(format):
128 return format
129 else:
130 return None
131
Greg Wardaebf7062000-04-04 02:05:59 +0000132def make_archive (base_name, format,
133 root_dir=None, base_dir=None,
134 verbose=0, dry_run=0):
Greg Wardaebf7062000-04-04 02:05:59 +0000135 """Create an archive file (eg. zip or tar). 'base_name' is the name
136 of the file to create, minus any format-specific extension; 'format'
137 is the archive format: one of "zip", "tar", "ztar", or "gztar".
138 'root_dir' is a directory that will be the root directory of the
139 archive; ie. we typically chdir into 'root_dir' before creating the
140 archive. 'base_dir' is the directory where we start archiving from;
141 ie. 'base_dir' will be the common prefix of all files and
142 directories in the archive. 'root_dir' and 'base_dir' both default
Greg Ward87909612000-06-01 01:07:55 +0000143 to the current directory. Returns the name of the archive file.
144 """
Greg Wardaebf7062000-04-04 02:05:59 +0000145 save_cwd = os.getcwd()
146 if root_dir is not None:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000147 log.debug("changing into '%s'", root_dir)
Greg Wardca4289f2000-09-26 02:13:49 +0000148 base_name = os.path.abspath(base_name)
Greg Wardaebf7062000-04-04 02:05:59 +0000149 if not dry_run:
Greg Wardca4289f2000-09-26 02:13:49 +0000150 os.chdir(root_dir)
Greg Wardaebf7062000-04-04 02:05:59 +0000151
152 if base_dir is None:
153 base_dir = os.curdir
154
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000155 kwargs = { 'dry_run': dry_run }
Fred Drakeb94b8492001-12-06 20:51:35 +0000156
Greg Warddb807542000-04-22 03:09:56 +0000157 try:
158 format_info = ARCHIVE_FORMATS[format]
159 except KeyError:
160 raise ValueError, "unknown archive format '%s'" % format
Greg Wardaebf7062000-04-04 02:05:59 +0000161
Greg Warddb807542000-04-22 03:09:56 +0000162 func = format_info[0]
163 for (arg,val) in format_info[1]:
164 kwargs[arg] = val
Greg Wardca4289f2000-09-26 02:13:49 +0000165 filename = apply(func, (base_name, base_dir), kwargs)
Greg Wardaebf7062000-04-04 02:05:59 +0000166
167 if root_dir is not None:
Jeremy Hyltoncd8a1142002-06-04 20:14:43 +0000168 log.debug("changing back to '%s'", save_cwd)
Greg Wardca4289f2000-09-26 02:13:49 +0000169 os.chdir(save_cwd)
Greg Wardaebf7062000-04-04 02:05:59 +0000170
Greg Ward87909612000-06-01 01:07:55 +0000171 return filename
172
Greg Wardaebf7062000-04-04 02:05:59 +0000173# make_archive ()