diff --git a/Lib/distutils/archive_util.py b/Lib/distutils/archive_util.py index 08a9e569019..a568854e4ee 100644 --- a/Lib/distutils/archive_util.py +++ b/Lib/distutils/archive_util.py @@ -6,6 +6,9 @@ that sort of thing).""" __revision__ = "$Id$" import os +from warnings import warn +import sys + from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath @@ -22,36 +25,45 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0): the appropriate compression extension (".gz", ".bz2" or ".Z"). Returns the output filename. """ - # XXX GNU tar 1.13 has a nifty option to add a prefix directory. - # It's pretty new, though, so we certainly can't require it -- - # but it would be nice to take advantage of it to skip the - # "create a tree of hardlinks" step! (Would also be nice to - # detect GNU tar to use its 'z' option and save a step.) - - compress_ext = {'gzip': ".gz", - 'bzip2': '.bz2', - 'compress': ".Z" } + tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', None: '', 'compress': ''} + compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'compress': '.Z'} # flags for compression program, each element of list will be an argument - compress_flags = {'gzip': ["-f9"], - 'compress': ["-f"], - 'bzip2': ['-f9']} - if compress is not None and compress not in compress_ext.keys(): raise ValueError( - "bad value for 'compress': must be None, 'gzip', or 'compress'") + "bad value for 'compress': must be None, 'gzip', 'bzip2' " + "or 'compress'") + + archive_name = base_name + '.tar' + if compress != 'compress': + archive_name += compress_ext.get(compress, '') - archive_name = base_name + ".tar" mkpath(os.path.dirname(archive_name), dry_run=dry_run) - cmd = ["tar", "-cf", archive_name, base_dir] - spawn(cmd, dry_run=dry_run) - if compress: - spawn([compress] + compress_flags[compress] + [archive_name], - dry_run=dry_run) - return archive_name + compress_ext[compress] - else: - return archive_name + # creating the tarball + import tarfile # late import so Python build itself doesn't break + + log.info('Creating tar archive') + if not dry_run: + tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) + try: + tar.add(base_dir) + finally: + tar.close() + + # compression using `compress` + if compress == 'compress': + warn("'compress' will be deprecated.", PendingDeprecationWarning) + # the option varies depending on the platform + compressed_name = archive_name + compress_ext[compress] + if sys.platform == 'win32': + cmd = [compress, archive_name, compressed_name] + else: + cmd = [compress, '-f', archive_name] + spawn(cmd, dry_run=dry_run) + return compressed_name + + return archive_name def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. diff --git a/Lib/distutils/tests/test_archive_util.py b/Lib/distutils/tests/test_archive_util.py index cabb55bc15f..5db9a5d096e 100644 --- a/Lib/distutils/tests/test_archive_util.py +++ b/Lib/distutils/tests/test_archive_util.py @@ -3,12 +3,15 @@ __revision__ = "$Id$" import unittest import os +import tarfile from os.path import splitdrive +import warnings from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive) -from distutils.spawn import find_executable +from distutils.spawn import find_executable, spawn from distutils.tests import support +from test.support import check_warnings try: import zipfile @@ -19,12 +22,13 @@ except ImportError: class ArchiveUtilTestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() self.write_file([tmpdir, 'file1'], 'xxx') self.write_file([tmpdir, 'file2'], 'xxx') + os.mkdir(os.path.join(tmpdir, 'sub')) + self.write_file([tmpdir, 'sub', 'file3'], 'xxx') tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], @@ -55,6 +59,111 @@ class ArchiveUtilTestCase(support.TempdirManager, tarball = base_name + '.tar' self.assert_(os.path.exists(tarball)) + def _tarinfo(self, path): + tar = tarfile.open(path) + try: + names = tar.getnames() + names.sort() + return tuple(names) + finally: + tar.close() + + def _create_files(self): + # creating something to tar + tmpdir = self.mkdtemp() + dist = os.path.join(tmpdir, 'dist') + os.mkdir(dist) + self.write_file([dist, 'file1'], 'xxx') + self.write_file([dist, 'file2'], 'xxx') + os.mkdir(os.path.join(dist, 'sub')) + self.write_file([dist, 'sub', 'file3'], 'xxx') + os.mkdir(os.path.join(dist, 'sub2')) + tmpdir2 = self.mkdtemp() + base_name = os.path.join(tmpdir2, 'archive') + return tmpdir, tmpdir2, base_name + + @unittest.skipUnless(find_executable('tar'), 'Need the tar command to run') + def test_tarfile_vs_tar(self): + tmpdir, tmpdir2, base_name = self._create_files() + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist') + finally: + os.chdir(old_dir) + + # check if the compressed tarball was created + tarball = base_name + '.tar.gz' + self.assert_(os.path.exists(tarball)) + + # now create another tarball using `tar` + tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') + cmd = ['tar', '-czf', 'archive2.tar.gz', 'dist'] + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + spawn(cmd) + finally: + os.chdir(old_dir) + + self.assert_(os.path.exists(tarball2)) + # let's compare both tarballs + self.assertEquals(self._tarinfo(tarball), self._tarinfo(tarball2)) + + # trying an uncompressed one + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + # now for a dry_run + base_name = os.path.join(tmpdir2, 'archive') + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + make_tarball(base_name, 'dist', compress=None, dry_run=True) + finally: + os.chdir(old_dir) + tarball = base_name + '.tar' + self.assert_(os.path.exists(tarball)) + + @unittest.skipUnless(find_executable('compress'), + 'The compress program is required') + def test_compress_deprecated(self): + tmpdir, tmpdir2, base_name = self._create_files() + + # using compress and testing the PendingDeprecationWarning + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress') + finally: + os.chdir(old_dir) + tarball = base_name + '.tar.Z' + self.assert_(os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + + # same test with dry_run + os.remove(tarball) + old_dir = os.getcwd() + os.chdir(tmpdir) + try: + with check_warnings() as w: + warnings.simplefilter("always") + make_tarball(base_name, 'dist', compress='compress', + dry_run=True) + finally: + os.chdir(old_dir) + self.assert_(not os.path.exists(tarball)) + self.assertEquals(len(w.warnings), 1) + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile(self): # creating something to tar diff --git a/Misc/NEWS b/Misc/NEWS index d1117097d64..8a01b210e7b 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -684,6 +684,8 @@ Core and Builtins Library ------- +- Issue #6048: Now Distutils uses the tarfile module in archive_util. + - Issue #6062: In distutils, fixed the package option of build_ext. Feedback and tests on pywin32 by Tim Golden.