diff options
-rw-r--r-- | Help/cpack_gen/deb.rst | 8 | ||||
-rw-r--r-- | Help/dev/maint.rst | 9 | ||||
-rw-r--r-- | Help/release/dev/cpack-deb-source-date-epoch.rst | 6 | ||||
-rw-r--r-- | Modules/GetPrerequisites.cmake | 18 | ||||
-rw-r--r-- | Modules/GoogleTest.cmake | 7 | ||||
-rw-r--r-- | Source/CMakeVersion.cmake | 2 | ||||
-rw-r--r-- | Source/cmArchiveWrite.cxx | 28 | ||||
-rw-r--r-- | Source/cmLocalGenerator.cxx | 142 | ||||
-rw-r--r-- | Source/cmLocalGenerator.h | 10 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/CPackTestHelpers.cmake | 6 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/DEB/Helpers.cmake | 2 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/RunCMakeTest.cmake | 3 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/tests/TIMESTAMPS/ExpectedFiles.cmake | 2 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/tests/TIMESTAMPS/VerifyResult.cmake | 58 | ||||
-rw-r--r-- | Tests/RunCMake/CPack/tests/TIMESTAMPS/test.cmake | 3 |
15 files changed, 215 insertions, 89 deletions
diff --git a/Help/cpack_gen/deb.rst b/Help/cpack_gen/deb.rst index c5923be..37d750d 100644 --- a/Help/cpack_gen/deb.rst +++ b/Help/cpack_gen/deb.rst @@ -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. diff --git a/Help/dev/maint.rst b/Help/dev/maint.rst index ec76479..a8942cd 100644 --- a/Help/dev/maint.rst +++ b/Help/dev/maint.rst @@ -51,15 +51,18 @@ using a local branch named ``release-$ver``, where ``$ver`` is the version number of the current release in the form ``$major.$minor``. It is always merged into ``master`` before publishing. -To merge some ``$topic`` branch into ``release``, first create the local -branch: +Before merging a ``$topic`` branch into ``release``, verify that the +``$topic`` branch has already been merged to ``master`` via the usual +``Do: merge`` process. Then, to merge the ``$topic`` branch into +``release``, start by creating the local branch: .. code-block:: shell git fetch origin git checkout -b release-$ver origin/release -Merge the ``$topic`` branch into the local ``release-$ver`` branch: +Merge the ``$topic`` branch into the local ``release-$ver`` branch, making +sure to include a ``Merge-request: !xxxx`` footer in the commit message: .. code-block:: shell diff --git a/Help/release/dev/cpack-deb-source-date-epoch.rst b/Help/release/dev/cpack-deb-source-date-epoch.rst new file mode 100644 index 0000000..b276569 --- /dev/null +++ b/Help/release/dev/cpack-deb-source-date-epoch.rst @@ -0,0 +1,6 @@ +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. diff --git a/Modules/GetPrerequisites.cmake b/Modules/GetPrerequisites.cmake index d397791..ca71009 100644 --- a/Modules/GetPrerequisites.cmake +++ b/Modules/GetPrerequisites.cmake @@ -275,7 +275,6 @@ function(gp_item_default_embedded_path item default_embedded_path_var) # as the executable by default: # set(path "@executable_path") - set(overridden 0) # On the Mac, relative to the executable depending on the type # of the thing we are embedding: @@ -294,20 +293,11 @@ function(gp_item_default_embedded_path item default_embedded_path_var) # set(path "@executable_path/../../Contents/MacOS") - # Embed .dylibs right next to the main bundle executable: + # Embed frameworks and .dylibs in the embedded "Frameworks" directory + # (sibling of MacOS): # - if(item MATCHES "\\.dylib$") - set(path "@executable_path/../MacOS") - set(overridden 1) - endif() - - # Embed frameworks in the embedded "Frameworks" directory (sibling of MacOS): - # - if(NOT overridden) - if(item MATCHES "[^/]+\\.framework/") - set(path "@executable_path/../Frameworks") - set(overridden 1) - endif() + if(item MATCHES "[^/]+\\.framework/" OR item MATCHES "\\.dylib$") + set(path "@executable_path/../Frameworks") endif() endif() diff --git a/Modules/GoogleTest.cmake b/Modules/GoogleTest.cmake index bfb83e1..2c9ee11 100644 --- a/Modules/GoogleTest.cmake +++ b/Modules/GoogleTest.cmake @@ -238,6 +238,10 @@ same as the Google Test name (i.e. ``suite.testcase``); see also #]=======================================================================] +# Save project's policies +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) # if IN_LIST + #------------------------------------------------------------------------------ function(gtest_add_tests) @@ -454,3 +458,6 @@ endfunction() set(_GOOGLETEST_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/GoogleTestAddTests.cmake ) + +# Restore project's policies +cmake_policy(POP) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 5649c0b..8d037e4 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 12) -set(CMake_VERSION_PATCH 20180725) +set(CMake_VERSION_PATCH 20180727) #set(CMake_VERSION_RC 1) diff --git a/Source/cmArchiveWrite.cxx b/Source/cmArchiveWrite.cxx index 1dd7734..6031781 100644 --- a/Source/cmArchiveWrite.cxx +++ b/Source/cmArchiveWrite.cxx @@ -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) diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index 072b958..647697a 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -844,12 +844,11 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, cmGeneratorTarget const* target, const std::string& lang, const std::string& config, - bool stripImplicitInclDirs) const + bool stripImplicitDirs, + bool appendAllImplicitDirs) const { - // Need to decide whether to automatically include the source and - // binary directories at the beginning of the include path. - bool includeSourceDir = false; - bool includeBinaryDir = false; + // Do not repeat an include path. + std::set<std::string> emitted; // When automatic include directories are requested for a build then // include the source and binary directories at the beginning of the @@ -859,26 +858,21 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, // cannot fix this because not all native build tools support // per-source-file include paths. if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) { - includeSourceDir = true; - includeBinaryDir = true; - } - - // Do not repeat an include path. - std::set<std::string> emitted; - - // Store the automatic include paths. - if (includeBinaryDir) { - std::string binDir = this->StateSnapshot.GetDirectory().GetCurrentBinary(); - if (emitted.find(binDir) == emitted.end()) { - dirs.push_back(binDir); - emitted.insert(binDir); + // Current binary directory + { + std::string binDir = + this->StateSnapshot.GetDirectory().GetCurrentBinary(); + if (emitted.insert(binDir).second) { + dirs.push_back(std::move(binDir)); + } } - } - if (includeSourceDir) { - std::string srcDir = this->StateSnapshot.GetDirectory().GetCurrentSource(); - if (emitted.find(srcDir) == emitted.end()) { - dirs.push_back(srcDir); - emitted.insert(srcDir); + // Current source directory + { + std::string srcDir = + this->StateSnapshot.GetDirectory().GetCurrentSource(); + if (emitted.insert(srcDir).second) { + dirs.push_back(std::move(srcDir)); + } } } @@ -886,43 +880,45 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, return; } - std::string rootPath; - if (const char* sysrootCompile = - this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { - rootPath = sysrootCompile; - } else { - rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); - } - + // Implicit include directories std::vector<std::string> implicitDirs; - // Load implicit include directories for this language. - std::string impDirVar = "CMAKE_"; - impDirVar += lang; - impDirVar += "_IMPLICIT_INCLUDE_DIRECTORIES"; - if (const char* value = this->Makefile->GetDefinition(impDirVar)) { - std::vector<std::string> impDirVec; - cmSystemTools::ExpandListArgument(value, impDirVec); - for (std::string const& i : impDirVec) { - std::string d = rootPath + i; - cmSystemTools::ConvertToUnixSlashes(d); - emitted.insert(std::move(d)); - if (!stripImplicitInclDirs) { + { + std::string rootPath; + if (const char* sysrootCompile = + this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { + rootPath = sysrootCompile; + } else { + rootPath = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); + } + + // Load implicit include directories for this language. + std::string key = "CMAKE_"; + key += lang; + key += "_IMPLICIT_INCLUDE_DIRECTORIES"; + if (const char* value = this->Makefile->GetDefinition(key)) { + std::vector<std::string> impDirVec; + cmSystemTools::ExpandListArgument(value, impDirVec); + for (std::string const& i : impDirVec) { + { + std::string d = rootPath + i; + cmSystemTools::ConvertToUnixSlashes(d); + emitted.insert(std::move(d)); + } implicitDirs.push_back(i); } } } // Get the target-specific include directories. - std::vector<std::string> includes; - - includes = target->GetIncludeDirectories(config, lang); + std::vector<std::string> userDirs = + target->GetIncludeDirectories(config, lang); // Support putting all the in-project include directories first if // it is requested by the project. if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) { std::string const &topSourceDir = this->GetState()->GetSourceDirectory(), &topBinaryDir = this->GetState()->GetBinaryDirectory(); - for (std::string const& i : includes) { + for (std::string const& i : userDirs) { // Emit this directory only if it is a subdirectory of the // top-level source or binary tree. if (cmSystemTools::ComparePath(i, topSourceDir) || @@ -937,7 +933,7 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, } // Construct the final ordered include directory list. - for (std::string const& i : includes) { + for (std::string const& i : userDirs) { if (emitted.insert(i).second) { dirs.push_back(i); } @@ -946,22 +942,38 @@ void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, this->MoveSystemIncludesToEnd(dirs, config, lang, target); // Add standard include directories for this language. - // We do not filter out implicit directories here. - std::string const standardIncludesVar = - "CMAKE_" + lang + "_STANDARD_INCLUDE_DIRECTORIES"; - std::string const standardIncludes = - this->Makefile->GetSafeDefinition(standardIncludesVar); - std::vector<std::string>::size_type const before = includes.size(); - cmSystemTools::ExpandListArgument(standardIncludes, includes); - for (std::vector<std::string>::iterator i = includes.begin() + before; - i != includes.end(); ++i) { - cmSystemTools::ConvertToUnixSlashes(*i); - dirs.push_back(*i); - } - - for (std::string const& i : implicitDirs) { - if (std::find(includes.begin(), includes.end(), i) != includes.end()) { - dirs.push_back(i); + { + std::vector<std::string>::size_type const before = userDirs.size(); + { + std::string key = "CMAKE_"; + key += lang; + key += "_STANDARD_INCLUDE_DIRECTORIES"; + std::string const value = this->Makefile->GetSafeDefinition(key); + cmSystemTools::ExpandListArgument(value, userDirs); + } + for (std::vector<std::string>::iterator i = userDirs.begin() + before, + ie = userDirs.end(); + i != ie; ++i) { + cmSystemTools::ConvertToUnixSlashes(*i); + dirs.push_back(*i); + } + } + + if (!stripImplicitDirs) { + if (!appendAllImplicitDirs) { + // Append only those implicit directories that were requested by the user + for (std::string const& i : implicitDirs) { + if (std::find(userDirs.begin(), userDirs.end(), i) != userDirs.end()) { + dirs.push_back(i); + } + } + } else { + // Append all implicit directories + for (std::string const& i : implicitDirs) { + if (std::find(dirs.begin(), dirs.end(), i) == dirs.end()) { + dirs.push_back(i); + } + } } } } diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 9ba62cc..52f0396 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -237,12 +237,18 @@ public: return true; } - /** Get the include flags for the current makefile and language. */ + /** @brief Get the include directories for the current makefile and language. + * @arg stripImplicitDirs Strip all directories found in + * CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES from the result. + * @arg appendAllImplicitDirs Append all directories found in + * CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES to the result. + */ void GetIncludeDirectories(std::vector<std::string>& dirs, cmGeneratorTarget const* target, const std::string& lang = "C", const std::string& config = "", - bool stripImplicitInclDirs = true) const; + bool stripImplicitDirs = true, + bool appendAllImplicitDirs = false) const; void AddCompileOptions(std::string& flags, cmGeneratorTarget* target, const std::string& lang, const std::string& config); void AddCompileDefinitions(std::set<std::string>& defines, diff --git a/Tests/RunCMake/CPack/CPackTestHelpers.cmake b/Tests/RunCMake/CPack/CPackTestHelpers.cmake index 8c9f4b4..d00ef3b 100644 --- a/Tests/RunCMake/CPack/CPackTestHelpers.cmake +++ b/Tests/RunCMake/CPack/CPackTestHelpers.cmake @@ -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" diff --git a/Tests/RunCMake/CPack/DEB/Helpers.cmake b/Tests/RunCMake/CPack/DEB/Helpers.cmake index f7c5c84..e01f81d 100644 --- a/Tests/RunCMake/CPack/DEB/Helpers.cmake +++ b/Tests/RunCMake/CPack/DEB/Helpers.cmake @@ -1,7 +1,7 @@ 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) diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index 33ffa14..b273c1e 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -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") diff --git a/Tests/RunCMake/CPack/tests/TIMESTAMPS/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/TIMESTAMPS/ExpectedFiles.cmake new file mode 100644 index 0000000..d1a3a5f --- /dev/null +++ b/Tests/RunCMake/CPack/tests/TIMESTAMPS/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/TIMESTAMPS/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/TIMESTAMPS/VerifyResult.cmake new file mode 100644 index 0000000..e7e2645 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/TIMESTAMPS/VerifyResult.cmake @@ -0,0 +1,58 @@ +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() diff --git a/Tests/RunCMake/CPack/tests/TIMESTAMPS/test.cmake b/Tests/RunCMake/CPack/tests/TIMESTAMPS/test.cmake new file mode 100644 index 0000000..a193852 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/TIMESTAMPS/test.cmake @@ -0,0 +1,3 @@ +install(FILES CMakeLists.txt DESTINATION foo COMPONENT test) + +set(CPACK_COMPONENTS_ALL test) |