From bc1c295b9584b3cbae3132da3d6ee9a872e9f5a5 Mon Sep 17 00:00:00 2001 From: Sergiu Deitsch Date: Fri, 29 Aug 2025 23:30:30 +0200 Subject: [PATCH] CPack: Add support for multiple checksums per package Closes: #27174 --- Help/release/dev/cpack-several-checksums.rst | 4 ++ Modules/CPack.cmake | 7 +- Source/CPack/cmCPackGenerator.cxx | 69 +++++++++++++++---- Tests/RunCMake/CPack/RunCMakeTest.cmake | 1 + .../ExpectedFiles.cmake | 2 + .../VerifyResult.cmake | 14 ++++ .../PACKAGE_CHECKSUM_MULTIPLE/test.cmake | 2 + 7 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 Help/release/dev/cpack-several-checksums.rst create mode 100644 Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/ExpectedFiles.cmake create mode 100644 Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/VerifyResult.cmake create mode 100644 Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/test.cmake diff --git a/Help/release/dev/cpack-several-checksums.rst b/Help/release/dev/cpack-several-checksums.rst new file mode 100644 index 0000000000..2ce34bba83 --- /dev/null +++ b/Help/release/dev/cpack-several-checksums.rst @@ -0,0 +1,4 @@ +cpack-several-checksums +----------------------- + +* :variable:`CPACK_PACKAGE_CHECKSUM` now supports multiple values. diff --git a/Modules/CPack.cmake b/Modules/CPack.cmake index 2da32164ba..0be4846127 100644 --- a/Modules/CPack.cmake +++ b/Modules/CPack.cmake @@ -204,8 +204,8 @@ installers. The most commonly-used variables are: .. versionadded:: 3.7 - An algorithm that will be used to generate an additional file with the - checksum of the package. The output file name will be: + One or multiple algorithms that will be used to generate additional files with + the checksum of the package. The output file names will be: .. code-block:: cmake @@ -214,6 +214,9 @@ installers. The most commonly-used variables are: Supported algorithms are those listed by the :ref:`string(\) ` command. + .. versionchanged:: 4.2 + The variable accepts a list of algorithms. + .. variable:: CPACK_PROJECT_CONFIG_FILE CPack-time project CPack configuration file. This file is included at cpack diff --git a/Source/CPack/cmCPackGenerator.cxx b/Source/CPack/cmCPackGenerator.cxx index e30a3435bb..efe15c2cb0 100644 --- a/Source/CPack/cmCPackGenerator.cxx +++ b/Source/CPack/cmCPackGenerator.cxx @@ -3,6 +3,7 @@ #include "cmCPackGenerator.h" #include +#include #include #include @@ -174,14 +175,42 @@ int cmCPackGenerator::PrepareNames() return 0; } - // Check algorithm for calculating the checksum of the package. - cmValue algoSignature = this->GetOption("CPACK_PACKAGE_CHECKSUM"); - if (algoSignature) { - if (!cmCryptoHash::New(*algoSignature)) { - cmCPackLogger(cmCPackLog::LOG_ERROR, - "Cannot recognize algorithm: " << algoSignature - << std::endl); - return 0; + // Check algorithms for calculating the checksum of the package. + cmValue algoSignatures = this->GetOption("CPACK_PACKAGE_CHECKSUM"); + if (cmNonempty(algoSignatures)) { + cmList algoList{ algoSignatures }; + // Workout unique algorithms and duplicates for diagnostic purposes + algoList.sort(); + // Store a copy since std::unique modifies the sequence + cmList const sortedAlgoList = algoList; + auto const newEnd = std::unique(algoList.begin(), algoList.end()); + + if (newEnd != algoList.end()) { + cmList duplicatesAlgoList; + + std::set_difference(sortedAlgoList.begin(), sortedAlgoList.end(), + algoList.begin(), newEnd, + std::back_inserter(duplicatesAlgoList)); + // Make sure to output duplicates a single time even if these appear more + // than two times. Exploit the already sorted sequence to determine the + // unique elements. + duplicatesAlgoList.erase( + std::unique(duplicatesAlgoList.begin(), duplicatesAlgoList.end()), + duplicatesAlgoList.end()); + + cmCPackLogger(cmCPackLog::LOG_WARNING, + "Algorithm specified multiple times: " + << duplicatesAlgoList.join(", ") << std::endl); + } + + algoList.erase(newEnd, algoList.end()); + + for (std::string const& algo : algoList) { + if (!cmCryptoHash::New(algo)) { + cmCPackLogger(cmCPackLog::LOG_ERROR, + "Cannot recognize algorithm: " << algo << std::endl); + return 0; + } } } @@ -1200,8 +1229,22 @@ int cmCPackGenerator::DoPackage() } /* Prepare checksum algorithm*/ - cmValue algo = this->GetOption("CPACK_PACKAGE_CHECKSUM"); - std::unique_ptr crypto = cmCryptoHash::New(*algo); + cmValue algoSignatures = this->GetOption("CPACK_PACKAGE_CHECKSUM"); + std::vector> crypto; + + if (cmNonempty(algoSignatures)) { + cmList algoList{ algoSignatures }; + // Keep unique algorithms since generating the same checksum multiple times + // is not meaningful. + algoList.remove_duplicates(); + crypto.reserve(algoList.size()); + + for (std::string const& algo : algoList) { + if (std::unique_ptr hash = cmCryptoHash::New(algo)) { + crypto.push_back(std::move(hash)); + } + } + } /* * Copy the generated packages to final destination @@ -1218,9 +1261,9 @@ int cmCPackGenerator::DoPackage() if (!this->CopyPackageFile(pkgFileName, filename)) { return 0; } - /* Generate checksum file */ - if (crypto) { - if (!this->GenerateChecksumFile(*crypto, filename)) { + /* Generate checksum files */ + for (std::unique_ptr const& hash : crypto) { + if (!this->GenerateChecksumFile(*hash, filename)) { return 0; } } diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index b6cacaa11f..83bf1366f8 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -41,6 +41,7 @@ run_cpack_test_package_target(MINIMAL "RPM.MINIMAL;DEB.MINIMAL;7Z;TBZ2;TGZ;TXZ;T run_cpack_test_package_target(THREADED_ALL "TXZ;DEB" false "MONOLITHIC;COMPONENT") run_cpack_test_package_target(THREADED "TXZ;DEB" false "MONOLITHIC;COMPONENT") run_cpack_test_subtests(PACKAGE_CHECKSUM "invalid;MD5;SHA1;SHA224;SHA256;SHA384;SHA512" "TGZ" false "MONOLITHIC") +run_cpack_test(PACKAGE_CHECKSUM_MULTIPLE "TGZ" false "MONOLITHIC") run_cpack_test(PARTIALLY_RELOCATABLE_WARNING "RPM.PARTIALLY_RELOCATABLE_WARNING" false "COMPONENT") run_cpack_test(PER_COMPONENT_FIELDS "RPM.PER_COMPONENT_FIELDS;DEB.PER_COMPONENT_FIELDS" false "COMPONENT") run_cpack_test_subtests(SINGLE_DEBUGINFO "no_main_component" "RPM.SINGLE_DEBUGINFO" true "CUSTOM") diff --git a/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/ExpectedFiles.cmake new file mode 100644 index 0000000000..d1a3a5fb2d --- /dev/null +++ b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/ExpectedFiles.cmake @@ -0,0 +1,2 @@ +set(EXPECTED_FILES_COUNT "1") +set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt") diff --git a/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/VerifyResult.cmake new file mode 100644 index 0000000000..9ad644004a --- /dev/null +++ b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/VerifyResult.cmake @@ -0,0 +1,14 @@ +set(hash_algos MD5 SHA1 SHA224 SHA256 SHA384 SHA512) + +file(GLOB PACKAGE RELATIVE "${bin_dir}" "*.tar.gz") + +foreach(algo IN LISTS hash_algos) + string(TOLOWER ${algo} CHECKSUM_EXTENSION) + file(STRINGS ${PACKAGE}.${CHECKSUM_EXTENSION} CHSUM_VALUE) + file(${algo} ${PACKAGE} expected_value) + set(expected_value "${expected_value} ${PACKAGE}") + + if(NOT expected_value STREQUAL CHSUM_VALUE) + message(FATAL_ERROR "Generated checksum is not valid! Expected [${expected_value}] Got [${CHSUM_VALUE}]") + endif() +endforeach() diff --git a/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/test.cmake b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/test.cmake new file mode 100644 index 0000000000..54eb20075f --- /dev/null +++ b/Tests/RunCMake/CPack/tests/PACKAGE_CHECKSUM_MULTIPLE/test.cmake @@ -0,0 +1,2 @@ +install(FILES CMakeLists.txt DESTINATION foo) +set(CPACK_PACKAGE_CHECKSUM MD5 SHA1 SHA224 SHA256 SHA384 SHA512)