1aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward"""distutils.archive_util
2aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
3aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg WardUtility functions for creating archive files (tarballs, zip files,
4aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Wardthat sort of thing)."""
5aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
6aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Wardimport os
777c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadéfrom warnings import warn
877c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadéimport sys
977c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
102c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitroutry:
112c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou    import zipfile
12cd171c8e92c10d327151400fd8f16b11a4964615Brett Cannonexcept ImportError:
132c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou    zipfile = None
142c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou
152c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou
16aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Wardfrom distutils.errors import DistutilsExecError
17aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Wardfrom distutils.spawn import spawn
1804e25a1bdfd8fd724ab46a9c946ae8aa11cd09afGreg Wardfrom distutils.dir_util import mkpath
19cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hyltonfrom distutils import log
20aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
215e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchlingtry:
225e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    from pwd import getpwnam
23aa32779632270a8286e65f02fcbae2a08419d23aVictor Stinnerexcept ImportError:
245e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    getpwnam = None
255e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
265e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchlingtry:
275e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    from grp import getgrnam
28aa32779632270a8286e65f02fcbae2a08419d23aVictor Stinnerexcept ImportError:
295e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    getgrnam = None
305e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
315e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchlingdef _get_gid(name):
325e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    """Returns a gid, given a group name."""
335e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    if getgrnam is None or name is None:
345e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        return None
355e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    try:
365e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        result = getgrnam(name)
375e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    except KeyError:
385e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        result = None
395e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    if result is not None:
405e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        return result[2]
415e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    return None
425e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
435e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchlingdef _get_uid(name):
445e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    """Returns an uid, given a user name."""
455e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    if getpwnam is None or name is None:
465e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        return None
475e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    try:
485e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        result = getpwnam(name)
495e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    except KeyError:
505e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        result = None
515e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    if result is not None:
525e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        return result[2]
535e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    return None
545e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
555e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchlingdef make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
565e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling                 owner=None, group=None):
57aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    """Create a (possibly compressed) tar file from all the files under
58eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    'base_dir'.
59eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé
60b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
61b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    None.  ("compress" will be deprecated in Python 3.2)
625e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
635e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    'owner' and 'group' can be used to define an owner and a group for the
645e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    archive that is being built. If not provided, the current owner and group
655e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    will be used.
665e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
67eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    The output tar file will be named 'base_dir' +  ".tar", possibly plus
68b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z").
695e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
70eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    Returns the output filename.
71ca4289f7b224a669f8eca70bfe2e0d3af09dd970Greg Ward    """
72b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '',
73b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka                       'compress': ''}
74b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz',
75b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka                    'compress': '.Z'}
76b94b849d65af71b4b432a74fdaef8ccd88209cc0Fred Drake
77f194878d6ad3b739974e805482135bad5978fa2bGreg Ward    # flags for compression program, each element of list will be an argument
78f194878d6ad3b739974e805482135bad5978fa2bGreg Ward    if compress is not None and compress not in compress_ext.keys():
795b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter        raise ValueError(
80b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka              "bad value for 'compress': must be None, 'gzip', 'bzip2', "
81b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka              "'xz' or 'compress'")
8277c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
8377c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    archive_name = base_name + '.tar'
8477c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    if compress != 'compress':
8577c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        archive_name += compress_ext.get(compress, '')
86aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
87cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton    mkpath(os.path.dirname(archive_name), dry_run=dry_run)
8877c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
8977c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    # creating the tarball
9077c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    import tarfile  # late import so Python build itself doesn't break
9177c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
9277c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    log.info('Creating tar archive')
935e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
945e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    uid = _get_uid(owner)
955e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    gid = _get_gid(group)
965e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
975e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    def _set_uid_gid(tarinfo):
985e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        if gid is not None:
995e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling            tarinfo.gid = gid
1005e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling            tarinfo.gname = group
1015e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        if uid is not None:
1025e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling            tarinfo.uid = uid
1035e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling            tarinfo.uname = owner
1045e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        return tarinfo
1055e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
10677c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    if not dry_run:
10777c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress])
10877c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        try:
1095e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling            tar.add(base_dir, filter=_set_uid_gid)
11077c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        finally:
11177c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé            tar.close()
11277c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
11377c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    # compression using `compress`
11477c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    if compress == 'compress':
11577c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        warn("'compress' will be deprecated.", PendingDeprecationWarning)
11677c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        # the option varies depending on the platform
11777c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        compressed_name = archive_name + compress_ext[compress]
11877c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        if sys.platform == 'win32':
11977c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé            cmd = [compress, archive_name, compressed_name]
12077c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        else:
12177c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé            cmd = [compress, '-f', archive_name]
12277c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        spawn(cmd, dry_run=dry_run)
12377c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé        return compressed_name
12477c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé
12577c8b376fcb2a0c77cc1c9b21f16c2524f63d78aTarek Ziadé    return archive_name
126aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
127eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadédef make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
128eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    """Create a zip file from all the files under 'base_dir'.
129aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
1307e2e321da310dd2a450ad3422b9567e0e2d04182Éric Araujo    The output zip file will be named 'base_name' + ".zip".  Uses either the
131eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    "zipfile" Python module (if available) or the InfoZIP "zip" utility
132eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    (if installed and found on the default search path).  If neither tool is
133eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    available, raises DistutilsExecError.  Returns the name of the output zip
134eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    file.
135ca4289f7b224a669f8eca70bfe2e0d3af09dd970Greg Ward    """
136aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    zip_filename = base_name + ".zip"
137cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton    mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
138cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling
139cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling    # If zipfile module is not available, try spawning an external
140cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling    # 'zip' command.
141cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling    if zipfile is None:
142cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling        if verbose:
143cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling            zipoptions = "-r"
144cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling        else:
145cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling            zipoptions = "-rq"
146182b5aca27d376b08a2904bed42b751496f932f3Tim Peters
147aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward        try:
148cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling            spawn(["zip", zipoptions, zip_filename, base_dir],
149cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling                  dry_run=dry_run)
150cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling        except DistutilsExecError:
151cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling            # XXX really should distinguish between "couldn't find
152cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling            # external 'zip' command" and "zip failed".
1535b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter            raise DistutilsExecError(("unable to create zip file '%s': "
154cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling                   "could neither import the 'zipfile' module nor "
1555b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter                   "find a standalone zip utility") % zip_filename)
156aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
157cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling    else:
158cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling        log.info("creating '%s' and adding '%s' to it",
159cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton                 zip_filename, base_dir)
160cdd215789c86f3cd729cf10c9d6fe6b3d5df5d7bAndrew M. Kuchling
161aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward        if not dry_run:
1622c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou            try:
1632c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou                zip = zipfile.ZipFile(zip_filename, "w",
1642c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou                                      compression=zipfile.ZIP_DEFLATED)
1652c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou            except RuntimeError:
1662c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou                zip = zipfile.ZipFile(zip_filename, "w",
1672c50a09ac47e701768a4e20f8a03d17263e8db8fAntoine Pitrou                                      compression=zipfile.ZIP_STORED)
168aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
169699adb9cd8efe80c99fb7d2815286aa026d83b91Benjamin Peterson            for dirpath, dirnames, filenames in os.walk(base_dir):
170699adb9cd8efe80c99fb7d2815286aa026d83b91Benjamin Peterson                for name in filenames:
171699adb9cd8efe80c99fb7d2815286aa026d83b91Benjamin Peterson                    path = os.path.normpath(os.path.join(dirpath, name))
172699adb9cd8efe80c99fb7d2815286aa026d83b91Benjamin Peterson                    if os.path.isfile(path):
173eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé                        zip.write(path, path)
174dd917f84e3775596049e09746f32053c50b3d422Vinay Sajip                        log.info("adding '%s'", path)
175eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé            zip.close()
176aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
177aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    return zip_filename
178aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
179db80754abcc49b8331a6da9d30c04dfcde815bc2Greg WardARCHIVE_FORMATS = {
1802ff7887270385bd6dddc2d5461486334b4fec8bcGreg Ward    'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
1812ff7887270385bd6dddc2d5461486334b4fec8bcGreg Ward    'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
182b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
1832ff7887270385bd6dddc2d5461486334b4fec8bcGreg Ward    'ztar':  (make_tarball, [('compress', 'compress')], "compressed tar file"),
1842ff7887270385bd6dddc2d5461486334b4fec8bcGreg Ward    'tar':   (make_tarball, [('compress', None)], "uncompressed tar file"),
18504e25a1bdfd8fd724ab46a9c946ae8aa11cd09afGreg Ward    'zip':   (make_zipfile, [],"ZIP file")
186db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward    }
187db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward
188eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadédef check_archive_formats(formats):
189eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    """Returns the first format from the 'format' list that is unknown.
190eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé
191eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    If all formats are known, returns None
192eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    """
193db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward    for format in formats:
194e2b70bcf7401477936fba99a8bf4a1f759ecc8a3Guido van Rossum        if format not in ARCHIVE_FORMATS:
195db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward            return format
196eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    return None
197eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé
198eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadédef make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
1995e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling                 dry_run=0, owner=None, group=None):
200eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    """Create an archive file (eg. zip or tar).
201eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé
202eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    'base_name' is the name of the file to create, minus any format-specific
203b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    extension; 'format' is the archive format: one of "zip", "tar", "gztar",
204b9cec6a30f77c4c0ab208053ffafc02fddd01962Serhiy Storchaka    "bztar", "xztar", or "ztar".
205eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé
206aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    'root_dir' is a directory that will be the root directory of the
207aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    archive; ie. we typically chdir into 'root_dir' before creating the
208aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    archive.  'base_dir' is the directory where we start archiving from;
209aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    ie. 'base_dir' will be the common prefix of all files and
210aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    directories in the archive.  'root_dir' and 'base_dir' both default
211879096137d95548dacc8ce88d11d8e23fe239f09Greg Ward    to the current directory.  Returns the name of the archive file.
2125e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
2135e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    'owner' and 'group' are used when creating a tar archive. By default,
2145e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    uses the current owner and group.
215879096137d95548dacc8ce88d11d8e23fe239f09Greg Ward    """
216aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    save_cwd = os.getcwd()
217aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    if root_dir is not None:
218cd8a1148e19116db109f27d26c02e1de536dc76eJeremy Hylton        log.debug("changing into '%s'", root_dir)
219ca4289f7b224a669f8eca70bfe2e0d3af09dd970Greg Ward        base_name = os.path.abspath(base_name)
220aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward        if not dry_run:
221ca4289f7b224a669f8eca70bfe2e0d3af09dd970Greg Ward            os.chdir(root_dir)
222aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
223aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward    if base_dir is None:
224aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward        base_dir = os.curdir
225aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
226eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    kwargs = {'dry_run': dry_run}
227b94b849d65af71b4b432a74fdaef8ccd88209cc0Fred Drake
228db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward    try:
229db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward        format_info = ARCHIVE_FORMATS[format]
230db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward    except KeyError:
2315b7e9d76f39dbf63573519c178835f72e5a5027aCollin Winter        raise ValueError("unknown archive format '%s'" % format)
232aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
233db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward    func = format_info[0]
234eb5f27ec9c19e2b2a125be8a5635cc0b257a4c02Tarek Ziadé    for arg, val in format_info[1]:
235db80754abcc49b8331a6da9d30c04dfcde815bc2Greg Ward        kwargs[arg] = val
2365e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
2375e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling    if format != 'zip':
2385e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        kwargs['owner'] = owner
2395e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling        kwargs['group'] = group
2405e2d45672c8f84f6b1877e68ab92b4b50e2d124dAndrew Kuchling
24153fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé    try:
24253fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé        filename = func(base_name, base_dir, **kwargs)
24353fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé    finally:
24453fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé        if root_dir is not None:
24553fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé            log.debug("changing back to '%s'", save_cwd)
24653fdb18b822395cae6e085f85ccc59f07a38dfd6Tarek Ziadé            os.chdir(save_cwd)
247aebf706b4e845d7525d2d8e792f0e23fcfb3e6afGreg Ward
248879096137d95548dacc8ce88d11d8e23fe239f09Greg Ward    return filename
249