1 """distutils.archive_util
3 Utility functions for creating archive files (tarballs, zip files,
4 that sort of thing)."""
9 from warnings
import warn
12 from distutils
.errors
import DistutilsExecError
13 from distutils
.spawn
import spawn
14 from distutils
.dir_util
import mkpath
15 from distutils
import log
18 from pwd
import getpwnam
23 from grp
import getgrnam
28 """Returns a gid, given a group name."""
29 if getgrnam
is None or name
is None:
32 result
= getgrnam(name
)
35 if result
is not None:
40 """Returns an uid, given a user name."""
41 if getpwnam
is None or name
is None:
44 result
= getpwnam(name
)
47 if result
is not None:
51 def make_tarball(base_name
, base_dir
, compress
="gzip", verbose
=0, dry_run
=0,
52 owner
=None, group
=None):
53 """Create a (possibly compressed) tar file from all the files under
56 'compress' must be "gzip" (the default), "compress", "bzip2", or None.
57 (compress will be deprecated in Python 3.2)
59 'owner' and 'group' can be used to define an owner and a group for the
60 archive that is being built. If not provided, the current owner and group
63 The output tar file will be named 'base_dir' + ".tar", possibly plus
64 the appropriate compression extension (".gz", ".bz2" or ".Z").
66 Returns the output filename.
68 tar_compression
= {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''}
69 compress_ext
= {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'}
71 # flags for compression program, each element of list will be an argument
72 if compress
is not None and compress
not in compress_ext
.keys():
74 ("bad value for 'compress': must be None, 'gzip', 'bzip2' "
77 archive_name
= base_name
+ '.tar'
78 if compress
!= 'compress':
79 archive_name
+= compress_ext
.get(compress
, '')
81 mkpath(os
.path
.dirname(archive_name
), dry_run
=dry_run
)
83 # creating the tarball
84 import tarfile
# late import so Python build itself doesn't break
86 log
.info('Creating tar archive')
91 def _set_uid_gid(tarinfo
):
101 tar
= tarfile
.open(archive_name
, 'w|%s' % tar_compression
[compress
])
103 tar
.add(base_dir
, filter=_set_uid_gid
)
107 # compression using `compress`
108 if compress
== 'compress':
109 warn("'compress' will be deprecated.", PendingDeprecationWarning
)
110 # the option varies depending on the platform
111 compressed_name
= archive_name
+ compress_ext
[compress
]
112 if sys
.platform
== 'win32':
113 cmd
= [compress
, archive_name
, compressed_name
]
115 cmd
= [compress
, '-f', archive_name
]
116 spawn(cmd
, dry_run
=dry_run
)
117 return compressed_name
121 def make_zipfile(base_name
, base_dir
, verbose
=0, dry_run
=0):
122 """Create a zip file from all the files under 'base_dir'.
124 The output zip file will be named 'base_dir' + ".zip". Uses either the
125 "zipfile" Python module (if available) or the InfoZIP "zip" utility
126 (if installed and found on the default search path). If neither tool is
127 available, raises DistutilsExecError. Returns the name of the output zip
135 zip_filename
= base_name
+ ".zip"
136 mkpath(os
.path
.dirname(zip_filename
), dry_run
=dry_run
)
138 # If zipfile module is not available, try spawning an external
147 spawn(["zip", zipoptions
, zip_filename
, base_dir
],
149 except DistutilsExecError
:
150 # XXX really should distinguish between "couldn't find
151 # external 'zip' command" and "zip failed".
152 raise DistutilsExecError
, \
153 ("unable to create zip file '%s': "
154 "could neither import the 'zipfile' module nor "
155 "find a standalone zip utility") % zip_filename
158 log
.info("creating '%s' and adding '%s' to it",
159 zip_filename
, base_dir
)
162 zip = zipfile
.ZipFile(zip_filename
, "w",
163 compression
=zipfile
.ZIP_DEFLATED
)
165 for dirpath
, dirnames
, filenames
in os
.walk(base_dir
):
166 for name
in filenames
:
167 path
= os
.path
.normpath(os
.path
.join(dirpath
, name
))
168 if os
.path
.isfile(path
):
169 zip.write(path
, path
)
170 log
.info("adding '%s'" % path
)
176 'gztar': (make_tarball
, [('compress', 'gzip')], "gzip'ed tar-file"),
177 'bztar': (make_tarball
, [('compress', 'bzip2')], "bzip2'ed tar-file"),
178 'ztar': (make_tarball
, [('compress', 'compress')], "compressed tar file"),
179 'tar': (make_tarball
, [('compress', None)], "uncompressed tar file"),
180 'zip': (make_zipfile
, [],"ZIP file")
183 def check_archive_formats(formats
):
184 """Returns the first format from the 'format' list that is unknown.
186 If all formats are known, returns None
188 for format
in formats
:
189 if format
not in ARCHIVE_FORMATS
:
193 def make_archive(base_name
, format
, root_dir
=None, base_dir
=None, verbose
=0,
194 dry_run
=0, owner
=None, group
=None):
195 """Create an archive file (eg. zip or tar).
197 'base_name' is the name of the file to create, minus any format-specific
198 extension; 'format' is the archive format: one of "zip", "tar", "ztar",
201 'root_dir' is a directory that will be the root directory of the
202 archive; ie. we typically chdir into 'root_dir' before creating the
203 archive. 'base_dir' is the directory where we start archiving from;
204 ie. 'base_dir' will be the common prefix of all files and
205 directories in the archive. 'root_dir' and 'base_dir' both default
206 to the current directory. Returns the name of the archive file.
208 'owner' and 'group' are used when creating a tar archive. By default,
209 uses the current owner and group.
211 save_cwd
= os
.getcwd()
212 if root_dir
is not None:
213 log
.debug("changing into '%s'", root_dir
)
214 base_name
= os
.path
.abspath(base_name
)
221 kwargs
= {'dry_run': dry_run
}
224 format_info
= ARCHIVE_FORMATS
[format
]
226 raise ValueError, "unknown archive format '%s'" % format
228 func
= format_info
[0]
229 for arg
, val
in format_info
[1]:
233 kwargs
['owner'] = owner
234 kwargs
['group'] = group
237 filename
= func(base_name
, base_dir
, **kwargs
)
239 if root_dir
is not None:
240 log
.debug("changing back to '%s'", save_cwd
)