blob: bae425be06697bf1635770cf9abe330152189c4d [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
6# created 2000/04/03, Greg Ward (extracted from util.py)
7
8__revision__ = "$Id$"
9
10import os
11from distutils.errors import DistutilsExecError
12from distutils.spawn import spawn
13
14
15def make_tarball (base_name, base_dir, compress="gzip",
16 verbose=0, dry_run=0):
17 """Create a (possibly compressed) tar file from all the files under
18 'base_dir'. 'compress' must be "gzip" (the default), "compress", or
19 None. Both "tar" and the compression utility named by 'compress'
20 must be on the default program search path, so this is probably
21 Unix-specific. The output tar file will be named 'base_dir' +
22 ".tar", possibly plus the appropriate compression extension
23 (".gz" or ".Z"). Return the output filename."""
24
25 # XXX GNU tar 1.13 has a nifty option to add a prefix directory.
26 # It's pretty new, though, so we certainly can't require it --
27 # but it would be nice to take advantage of it to skip the
28 # "create a tree of hardlinks" step! (Would also be nice to
29 # detect GNU tar to use its 'z' option and save a step.)
30
31 compress_ext = { 'gzip': ".gz",
32 'compress': ".Z" }
33
34 if compress is not None and compress not in ('gzip', 'compress'):
35 raise ValueError, \
36 "bad value for 'compress': must be None, 'gzip', or 'compress'"
37
38 archive_name = base_name + ".tar"
39 cmd = ["tar", "-cf", archive_name, base_dir]
40 spawn (cmd, verbose=verbose, dry_run=dry_run)
41
42 if compress:
43 spawn ([compress, archive_name], verbose=verbose, dry_run=dry_run)
44 return archive_name + compress_ext[compress]
45 else:
46 return archive_name
47
48# make_tarball ()
49
50
51def make_zipfile (base_name, base_dir, verbose=0, dry_run=0):
52 """Create a zip file from all the files under 'base_dir'. The
53 output zip file will be named 'base_dir' + ".zip". Uses either the
54 InfoZIP "zip" utility (if installed and found on the default search
55 path) or the "zipfile" Python module (if available). If neither
56 tool is available, raises DistutilsExecError. Returns the name
57 of the output zip file."""
58
59 # This initially assumed the Unix 'zip' utility -- but
60 # apparently InfoZIP's zip.exe works the same under Windows, so
61 # no changes needed!
62
63 zip_filename = base_name + ".zip"
64 try:
65 spawn (["zip", "-rq", zip_filename, base_dir],
66 verbose=verbose, dry_run=dry_run)
67 except DistutilsExecError:
68
69 # XXX really should distinguish between "couldn't find
70 # external 'zip' command" and "zip failed" -- shouldn't try
71 # again in the latter case. (I think fixing this will
72 # require some cooperation from the spawn module -- perhaps
73 # a utility function to search the path, so we can fallback
74 # on zipfile.py without the failed spawn.)
75 try:
76 import zipfile
77 except ImportError:
78 raise DistutilsExecError, \
79 ("unable to create zip file '%s': " +
80 "could neither find a standalone zip utility nor " +
81 "import the 'zipfile' module") % zip_filename
82
83 if verbose:
84 print "creating '%s' and adding '%s' to it" % \
85 (zip_filename, base_dir)
86
87 def visit (z, dirname, names):
88 for name in names:
89 path = os.path.join (dirname, name)
90 if os.path.isfile (path):
91 z.write (path, path)
92
93 if not dry_run:
94 z = zipfile.ZipFile (zip_filename, "wb",
95 compression=zipfile.ZIP_DEFLATED)
96
97 os.path.walk (base_dir, visit, z)
98 z.close()
99
100 return zip_filename
101
102# make_zipfile ()
103
104
Greg Warddb807542000-04-22 03:09:56 +0000105ARCHIVE_FORMATS = {
106 'gztar': (make_tarball, [('compress', 'gzip')]),
107 'ztar': (make_tarball, [('compress', 'compress')]),
108 'tar': (make_tarball, [('compress', None)]),
109 'zip': (make_zipfile, [])
110 }
111
112def check_archive_formats (formats):
113 for format in formats:
114 if not ARCHIVE_FORMATS.has_key(format):
115 return format
116 else:
117 return None
118
Greg Wardaebf7062000-04-04 02:05:59 +0000119def make_archive (base_name, format,
120 root_dir=None, base_dir=None,
121 verbose=0, dry_run=0):
122
123 """Create an archive file (eg. zip or tar). 'base_name' is the name
124 of the file to create, minus any format-specific extension; 'format'
125 is the archive format: one of "zip", "tar", "ztar", or "gztar".
126 'root_dir' is a directory that will be the root directory of the
127 archive; ie. we typically chdir into 'root_dir' before creating the
128 archive. 'base_dir' is the directory where we start archiving from;
129 ie. 'base_dir' will be the common prefix of all files and
130 directories in the archive. 'root_dir' and 'base_dir' both default
131 to the current directory."""
132
133 save_cwd = os.getcwd()
134 if root_dir is not None:
135 if verbose:
136 print "changing into '%s'" % root_dir
137 base_name = os.path.abspath (base_name)
138 if not dry_run:
139 os.chdir (root_dir)
140
141 if base_dir is None:
142 base_dir = os.curdir
143
144 kwargs = { 'verbose': verbose,
145 'dry_run': dry_run }
146
Greg Warddb807542000-04-22 03:09:56 +0000147 try:
148 format_info = ARCHIVE_FORMATS[format]
149 except KeyError:
150 raise ValueError, "unknown archive format '%s'" % format
Greg Wardaebf7062000-04-04 02:05:59 +0000151
Greg Warddb807542000-04-22 03:09:56 +0000152 func = format_info[0]
153 for (arg,val) in format_info[1]:
154 kwargs[arg] = val
Greg Wardaebf7062000-04-04 02:05:59 +0000155 apply (func, (base_name, base_dir), kwargs)
156
157 if root_dir is not None:
158 if verbose:
159 print "changing back to '%s'" % save_cwd
160 os.chdir (save_cwd)
161
162# make_archive ()