Browse Source

cli tar: support different algorithms for zip & 7z

Fixes: #27443
AJIOB 1 month ago
parent
commit
94ae247d44
59 changed files with 496 additions and 124 deletions
  1. 12 0
      Help/command/file.rst
  2. 8 8
      Help/cpack_gen/archive.rst
  3. 1 1
      Help/cpack_gen/deb.rst
  4. 1 1
      Help/cpack_gen/rpm.rst
  5. 14 2
      Help/manual/cmake.1.rst
  6. 10 0
      Help/release/dev/cli-tar-7z-algorithms.rst
  7. 1 0
      Source/Modules/CMakeBuildUtilities.cmake
  8. 199 89
      Source/cmArchiveWrite.cxx
  9. 2 1
      Source/cmArchiveWrite.h
  10. 1 0
      Source/cmFileCommand.cxx
  11. 10 0
      Source/cmSystemTools.cxx
  12. 1 0
      Source/cmSystemTools.h
  13. 6 13
      Source/cmcmd.cxx
  14. 3 0
      Tests/RunCMake/CMakeLists.txt
  15. 10 0
      Tests/RunCMake/CommandLineTar/7zip-bz2.cmake
  16. 0 1
      Tests/RunCMake/CommandLineTar/7zip-gz-stderr.txt
  17. 10 0
      Tests/RunCMake/CommandLineTar/7zip-gz.cmake
  18. 10 0
      Tests/RunCMake/CommandLineTar/7zip-lzma.cmake
  19. 10 0
      Tests/RunCMake/CommandLineTar/7zip-xz.cmake
  20. 0 0
      Tests/RunCMake/CommandLineTar/7zip-zstd-unsupported-result.txt
  21. 2 0
      Tests/RunCMake/CommandLineTar/7zip-zstd-unsupported-stderr.txt
  22. 10 0
      Tests/RunCMake/CommandLineTar/7zip-zstd.cmake
  23. 40 2
      Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake
  24. 0 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-7z-result.txt
  25. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-7z-stderr.txt
  26. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-7z-zstd-result.txt
  27. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-7z-zstd-stderr.txt
  28. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-lzma-result.txt
  29. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-lzma-stderr.txt
  30. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-zip-result.txt
  31. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-zip-stderr.txt
  32. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-zip-zstd-result.txt
  33. 1 0
      Tests/RunCMake/CommandLineTar/bad-compression-level-zip-zstd-stderr.txt
  34. 1 1
      Tests/RunCMake/CommandLineTar/bad-format-stderr.txt
  35. 1 1
      Tests/RunCMake/CommandLineTar/bad-wrong-flag-stderr.txt
  36. 10 0
      Tests/RunCMake/CommandLineTar/compression-level-7z-zstd.cmake
  37. 10 0
      Tests/RunCMake/CommandLineTar/compression-level-7z.cmake
  38. 1 1
      Tests/RunCMake/CommandLineTar/compression-level-bz2.cmake
  39. 10 0
      Tests/RunCMake/CommandLineTar/compression-level-lzma.cmake
  40. 10 0
      Tests/RunCMake/CommandLineTar/compression-level-zip-zstd.cmake
  41. 10 0
      Tests/RunCMake/CommandLineTar/compression-level-zip.cmake
  42. 1 1
      Tests/RunCMake/CommandLineTar/end-opt2-stderr.txt
  43. 1 1
      Tests/RunCMake/CommandLineTar/without-files-stderr.txt
  44. 0 1
      Tests/RunCMake/CommandLineTar/zip-bz2-stderr.txt
  45. 1 0
      Tests/RunCMake/CommandLineTar/zip-bz2-unsupported-result.txt
  46. 2 0
      Tests/RunCMake/CommandLineTar/zip-bz2-unsupported-stderr.txt
  47. 10 0
      Tests/RunCMake/CommandLineTar/zip-bz2.cmake
  48. 10 0
      Tests/RunCMake/CommandLineTar/zip-gz.cmake
  49. 1 0
      Tests/RunCMake/CommandLineTar/zip-lzma-unsupported-result.txt
  50. 2 0
      Tests/RunCMake/CommandLineTar/zip-lzma-unsupported-stderr.txt
  51. 10 0
      Tests/RunCMake/CommandLineTar/zip-lzma.cmake
  52. 1 0
      Tests/RunCMake/CommandLineTar/zip-xz-unsupported-result.txt
  53. 2 0
      Tests/RunCMake/CommandLineTar/zip-xz-unsupported-stderr.txt
  54. 10 0
      Tests/RunCMake/CommandLineTar/zip-xz.cmake
  55. 1 0
      Tests/RunCMake/CommandLineTar/zip-zstd-unsupported-result.txt
  56. 2 0
      Tests/RunCMake/CommandLineTar/zip-zstd-unsupported-stderr.txt
  57. 10 0
      Tests/RunCMake/CommandLineTar/zip-zstd.cmake
  58. 1 0
      Tests/RunCMake/File_Archive/RunCMakeTest.cmake
  59. 8 0
      Tests/RunCMake/File_Archive/gnutar-deflate.cmake

+ 12 - 0
Help/command/file.rst

@@ -934,6 +934,12 @@ Archiving
     ``7zip``, ``gnutar``, ``pax``, ``paxr``, ``raw`` and ``zip``.
     If ``FORMAT`` is not given, the default format is ``paxr``.
 
+    The default compression method depends on the format:
+
+    * ``7zip`` uses ``LZMA`` compression
+    * ``zip`` uses ``Deflate`` compression
+    * others uses no compression by default
+
   ``COMPRESSION <compression>``
     Some archive formats allow the type of compression to be specified.
     The ``7zip`` and ``zip`` archive formats already imply a specific type of
@@ -943,6 +949,12 @@ Archiving
 
     * ``None``
     * ``BZip2``
+    * ``Deflate``
+
+      .. versionadded:: 4.3
+
+      This is an alias for ``GZip``.
+
     * ``GZip``
     * ``LZMA``
 

+ 8 - 8
Help/cpack_gen/archive.rst

@@ -4,29 +4,29 @@ CPack Archive Generator
 CPack generator for packaging files into an archive, which can have
 any of the following formats:
 
-- 7Z - 7zip - (``.7z``)
+- 7Z - 7zip - (``.7z``) - LZMA compressed
 
   .. versionadded:: 3.1
 
-- TAR (``.tar``)
+- TAR (``.tar``) - no compression is used
 
   .. versionadded:: 4.0
 
-- TBZ2 (``.tar.bz2``)
+- TBZ2 (``.tar.bz2``) - BZip2 compressed
 
-- TGZ (``.tar.gz``)
+- TGZ (``.tar.gz``) - Deflate compressed
 
-- TXZ (``.tar.xz``)
+- TXZ (``.tar.xz``) - LZMA2 compressed
 
   .. versionadded:: 3.1
 
-- TZ (``.tar.Z``)
+- TZ (``.tar.Z``) - LZW compressed
 
-- TZST (``.tar.zst``)
+- TZST (``.tar.zst``) - Zstandard compressed
 
   .. versionadded:: 3.16
 
-- ZIP (``.zip``)
+- ZIP (``.zip``) - Deflate compressed
 
 When this generator is called from ``CPackSourceConfig.cmake`` (or through
 the ``package_source`` target), then the generated archive will contain all

+ 1 - 1
Help/cpack_gen/deb.rst

@@ -299,7 +299,7 @@ List of CPack DEB generator specific variables:
     bzip2 Burrows–Wheeler algorithm
 
   ``gzip``
-    GNU Gzip compression
+    GNU Gzip Deflate compression
 
   ``zstd``
     .. versionadded:: 3.22

+ 1 - 1
Help/cpack_gen/rpm.rst

@@ -261,7 +261,7 @@ List of CPack RPM generator specific variables:
     bzip2 Burrows–Wheeler algorithm
 
   ``gzip``
-    GNU Gzip compression
+    GNU Gzip Deflate compression
 
   ``zstd``
     .. versionadded:: 3.31

+ 14 - 2
Help/manual/cmake.1.rst

@@ -1450,7 +1450,7 @@ Available commands are:
 
   .. option:: z
 
-    Compress the resulting archive with gzip.
+    Compress the resulting archive with gzip (Deflate).
 
   .. option:: j
 
@@ -1491,6 +1491,18 @@ Available commands are:
     Supported formats are: ``7zip``, ``gnutar``, ``pax``,
     ``paxr`` (restricted pax, default), and ``zip``.
 
+    If the compression method is not specified, the compression method
+    depends on the format:
+
+    * ``7zip`` uses ``LZMA`` compression
+    * ``zip`` uses ``Deflate`` compression
+    * others uses no compression by default
+
+    .. versionadded:: 4.3
+
+      The ``7zip`` and ``zip`` formats support changing the default compression
+      method and compression level.
+
   .. option:: --mtime=<date>
 
     .. versionadded:: 3.1
@@ -1506,7 +1518,7 @@ Available commands are:
     the ``--cmake-tar-compression-level`` option is given.
 
     The ``<compression-level>`` of the ``Zstd`` algorithm can be set
-    between ``0`` and ``19``.
+    between ``0`` and ``19``, except for the ``zip`` format.
 
   .. option:: --cmake-tar-threads=<number>
 

+ 10 - 0
Help/release/dev/cli-tar-7z-algorithms.rst

@@ -0,0 +1,10 @@
+cli-tar-7z-algorithms
+---------------------
+
+* The :manual:`cmake(1)` ``-E tar`` tool supports support compression methods
+  specification for ``7zip`` and ``zip`` formats.
+* The :manual:`cmake(1)` ``-E tar`` tool supports support compression levels
+  specification for ``7zip`` and ``zip`` formats.
+* The :command:`file(ARCHIVE_CREATE)` command's ``COMPRESSION`` option,
+  supports ``Deflate`` compression. The ``Deflate`` compression is
+  an alias for ``GZip``.

+ 1 - 0
Source/Modules/CMakeBuildUtilities.cmake

@@ -258,6 +258,7 @@ if(CMAKE_USE_SYSTEM_LIBARCHIVE)
       IMPORTED_LOCATION "${LibArchive_LIBRARIES}"
       INTERFACE_INCLUDE_DIRECTORIES "${LibArchive_INCLUDE_DIRS}")
   endif ()
+  set(CMake_TEST_LibArchive_VERSION ${LibArchive_VERSION})
 else()
   set(DONT_FAIL_ON_CRC_ERROR OFF)
   set(EXPAT_INCLUDE_DIR ${CMAKE_EXPAT_INCLUDES})

+ 199 - 89
Source/cmArchiveWrite.cxx

@@ -89,6 +89,17 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
   // Upstream fixed an issue with their integer parsing in 3.4.0
   // which would cause spurious errors to be raised from `strtoull`.
 
+  if (archive_write_set_format_by_name(this->Archive, format.c_str()) !=
+      ARCHIVE_OK) {
+    this->Error = cmStrCat("archive_write_set_format_by_name: ",
+                           cm_archive_error_string(this->Archive));
+    return;
+  }
+
+  bool is7zip = (format == "7zip");
+  bool isZip = (format == "zip");
+  bool isFormatSupportsCompressionNatively = (is7zip || isZip);
+
   if (numThreads < 1) {
     int upperLimit = (numThreads == 0) ? std::numeric_limits<int>::max()
                                        : std::abs(numThreads);
@@ -99,122 +110,228 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
 
   std::string sNumThreads = std::to_string(numThreads);
 
-  switch (c) {
-    case CompressNone:
-      if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_none: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
-      break;
-    case CompressCompress:
-      if (archive_write_add_filter_compress(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_compress: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
-      break;
-    case CompressGZip: {
-      if (archive_write_add_filter_gzip(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_gzip: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
-      std::string source_date_epoch;
-      cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
-      if (!source_date_epoch.empty()) {
-        // We're not able to specify an arbitrary timestamp for gzip.
-        // The next best thing is to omit the timestamp entirely.
-        if (archive_write_set_filter_option(this->Archive, "gzip", "timestamp",
-                                            nullptr) != ARCHIVE_OK) {
-          this->Error = cmStrCat("archive_write_set_filter_option: ",
+  if (!isFormatSupportsCompressionNatively) {
+    switch (c) {
+      case CompressNone:
+        if (archive_write_add_filter_none(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_none: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
+        break;
+      case CompressCompress:
+        if (archive_write_add_filter_compress(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_compress: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
+        break;
+      case CompressGZip: {
+        if (archive_write_add_filter_gzip(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_gzip: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
+        std::string source_date_epoch;
+        cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
+        if (!source_date_epoch.empty()) {
+          // We're not able to specify an arbitrary timestamp for gzip.
+          // The next best thing is to omit the timestamp entirely.
+          if (archive_write_set_filter_option(
+                this->Archive, "gzip", "timestamp", nullptr) != ARCHIVE_OK) {
+            this->Error = cmStrCat("archive_write_set_filter_option: ",
+                                   cm_archive_error_string(this->Archive));
+            return;
+          }
+        }
+      } break;
+      case CompressBZip2:
+        if (archive_write_add_filter_bzip2(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_bzip2: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
+        break;
+      case CompressLZMA:
+        if (archive_write_add_filter_lzma(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_lzma: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
+        break;
+      case CompressXZ:
+        if (archive_write_add_filter_xz(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_xz: ",
                                  cm_archive_error_string(this->Archive));
           return;
         }
-      }
-    } break;
-    case CompressBZip2:
-      if (archive_write_add_filter_bzip2(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_bzip2: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
-      break;
-    case CompressLZMA:
-      if (archive_write_add_filter_lzma(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_lzma: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
-      break;
-    case CompressXZ:
-      if (archive_write_add_filter_xz(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_xz: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
 
 #if ARCHIVE_VERSION_NUMBER >= 3004000
 
 #  ifdef _AIX
-      // FIXME: Using more than 2 threads creates an empty archive.
-      // Enforce this limit pending further investigation.
-      if (numThreads > 2) {
-        numThreads = 2;
-        sNumThreads = std::to_string(numThreads);
-      }
+        // FIXME: Using more than 2 threads creates an empty archive.
+        // Enforce this limit pending further investigation.
+        if (numThreads > 2) {
+          numThreads = 2;
+          sNumThreads = std::to_string(numThreads);
+        }
 #  endif
-      if (archive_write_set_filter_option(this->Archive, "xz", "threads",
-                                          sNumThreads.c_str()) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_compressor_xz_options: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
+        if (archive_write_set_filter_option(this->Archive, "xz", "threads",
+                                            sNumThreads.c_str()) !=
+            ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_compressor_xz_options: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
 #endif
 
-      break;
-    case CompressZstd:
-      if (archive_write_add_filter_zstd(this->Archive) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_write_add_filter_zstd: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
+        break;
+      case CompressZstd:
+        if (archive_write_add_filter_zstd(this->Archive) != ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_write_add_filter_zstd: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
 
 #if ARCHIVE_VERSION_NUMBER >= 3006000
-      if (archive_write_set_filter_option(this->Archive, "zstd", "threads",
-                                          sNumThreads.c_str()) != ARCHIVE_OK) {
-        this->Error = cmStrCat("archive_compressor_zstd_options: ",
-                               cm_archive_error_string(this->Archive));
-        return;
-      }
+        if (archive_write_set_filter_option(this->Archive, "zstd", "threads",
+                                            sNumThreads.c_str()) !=
+            ARCHIVE_OK) {
+          this->Error = cmStrCat("archive_compressor_zstd_options: ",
+                                 cm_archive_error_string(this->Archive));
+          return;
+        }
 #endif
-      break;
+        break;
+      case CompressPPMd:
+        this->Error = cmStrCat("PPMd is not supported for ", format);
+        return;
+    }
   }
 
-  if (compressionLevel != 0) {
+  if (isFormatSupportsCompressionNatively || compressionLevel != 0) {
     std::string compressionLevelStr = std::to_string(compressionLevel);
     std::string archiveFilterName;
     switch (c) {
       case CompressNone:
+        if (is7zip || isZip) {
+          archiveFilterName = "store";
+        } else {
+          // Nothing to do - the value should be empty
+        }
+        break;
       case CompressCompress:
+        if (is7zip || isZip) {
+          this->Error =
+            cmStrCat("CompressCompress is not supported for ", format);
+        } else {
+          // Nothing to do - the value should be empty
+        }
         break;
       case CompressGZip:
-        archiveFilterName = "gzip";
+        if (is7zip || isZip) {
+          archiveFilterName = "deflate";
+        } else {
+          archiveFilterName = "gzip";
+        }
         break;
       case CompressBZip2:
+#if ARCHIVE_VERSION_NUMBER < 3008000
+        if (isZip) {
+          this->Error = cmStrCat("BZip2 is not supported for ", format,
+                                 ". Please, build CMake with libarchive 3.8.0 "
+                                 "or newer if you want to use it.");
+          return;
+        }
+#endif
         archiveFilterName = "bzip2";
         break;
       case CompressLZMA:
-        archiveFilterName = "lzma";
+#if ARCHIVE_VERSION_NUMBER < 3008000
+        if (isZip) {
+          this->Error = cmStrCat("LZMA is not supported for ", format,
+                                 ". Please, build CMake with libarchive 3.8.0 "
+                                 "or newer if you want to use it.");
+          return;
+        }
+#endif
+        if (is7zip) {
+          archiveFilterName = "lzma1";
+        } else {
+          archiveFilterName = "lzma";
+        }
         break;
       case CompressXZ:
-        archiveFilterName = "xz";
+#if ARCHIVE_VERSION_NUMBER < 3008000
+        if (isZip) {
+          this->Error = cmStrCat("LZMA2 (XZ) is not supported for ", format,
+                                 ". Please, build CMake with libarchive 3.8.0 "
+                                 "or newer if you want to use it.");
+          return;
+        }
+#endif
+        if (is7zip) {
+          archiveFilterName = "lzma2";
+        } else {
+          archiveFilterName = "xz";
+        }
         break;
       case CompressZstd:
+#if ARCHIVE_VERSION_NUMBER < 3008000
+        if (is7zip || isZip) {
+          this->Error = cmStrCat("Zstd is not supported for ", format,
+                                 ". Please, build CMake with libarchive 3.8.0 "
+                                 "or newer if you want to use it.");
+          return;
+        }
+#endif
         archiveFilterName = "zstd";
         break;
+      case CompressPPMd:
+        if (is7zip) {
+          archiveFilterName = "ppmd";
+        } else {
+          this->Error = cmStrCat("PPMd is not supported for ", format);
+        }
+        return;
     }
-    if (!archiveFilterName.empty()) {
+
+    if (isFormatSupportsCompressionNatively) {
+      if (archiveFilterName.empty()) {
+        this->Error = cmStrCat("Unknown compression method for ", format);
+        return;
+      }
+
+      if (archive_write_set_format_option(
+            this->Archive, format.c_str(), "compression",
+            archiveFilterName.c_str()) != ARCHIVE_OK) {
+        this->Error =
+          cmStrCat("archive_write_set_format_option(compression): ",
+                   cm_archive_error_string(this->Archive));
+        return;
+      }
+
+#if ARCHIVE_VERSION_NUMBER >= 3008000
+      if (archive_write_set_format_option(this->Archive, format.c_str(),
+                                          "threads",
+                                          sNumThreads.c_str()) != ARCHIVE_OK) {
+        this->Error = cmStrCat("archive_write_set_format_option(threads): ",
+                               cm_archive_error_string(this->Archive));
+        return;
+      }
+#endif
+
+      if (compressionLevel != 0) {
+        if (archive_write_set_format_option(
+              this->Archive, format.c_str(), "compression-level",
+              compressionLevelStr.c_str()) != ARCHIVE_OK) {
+          this->Error =
+            cmStrCat("archive_write_set_format_option(compression-level): ",
+                     cm_archive_error_string(this->Archive));
+          return;
+        }
+      }
+    } else if (compressionLevel != 0 && !archiveFilterName.empty()) {
       if (archive_write_set_filter_option(
             this->Archive, archiveFilterName.c_str(), "compression-level",
             compressionLevelStr.c_str()) != ARCHIVE_OK) {
@@ -233,13 +350,6 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
   }
 #endif
 
-  if (archive_write_set_format_by_name(this->Archive, format.c_str()) !=
-      ARCHIVE_OK) {
-    this->Error = cmStrCat("archive_write_set_format_by_name: ",
-                           cm_archive_error_string(this->Archive));
-    return;
-  }
-
   // do not pad the last block!!
   if (archive_write_set_bytes_in_last_block(this->Archive, 1)) {
     this->Error = cmStrCat("archive_write_set_bytes_in_last_block: ",

+ 2 - 1
Source/cmArchiveWrite.h

@@ -49,7 +49,8 @@ public:
     CompressBZip2,
     CompressLZMA,
     CompressXZ,
-    CompressZstd
+    CompressZstd,
+    CompressPPMd,
   };
 
   /** Construct with output stream to which to write archive.  */

+ 1 - 0
Source/cmFileCommand.cxx

@@ -3726,6 +3726,7 @@ bool HandleArchiveCreateCommand(std::vector<std::string> const& args,
   static std::map<std::string, cmSystemTools::cmTarCompression>
     compressionTypeMap = { { "None", cmSystemTools::TarCompressNone },
                            { "BZip2", cmSystemTools::TarCompressBZip2 },
+                           { "Deflate", cmSystemTools::TarCompressGZip },
                            { "GZip", cmSystemTools::TarCompressGZip },
                            { "LZMA", cmSystemTools::TarCompressLZMA },
                            { "LZMA2", cmSystemTools::TarCompressXZ },

+ 10 - 0
Source/cmSystemTools.cxx

@@ -2412,6 +2412,16 @@ bool cmSystemTools::CreateTar(std::string const& outFileName,
     case TarCompressLZMA:
       compress = cmArchiveWrite::CompressLZMA;
       break;
+    case TarCompressAuto:
+      // Kept for backwards compatibility with pre-4.3 versions of CMake
+      if (format == "zip") {
+        compress = cmArchiveWrite::CompressGZip;
+      } else if (format == "7zip") {
+        compress = cmArchiveWrite::CompressLZMA;
+      } else {
+        compress = cmArchiveWrite::CompressNone;
+      }
+      break;
     case TarCompressNone:
       compress = cmArchiveWrite::CompressNone;
       break;

+ 1 - 0
Source/cmSystemTools.h

@@ -550,6 +550,7 @@ public:
     TarCompressLZMA,
     TarCompressXZ,
     TarCompressZstd,
+    TarCompressAuto,
     TarCompressNone
   };
 

+ 6 - 13
Source/cmcmd.cxx

@@ -1589,7 +1589,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
       cmSystemTools::cmTarExtractTimestamps extractTimestamps =
         cmSystemTools::cmTarExtractTimestamps::Yes;
       cmSystemTools::cmTarCompression compress =
-        cmSystemTools::TarCompressNone;
+        cmSystemTools::TarCompressAuto;
       int nCompress = 0;
       bool doing_options = true;
       for (auto const& arg : cmMakeRange(args).advance(4)) {
@@ -1717,18 +1717,13 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
           }
         }
       }
-      if ((format == "7zip" || format == "zip") && nCompress > 0) {
-        cmSystemTools::Error("Can not use compression flags with format: " +
-                             format);
-        return 1;
-      }
       if (nCompress > 1) {
         cmSystemTools::Error("Can only compress a tar file one way; "
                              "at most one flag of z, j, or J may be used");
         return 1;
       }
       if (compressionLevelFlagPassed) {
-        if (nCompress == 0) {
+        if (nCompress == 0 && format != "zip" && format != "7zip") {
           cmSystemTools::Error("Can not use --cmake-tar-compression-level "
                                "without compression algorithm selection");
           return 1;
@@ -1736,17 +1731,15 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
 
         constexpr int minCompressionLevel = 0;
         int maxCompressionLevel = 9;
-        if (compress == cmSystemTools::TarCompressZstd) {
+        if (compress == cmSystemTools::TarCompressZstd && format != "zip") {
           maxCompressionLevel = 19;
         }
 
         if (compressionLevel < minCompressionLevel ||
             compressionLevel > maxCompressionLevel) {
-          cmSystemTools::Error(
-            cmStrCat("Compression level must be between ",
-                     std::to_string(minCompressionLevel), " and ",
-                     std::to_string(maxCompressionLevel), ". Got ",
-                     std::to_string(compressionLevel)));
+          cmSystemTools::Error(cmStrCat(
+            "Compression level must be between ", minCompressionLevel, " and ",
+            maxCompressionLevel, ". Got ", compressionLevel));
           return 1;
         }
       }

+ 3 - 0
Tests/RunCMake/CMakeLists.txt

@@ -1071,6 +1071,9 @@ add_RunCMake_test(CommandLine -DLLVM_RC=$<TARGET_FILE:pseudo_llvm-rc> -DCMAKE_SY
                   -DCYGWIN=${CYGWIN} -DMSYS=${MSYS} -DPython_EXECUTABLE=${Python_EXECUTABLE}
                   -DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>
                   -DPRINT_STDIN_EXE=$<TARGET_FILE:print_stdin>)
+if(CMake_TEST_LibArchive_VERSION)
+  list(APPEND CommandLineTar_ARGS -DCMake_TEST_LibArchive_VERSION=${CMake_TEST_LibArchive_VERSION})
+endif()
 add_RunCMake_test(CommandLineTar)
 
 if(CMAKE_PLATFORM_NO_VERSIONED_SONAME OR (NOT CMAKE_SHARED_LIBRARY_SONAME_FLAG AND NOT CMAKE_SHARED_LIBRARY_SONAME_C_FLAG))

+ 10 - 0
Tests/RunCMake/CommandLineTar/7zip-bz2.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS cvjf)
+set(COMPRESSION_OPTIONS --format=7zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 0 - 1
Tests/RunCMake/CommandLineTar/7zip-gz-stderr.txt

@@ -1 +0,0 @@
-CMake Error: Can not use compression flags with format: 7zip

+ 10 - 0
Tests/RunCMake/CommandLineTar/7zip-gz.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS -cvzf)
+set(COMPRESSION_OPTIONS --format=7zip)
+
+set(DECOMPRESSION_FLAGS -xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/7zip-lzma.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS -cvf)
+set(COMPRESSION_OPTIONS --format=7zip --lzma)
+
+set(DECOMPRESSION_FLAGS -xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/7zip-xz.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS cvJf)
+set(COMPRESSION_OPTIONS --format=7zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 0 - 0
Tests/RunCMake/CommandLineTar/7zip-gz-result.txt → Tests/RunCMake/CommandLineTar/7zip-zstd-unsupported-result.txt


+ 2 - 0
Tests/RunCMake/CommandLineTar/7zip-zstd-unsupported-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Zstd is not supported for 7zip. Please, build CMake with libarchive 3.8.0 or newer if you want to use it.
+CMake Error: Problem creating tar: bad.7z$

+ 10 - 0
Tests/RunCMake/CommandLineTar/7zip-zstd.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --zstd --format=7zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 40 - 2
Tests/RunCMake/CommandLineTar/RunCMakeTest.cmake

@@ -1,5 +1,12 @@
 include(RunCMake)
 
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_BINARY_DIR}")
+
+set(test_3_8_0 1)
+if(CMake_TEST_LibArchive_VERSION AND CMake_TEST_LibArchive_VERSION VERSION_LESS 3.8.0)
+  set(test_3_8_0 0)
+endif()
+
 function(external_command_test NAME)
   run_cmake_command(${NAME} ${CMAKE_COMMAND} -E ${ARGN})
 endfunction()
@@ -19,10 +26,17 @@ external_command_test(end-opt1           tar cvf bad.tar -- --bad)
 external_command_test(end-opt2           tar cvf bad.tar --)
 external_command_test(mtime              tar cvf bad.tar "--mtime=1970-01-01 00:00:00 UTC" ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
 external_command_test(bad-format         tar cvf bad.tar "--format=bad-format" ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
-external_command_test(zip-bz2            tar cvjf bad.tar "--format=zip" ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
-external_command_test(7zip-gz            tar cvzf bad.tar "--format=7zip" ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
 
 run_cmake(7zip)
+run_cmake(7zip-bz2)
+run_cmake(7zip-gz)
+run_cmake(7zip-lzma)
+run_cmake(7zip-xz)
+if (test_3_8_0)
+  run_cmake(7zip-zstd)
+else()
+  external_command_test(7zip-zstd-unsupported tar cvf bad.7z --zstd --format=7zip ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
+endif()
 run_cmake(gnutar)
 run_cmake(gnutar-gz)
 run_cmake(pax)
@@ -32,6 +46,18 @@ run_cmake(pax-zstd)
 run_cmake(paxr)
 run_cmake(paxr-bz2)
 run_cmake(zip)
+run_cmake(zip-gz)
+if (test_3_8_0)
+  run_cmake(zip-bz2)
+  run_cmake(zip-lzma)
+  run_cmake(zip-xz)
+  run_cmake(zip-zstd)
+else()
+  external_command_test(zip-bz2-unsupported   tar cvjf bad.zip --format=zip ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
+  external_command_test(zip-lzma-unsupported  tar cvf bad.zip --lzma --format=zip ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
+  external_command_test(zip-xz-unsupported    tar cvJf bad.zip --format=zip ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
+  external_command_test(zip-zstd-unsupported  tar cvf bad.zip --zstd --format=zip ${CMAKE_CURRENT_LIST_DIR}/test-file.txt)
+endif()
 
 # Check the --cmake-tar-threads option
 external_command_test(bad-threads-not-a-number  tar cvf bad.tar --cmake-tar-threads=nan .)
@@ -48,13 +74,25 @@ external_command_test(bad-compression-level-no-compression  tar cvf bad.tar --cm
 external_command_test(bad-compression-level-not-a-number  tar cvjf bad.tar --cmake-tar-compression-level=nan .)
 external_command_test(bad-compression-level-bz2  tar cvjf bad.tar --cmake-tar-compression-level=10 .)
 external_command_test(bad-compression-level-gz  tar cvzf bad.tar --cmake-tar-compression-level=10 .)
+external_command_test(bad-compression-level-lzma  tar cvf bad.tar --lzma --cmake-tar-compression-level=10 .)
 external_command_test(bad-compression-level-xz  tar cvJf bad.tar --cmake-tar-compression-level=10 .)
 external_command_test(bad-compression-level-zstd  tar cvf bad.tar --zstd --cmake-tar-compression-level=20 .)
+external_command_test(bad-compression-level-7z  tar cvf bad.7z --format=7zip --cmake-tar-compression-level=10 .)
+external_command_test(bad-compression-level-7z-zstd  tar cvf bad.7z --format=7zip --zstd --cmake-tar-compression-level=20 .)
+external_command_test(bad-compression-level-zip  tar cvf bad.zip --format=zip --cmake-tar-compression-level=10 .)
+external_command_test(bad-compression-level-zip-zstd  tar cvf bad.zip --format=zip --zstd --cmake-tar-compression-level=10 .)
 
 run_cmake(compression-level-bz2)
 run_cmake(compression-level-gz)
 run_cmake(compression-level-xz)
+run_cmake(compression-level-lzma)
 run_cmake(compression-level-zstd)
+run_cmake(compression-level-7z)
+run_cmake(compression-level-zip)
+if (test_3_8_0)
+  run_cmake(compression-level-7z-zstd)
+  run_cmake(compression-level-zip-zstd)
+endif()
 
 # Extracting only selected files or directories
 run_cmake(zip-filtered)

+ 0 - 0
Tests/RunCMake/CommandLineTar/zip-bz2-result.txt → Tests/RunCMake/CommandLineTar/bad-compression-level-7z-result.txt


+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-7z-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Compression level must be between 0 and 9. Got 10$

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-7z-zstd-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-7z-zstd-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Compression level must be between 0 and 19. Got 20$

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-lzma-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-lzma-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Compression level must be between 0 and 9. Got 10$

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-zip-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-zip-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Compression level must be between 0 and 9. Got 10$

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-zip-zstd-result.txt

@@ -0,0 +1 @@
+1

+ 1 - 0
Tests/RunCMake/CommandLineTar/bad-compression-level-zip-zstd-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Compression level must be between 0 and 9. Got 10$

+ 1 - 1
Tests/RunCMake/CommandLineTar/bad-format-stderr.txt

@@ -1 +1 @@
-CMake Error: Unknown -E tar --format= argument: bad-format
+^CMake Error: Unknown -E tar --format= argument: bad-format$

+ 1 - 1
Tests/RunCMake/CommandLineTar/bad-wrong-flag-stderr.txt

@@ -1 +1 @@
-^tar: Unknown argument: q
+^tar: Unknown argument: q$

+ 10 - 0
Tests/RunCMake/CommandLineTar/compression-level-7z-zstd.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --cmake-tar-compression-level=19 --format=7zip --zstd)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/compression-level-7z.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.7z")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --cmake-tar-compression-level=9 --format=7zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("377abcaf271c" LIMIT 6 HEX)

+ 1 - 1
Tests/RunCMake/CommandLineTar/compression-level-bz2.cmake

@@ -1,7 +1,7 @@
 set(OUTPUT_NAME "test.tar.bz2")
 
 set(COMPRESSION_FLAGS cvjf)
-set(COMPRESSION_OPTIONS --cmake-tar-threads=9)
+set(COMPRESSION_OPTIONS --cmake-tar-compression-level=9)
 
 set(DECOMPRESSION_FLAGS xvjf)
 

+ 10 - 0
Tests/RunCMake/CommandLineTar/compression-level-lzma.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.tar.zstd")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --format=pax --lzma --cmake-tar-compression-level=9)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("5d0000[08]00[04]" LIMIT 5 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/compression-level-zip-zstd.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --cmake-tar-compression-level=9 --format=zip --zstd)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/compression-level-zip.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --cmake-tar-compression-level=9 --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 1 - 1
Tests/RunCMake/CommandLineTar/end-opt2-stderr.txt

@@ -1 +1 @@
-^tar: No files or directories specified
+^tar: No files or directories specified$

+ 1 - 1
Tests/RunCMake/CommandLineTar/without-files-stderr.txt

@@ -1 +1 @@
-^tar: No files or directories specified
+^tar: No files or directories specified$

+ 0 - 1
Tests/RunCMake/CommandLineTar/zip-bz2-stderr.txt

@@ -1 +0,0 @@
-CMake Error: Can not use compression flags with format: zip

+ 1 - 0
Tests/RunCMake/CommandLineTar/zip-bz2-unsupported-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CommandLineTar/zip-bz2-unsupported-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: BZip2 is not supported for zip. Please, build CMake with libarchive 3.8.0 or newer if you want to use it.
+CMake Error: Problem creating tar: bad.zip$

+ 10 - 0
Tests/RunCMake/CommandLineTar/zip-bz2.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvjf)
+set(COMPRESSION_OPTIONS --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 10 - 0
Tests/RunCMake/CommandLineTar/zip-gz.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS -cvzf)
+set(COMPRESSION_OPTIONS --format=zip)
+
+set(DECOMPRESSION_FLAGS -xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 1 - 0
Tests/RunCMake/CommandLineTar/zip-lzma-unsupported-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CommandLineTar/zip-lzma-unsupported-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: LZMA is not supported for zip. Please, build CMake with libarchive 3.8.0 or newer if you want to use it.
+CMake Error: Problem creating tar: bad.zip$

+ 10 - 0
Tests/RunCMake/CommandLineTar/zip-lzma.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --lzma --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 1 - 0
Tests/RunCMake/CommandLineTar/zip-xz-unsupported-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CommandLineTar/zip-xz-unsupported-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: LZMA2 \(XZ\) is not supported for zip. Please, build CMake with libarchive 3.8.0 or newer if you want to use it.
+CMake Error: Problem creating tar: bad.zip$

+ 10 - 0
Tests/RunCMake/CommandLineTar/zip-xz.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvJf)
+set(COMPRESSION_OPTIONS --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 1 - 0
Tests/RunCMake/CommandLineTar/zip-zstd-unsupported-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/CommandLineTar/zip-zstd-unsupported-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Zstd is not supported for zip. Please, build CMake with libarchive 3.8.0 or newer if you want to use it.
+CMake Error: Problem creating tar: bad.zip$

+ 10 - 0
Tests/RunCMake/CommandLineTar/zip-zstd.cmake

@@ -0,0 +1,10 @@
+set(OUTPUT_NAME "test.zip")
+
+set(COMPRESSION_FLAGS cvf)
+set(COMPRESSION_OPTIONS --zstd --format=zip)
+
+set(DECOMPRESSION_FLAGS xvf)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("504b0304" LIMIT 4 HEX)

+ 1 - 0
Tests/RunCMake/File_Archive/RunCMakeTest.cmake

@@ -3,6 +3,7 @@ include(RunCMake)
 run_cmake(7zip)
 run_cmake(gnutar)
 run_cmake(gnutar-gz)
+run_cmake(gnutar-deflate)
 run_cmake(pax)
 run_cmake(pax-lzma)
 run_cmake(pax-lzma2)

+ 8 - 0
Tests/RunCMake/File_Archive/gnutar-deflate.cmake

@@ -0,0 +1,8 @@
+set(OUTPUT_NAME "test.tar.gz")
+
+set(ARCHIVE_FORMAT gnutar)
+set(COMPRESSION_TYPE Deflate)
+
+include(${CMAKE_CURRENT_LIST_DIR}/roundtrip.cmake)
+
+check_magic("1f8b" LIMIT 2 HEX)