]>
Commit | Line | Data |
---|---|---|
1e4a197e RD |
1 | """distutils.archive_util |
2 | ||
3 | Utility functions for creating archive files (tarballs, zip files, | |
4 | that sort of thing).""" | |
5 | ||
6 | # This module should be kept compatible with Python 1.5.2. | |
7 | ||
8 | __revision__ = "$Id$" | |
9 | ||
10 | import os | |
11 | from distutils.errors import DistutilsExecError | |
12 | from distutils.spawn import spawn | |
13 | from distutils.dir_util import mkpath | |
14 | from distutils import log | |
15 | ||
16 | def 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 | |
19 | '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 | """ | |
26 | # 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", | |
33 | 'bzip2': '.bz2', | |
34 | 'compress': ".Z" } | |
35 | ||
36 | # flags for compression program, each element of list will be an argument | |
37 | compress_flags = {'gzip': ["-f9"], | |
38 | 'compress': ["-f"], | |
39 | 'bzip2': ['-f9']} | |
40 | ||
41 | if compress is not None and compress not in compress_ext.keys(): | |
42 | raise ValueError, \ | |
43 | "bad value for 'compress': must be None, 'gzip', or 'compress'" | |
44 | ||
45 | archive_name = base_name + ".tar" | |
46 | mkpath(os.path.dirname(archive_name), dry_run=dry_run) | |
47 | cmd = ["tar", "-cf", archive_name, base_dir] | |
48 | spawn(cmd, dry_run=dry_run) | |
49 | ||
50 | if compress: | |
51 | spawn([compress] + compress_flags[compress] + [archive_name], | |
52 | dry_run=dry_run) | |
53 | return archive_name + compress_ext[compress] | |
54 | else: | |
55 | return archive_name | |
56 | ||
57 | # make_tarball () | |
58 | ||
59 | ||
60 | def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): | |
61 | """Create a zip file from all the files under 'base_dir'. The output | |
62 | 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. | |
66 | """ | |
67 | try: | |
68 | import zipfile | |
69 | except ImportError: | |
70 | zipfile = None | |
71 | ||
72 | zip_filename = base_name + ".zip" | |
73 | mkpath(os.path.dirname(zip_filename), dry_run=dry_run) | |
74 | ||
75 | # 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" | |
82 | ||
83 | 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", | |
96 | zip_filename, base_dir) | |
97 | ||
98 | def visit (z, dirname, names): | |
99 | for name in names: | |
100 | path = os.path.normpath(os.path.join(dirname, name)) | |
101 | if os.path.isfile(path): | |
102 | z.write(path, path) | |
103 | log.info("adding '%s'" % path) | |
104 | ||
105 | if not dry_run: | |
106 | z = zipfile.ZipFile(zip_filename, "w", | |
107 | compression=zipfile.ZIP_DEFLATED) | |
108 | ||
109 | os.path.walk(base_dir, visit, z) | |
110 | z.close() | |
111 | ||
112 | return zip_filename | |
113 | ||
114 | # make_zipfile () | |
115 | ||
116 | ||
117 | ARCHIVE_FORMATS = { | |
118 | '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"), | |
122 | 'zip': (make_zipfile, [],"ZIP file") | |
123 | } | |
124 | ||
125 | def 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 | ||
132 | def make_archive (base_name, format, | |
133 | root_dir=None, base_dir=None, | |
134 | verbose=0, dry_run=0): | |
135 | """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 | |
143 | to the current directory. Returns the name of the archive file. | |
144 | """ | |
145 | save_cwd = os.getcwd() | |
146 | if root_dir is not None: | |
147 | log.debug("changing into '%s'", root_dir) | |
148 | base_name = os.path.abspath(base_name) | |
149 | if not dry_run: | |
150 | os.chdir(root_dir) | |
151 | ||
152 | if base_dir is None: | |
153 | base_dir = os.curdir | |
154 | ||
155 | kwargs = { 'dry_run': dry_run } | |
156 | ||
157 | try: | |
158 | format_info = ARCHIVE_FORMATS[format] | |
159 | except KeyError: | |
160 | raise ValueError, "unknown archive format '%s'" % format | |
161 | ||
162 | func = format_info[0] | |
163 | for (arg,val) in format_info[1]: | |
164 | kwargs[arg] = val | |
165 | filename = apply(func, (base_name, base_dir), kwargs) | |
166 | ||
167 | if root_dir is not None: | |
168 | log.debug("changing back to '%s'", save_cwd) | |
169 | os.chdir(save_cwd) | |
170 | ||
171 | return filename | |
172 | ||
173 | # make_archive () |