blob: a28eed1ad8b11ecb24ae1a77c30e200f88b7700a [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
105def make_archive (base_name, format,
106 root_dir=None, base_dir=None,
107 verbose=0, dry_run=0):
108
109 """Create an archive file (eg. zip or tar). 'base_name' is the name
110 of the file to create, minus any format-specific extension; 'format'
111 is the archive format: one of "zip", "tar", "ztar", or "gztar".
112 'root_dir' is a directory that will be the root directory of the
113 archive; ie. we typically chdir into 'root_dir' before creating the
114 archive. 'base_dir' is the directory where we start archiving from;
115 ie. 'base_dir' will be the common prefix of all files and
116 directories in the archive. 'root_dir' and 'base_dir' both default
117 to the current directory."""
118
119 save_cwd = os.getcwd()
120 if root_dir is not None:
121 if verbose:
122 print "changing into '%s'" % root_dir
123 base_name = os.path.abspath (base_name)
124 if not dry_run:
125 os.chdir (root_dir)
126
127 if base_dir is None:
128 base_dir = os.curdir
129
130 kwargs = { 'verbose': verbose,
131 'dry_run': dry_run }
132
133 if format == 'gztar':
134 func = make_tarball
135 kwargs['compress'] = 'gzip'
136 elif format == 'ztar':
137 func = make_tarball
138 kwargs['compress'] = 'compress'
139 elif format == 'tar':
140 func = make_tarball
141 kwargs['compress'] = None
142 elif format == 'zip':
143 func = make_zipfile
144
145 apply (func, (base_name, base_dir), kwargs)
146
147 if root_dir is not None:
148 if verbose:
149 print "changing back to '%s'" % save_cwd
150 os.chdir (save_cwd)
151
152# make_archive ()