Commit bccbf9a7 authored by Brad King's avatar Brad King Committed by Kitware Robot
Browse files

Merge topic 'reproducible-tarballs'

548ac51d

 CPack/Deb: Support SOURCE_DATE_EPOCH when packaging files
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !2226
parents 131fcf65 548ac51d
Pipeline #110452 failed with stage
in 0 seconds
......@@ -527,3 +527,11 @@ alternate data stream (ADT) is used.
When a filesystem without ADT support is used only owner read/write
permissions can be preserved.
Reproducible packages
^^^^^^^^^^^^^^^^^^^^^
The environment variable ``SOURCE_DATE_EPOCH`` may be set to a UNIX
timestamp, defined as the number of seconds, excluding leap seconds,
since 01 Jan 1970 00:00:00 UTC. If set, the CPack Deb generator will
use its value for timestamps in the package.
cpack-deb-source-date-epoch
---------------------------
* The :cpack_gen:`CPack Deb Generator` learned to honor the ``SOURCE_DATE_EPOCH``
environment variable when packaging files. This is useful for generating
reproducible packages.
......@@ -10,6 +10,7 @@
#include "cmsys/Encoding.hxx"
#include "cmsys/FStream.hxx"
#include <iostream>
#include <sstream>
#include <string.h>
#include <time.h>
......@@ -94,13 +95,25 @@ cmArchiveWrite::cmArchiveWrite(std::ostream& os, Compress c,
return;
}
break;
case CompressGZip:
case CompressGZip: {
if (archive_write_add_filter_gzip(this->Archive) != ARCHIVE_OK) {
this->Error = "archive_write_add_filter_gzip: ";
this->Error += cm_archive_error_string(this->Archive);
return;
}
break;
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 = "archive_write_set_filter_option: ";
this->Error += cm_archive_error_string(this->Archive);
return;
}
}
} break;
case CompressBZip2:
if (archive_write_add_filter_bzip2(this->Archive) != ARCHIVE_OK) {
this->Error = "archive_write_add_filter_bzip2: ";
......@@ -243,6 +256,17 @@ bool cmArchiveWrite::AddFile(const char* file, size_t skip, const char* prefix)
return false;
}
archive_entry_set_mtime(e, t, 0);
} else {
std::string source_date_epoch;
cmSystemTools::GetEnv("SOURCE_DATE_EPOCH", source_date_epoch);
if (!source_date_epoch.empty()) {
std::istringstream iss(source_date_epoch);
time_t epochTime;
iss >> epochTime;
if (iss.eof() && !iss.fail()) {
archive_entry_set_mtime(e, epochTime, 0);
}
}
}
// manages the uid/guid of the entry (if any)
......
......@@ -78,8 +78,12 @@ function(run_cpack_test_common_ TEST_NAME types build SUBTEST_SUFFIX source PACK
endif()
# execute cpack
set(SETENV)
if(ENVIRONMENT)
set(SETENV ${CMAKE_COMMAND} -E env "${ENVIRONMENT}")
endif()
execute_process(
COMMAND ${cpack_command_}
COMMAND ${SETENV} ${cpack_command_}
WORKING_DIRECTORY "${RunCMake_TEST_BINARY_DIR}"
RESULT_VARIABLE "result_"
OUTPUT_FILE "${RunCMake_TEST_BINARY_DIR}/test_output.txt"
......
set(ALL_FILES_GLOB "*.deb")
function(getPackageContent FILE RESULT_VAR)
execute_process(COMMAND ${DPKG_EXECUTABLE} -c "${FILE}"
execute_process(COMMAND ${CMAKE_COMMAND} -E env TZ=Etc/UTC ${DPKG_EXECUTABLE} -c "${FILE}"
OUTPUT_VARIABLE package_content_
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
......
......@@ -28,6 +28,9 @@ run_cpack_test(EXTRA_SLASH_IN_PATH "RPM" true "COMPONENT")
run_cpack_source_test(SOURCE_PACKAGE "RPM")
run_cpack_test(SUGGESTS "RPM" false "MONOLITHIC")
run_cpack_test(SYMLINKS "RPM;TGZ" false "MONOLITHIC;COMPONENT")
set(ENVIRONMENT "SOURCE_DATE_EPOCH=123456789")
run_cpack_test(TIMESTAMPS "DEB;TGZ" false "COMPONENT")
unset(ENVIRONMENT)
run_cpack_test(USER_FILELIST "RPM" false "MONOLITHIC")
run_cpack_test(MD5SUMS "DEB" false "MONOLITHIC;COMPONENT")
run_cpack_test(CPACK_INSTALL_SCRIPT "ZIP" false "MONOLITHIC")
......
set(EXPECTED_FILES_COUNT "1")
set(EXPECTED_FILE_CONTENT_1_LIST "/foo;/foo/CMakeLists.txt")
macro(getFileMetadata_ FILE RESULT_VAR)
if(GENERATOR_TYPE STREQUAL "TGZ")
# getPackageContent defined for archives omit the metadata (non-verbose)
execute_process(COMMAND ${CMAKE_COMMAND} -E env TZ=Etc/UTC ${CMAKE_COMMAND} -E tar -xtvf ${FILE}
OUTPUT_VARIABLE ${RESULT_VAR}
ERROR_QUIET
OUTPUT_STRIP_TRAILING_WHITESPACE)
else()
getPackageContent("${FILE}" ${RESULT_VAR})
endif()
endmacro()
function(checkContentTimestamp FILE REGEX)
getFileMetadata_("${FILE}" METADATA_)
if(NOT METADATA_ MATCHES "${REGEX}")
string(REPLACE "\n" "\n " metadata_indented "${METADATA_}")
message(FATAL_ERROR
"Wrong timestamps in file:\n"
" ${FILE}\n"
"Expected timestamps to match:\n"
" ${REGEX}\n"
"Actual timestamps:\n"
" ${metadata_indented}")
endif()
endfunction()
function(checkTimestamp FILE_NAME)
file(READ ${FILE_NAME} ACTUAL_TIMESTAMP OFFSET 4 LIMIT 4 HEX)
if(NOT ACTUAL_TIMESTAMP STREQUAL "00000000")
message(FATAL_ERROR "${FILE_NAME} contains a timestamp [0x${ACTUAL_TIMESTAMP}]")
endif()
endfunction()
# Expected timestamp is UNIX time 123456789
if(GENERATOR_TYPE STREQUAL "TGZ")
set(EXPECTED_TIMESTAMP "29 Nov +1973")
set(EXPECTED_FILES foo/ foo/CMakeLists.txt)
else()
set(EXPECTED_TIMESTAMP "1973-11-29 21:33")
set(EXPECTED_FILES ./usr/ ./usr/foo/ ./usr/foo/CMakeLists.txt)
endif()
set(EXPECTED_METADATA)
foreach(FILE ${EXPECTED_FILES})
list(APPEND EXPECTED_METADATA ".* ${EXPECTED_TIMESTAMP} ${FILE}")
endforeach()
list(JOIN EXPECTED_METADATA ".*" EXPECTED_REGEX)
checkContentTimestamp("${FOUND_FILE_1}" "${EXPECTED_REGEX}")
if(GENERATOR_TYPE STREQUAL "TGZ")
checkTimestamp("${FOUND_FILE_1}")
else()
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${FOUND_FILE_1}")
checkTimestamp("data.tar.gz")
checkTimestamp("control.tar.gz")
endif()
install(FILES CMakeLists.txt DESTINATION foo COMPONENT test)
set(CPACK_COMPONENTS_ALL test)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment