diff options
21 files changed, 149 insertions, 5 deletions
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst index d1fafb5..b3091fa 100644 --- a/Help/manual/cmake-policies.7.rst +++ b/Help/manual/cmake-policies.7.rst @@ -58,6 +58,7 @@ Policies Introduced by CMake 3.24 .. toctree:: :maxdepth: 1 + CMP0135: ExternalProject ignores timestamps in archives by default for the URL download method. </policy/CMP0135> CMP0134: Fallback to \"HOST\" Windows registry view when \"TARGET\" view is not usable. </policy/CMP0134> CMP0133: The CPack module disables SLA by default in the CPack DragNDrop Generator. </policy/CMP0133> CMP0132: Do not set compiler environment variables on first run. </policy/CMP0132> diff --git a/Help/policy/CMP0135.rst b/Help/policy/CMP0135.rst new file mode 100644 index 0000000..1c0c134 --- /dev/null +++ b/Help/policy/CMP0135.rst @@ -0,0 +1,29 @@ +CMP0135 +------- + +.. versionadded:: 3.24 + +When using the ``URL`` download method with the :command:`ExternalProject_Add` +command, CMake 3.23 and below sets the timestamps of the extracted contents +to the same as the timestamps in the archive. When the ``URL`` changes, the +new archive is downloaded and extracted, but the timestamps of the extracted +contents might not be newer than the previous contents. Anything that depends +on the extracted contents might not be rebuilt, even though the contents may +change. + +CMake 3.24 and above prefers to set the timestamps of all extracted contents +to the time of the extraction. This ensures that anything that depends on the +extracted contents will be rebuilt whenever the ``URL`` changes. + +The ``DOWNLOAD_EXTRACT_TIMESTAMP`` option to the +:command:`ExternalProject_Add` command can be used to explicitly specify how +timestamps should be handled. When ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not +given, this policy controls the default behavior. The ``OLD`` behavior for +this policy is to restore the timestamps from the archive. The ``NEW`` +behavior sets the timestamps of extracted contents to the time of extraction. + +This policy was introduced in CMake version 3.24. CMake version |release| +warns when the policy is not set and uses ``OLD`` behavior. Use the +:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly. + +.. include:: DEPRECATED.txt diff --git a/Help/release/dev/ExternalProject-no-extract-timestamp.rst b/Help/release/dev/ExternalProject-no-extract-timestamp.rst new file mode 100644 index 0000000..0e8c01e --- /dev/null +++ b/Help/release/dev/ExternalProject-no-extract-timestamp.rst @@ -0,0 +1,8 @@ +ExternalProject-no-extract-timestamp +------------------------------------ + +* The :command:`ExternalProject_Add` command gained a new + ``DOWNLOAD_EXTRACT_TIMESTAMP`` option for controlling whether the timestamps + of extracted contents are set to match those in the archive when the ``URL`` + download method is used. A new policy :policy:`CMP0135` was added to control + the default behavior when the new option is not used. diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 42cb7a0..5c37be2 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -170,6 +170,19 @@ External Project Definition the default name is generally suitable and is not normally used outside of code internal to the ``ExternalProject`` module. + ``DOWNLOAD_EXTRACT_TIMESTAMP <bool>`` + .. versionadded:: 3.24 + + When specified with a true value, the timestamps of the extracted + files will match those in the archive. When false, the timestamps of + the extracted files will reflect the time at which the extraction + was performed. If the download URL changes, timestamps based off + those in the archive can result in dependent targets not being rebuilt + when they potentially should have been. Therefore, unless the file + timestamps are significant to the project in some way, use a false + value for this option. If ``DOWNLOAD_EXTRACT_TIMESTAMP`` is not given, + the default is false. See policy :policy:`CMP0135`. + ``DOWNLOAD_NO_EXTRACT <bool>`` .. versionadded:: 3.6 @@ -1534,7 +1547,7 @@ function(_ep_write_verifyfile_script script_filename LOCAL hash) endfunction() -function(_ep_write_extractfile_script script_filename name filename directory) +function(_ep_write_extractfile_script script_filename name filename directory options) set(args "") if(filename MATCHES "(\\.|=)(7z|tar\\.bz2|tar\\.gz|tar\\.xz|tbz2|tgz|txz|zip)$") @@ -2761,16 +2774,51 @@ hash=${hash} ) endif() list(APPEND cmd ${CMAKE_COMMAND} -P ${stamp_dir}/verify-${name}.cmake) - if (NOT no_extract) + get_target_property(extract_timestamp ${name} _EP_DOWNLOAD_EXTRACT_TIMESTAMP) + if(no_extract) + if(NOT extract_timestamp STREQUAL "extract_timestamp-NOTFOUND") + message(FATAL_ERROR + "Cannot specify DOWNLOAD_EXTRACT_TIMESTAMP when using " + "DOWNLOAD_NO_EXTRACT TRUE" + ) + endif() + set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) + else() + if(extract_timestamp STREQUAL "extract_timestamp-NOTFOUND") + # Default depends on policy CMP0135 + if(_EP_CMP0135 STREQUAL "") + message(AUTHOR_WARNING + "The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy " + "CMP0135 is not set. The policy's OLD behavior will be used. " + "When using a URL download, the timestamps of extracted files " + "should preferably be that of the time of extraction, otherwise " + "code that depends on the extracted contents might not be " + "rebuilt if the URL changes. The OLD behavior preserves the " + "timestamps from the archive instead, but this is usually not " + "what you want. Update your project to the NEW behavior or " + "specify the DOWNLOAD_EXTRACT_TIMESTAMP option with a value of " + "true to avoid this robustness issue." + ) + set(extract_timestamp TRUE) + elseif(_EP_CMP0135 STREQUAL "NEW") + set(extract_timestamp FALSE) + else() + set(extract_timestamp TRUE) + endif() + endif() + if(extract_timestamp) + set(options "") + else() + set(options "--touch") + endif() _ep_write_extractfile_script( "${stamp_dir}/extract-${name}.cmake" "${name}" "${file}" "${source_dir}" + "${options}" ) list(APPEND cmd COMMAND ${CMAKE_COMMAND} -P ${stamp_dir}/extract-${name}.cmake) - else () - set_property(TARGET ${name} PROPERTY _EP_DOWNLOADED_FILE ${file}) endif () endif() else() @@ -3438,6 +3486,9 @@ function(ExternalProject_Add name) ) set(cmp0114 "NEW") endif() + cmake_policy(GET CMP0135 _EP_CMP0135 + PARENT_SCOPE # undocumented, do not use outside of CMake + ) _ep_get_configuration_subdir_suffix(cfgdir) @@ -3483,6 +3534,7 @@ function(ExternalProject_Add name) URL_HASH URL_MD5 DOWNLOAD_NAME + DOWNLOAD_EXTRACT_TIMESTAMP DOWNLOAD_NO_EXTRACT DOWNLOAD_NO_PROGRESS TIMEOUT diff --git a/Modules/ExternalProject/extractfile.cmake.in b/Modules/ExternalProject/extractfile.cmake.in index d7f5756..984565b 100644 --- a/Modules/ExternalProject/extractfile.cmake.in +++ b/Modules/ExternalProject/extractfile.cmake.in @@ -29,7 +29,7 @@ file(MAKE_DIRECTORY "${ut_dir}") # Extract it: # message(STATUS "extracting... [tar @args@]") -execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} +execute_process(COMMAND ${CMAKE_COMMAND} -E tar @args@ ${filename} @options@ WORKING_DIRECTORY ${ut_dir} RESULT_VARIABLE rv ) diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h index 8739c55..7d06607 100644 --- a/Source/cmPolicies.h +++ b/Source/cmPolicies.h @@ -404,6 +404,10 @@ class cmMakefile; SELECT(POLICY, CMP0134, \ "Fallback to \"HOST\" Windows registry view when \"TARGET\" view " \ "is not usable.", \ + 3, 24, 0, cmPolicies::WARN) \ + SELECT(POLICY, CMP0135, \ + "ExternalProject ignores timestamps in archives by default for the " \ + "URL download method", \ 3, 24, 0, cmPolicies::WARN) #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1) diff --git a/Tests/RunCMake/CMP0135/CMP0135-Common.cmake b/Tests/RunCMake/CMP0135/CMP0135-Common.cmake new file mode 100644 index 0000000..4b7cce5 --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-Common.cmake @@ -0,0 +1,18 @@ +include(ExternalProject) + +set(stamp_dir "${CMAKE_CURRENT_BINARY_DIR}/stamps") + +ExternalProject_Add(fake_ext_proj + # We don't actually do a build, so we never try to download from this URL + URL https://example.com/something.zip + STAMP_DIR ${stamp_dir} +) + +# Report whether the --touch option was added to the extraction script +set(extraction_script "${stamp_dir}/extract-fake_ext_proj.cmake") +file(STRINGS "${extraction_script}" results REGEX "--touch") +if("${results}" STREQUAL "") + message(STATUS "Using timestamps from archive") +else() + message(STATUS "Using extraction time for the timestamps") +endif() diff --git a/Tests/RunCMake/CMP0135/CMP0135-NEW-stdout.txt b/Tests/RunCMake/CMP0135/CMP0135-NEW-stdout.txt new file mode 100644 index 0000000..bf53c0b --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-NEW-stdout.txt @@ -0,0 +1 @@ +Using extraction time for the timestamps diff --git a/Tests/RunCMake/CMP0135/CMP0135-NEW.cmake b/Tests/RunCMake/CMP0135/CMP0135-NEW.cmake new file mode 100644 index 0000000..1fd6354 --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-NEW.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0135 NEW) +include(CMP0135-Common.cmake) diff --git a/Tests/RunCMake/CMP0135/CMP0135-OLD-stdout.txt b/Tests/RunCMake/CMP0135/CMP0135-OLD-stdout.txt new file mode 100644 index 0000000..ee57beb --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-OLD-stdout.txt @@ -0,0 +1 @@ +Using timestamps from archive diff --git a/Tests/RunCMake/CMP0135/CMP0135-OLD.cmake b/Tests/RunCMake/CMP0135/CMP0135-OLD.cmake new file mode 100644 index 0000000..80d58a7 --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-OLD.cmake @@ -0,0 +1,2 @@ +cmake_policy(SET CMP0135 OLD) +include(CMP0135-Common.cmake) diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt new file mode 100644 index 0000000..8ba0027 --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-WARN-stderr.txt @@ -0,0 +1,10 @@ +CMake Warning \(dev\) at .*/Modules/ExternalProject.cmake:[0-9]+ \(message\): + The DOWNLOAD_EXTRACT_TIMESTAMP option was not given and policy CMP0135 is + not set\. The policy's OLD behavior will be used\. When using a URL + download, the timestamps of extracted files should preferably be that of + the time of extraction, otherwise code that depends on the extracted + contents might not be rebuilt if the URL changes\. The OLD behavior + preserves the timestamps from the archive instead, but this is usually not + what you want\. Update your project to the NEW behavior or specify the + DOWNLOAD_EXTRACT_TIMESTAMP option with a value of true to avoid this + robustness issue\. diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN-stdout.txt b/Tests/RunCMake/CMP0135/CMP0135-WARN-stdout.txt new file mode 100644 index 0000000..ee57beb --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-WARN-stdout.txt @@ -0,0 +1 @@ +Using timestamps from archive diff --git a/Tests/RunCMake/CMP0135/CMP0135-WARN.cmake b/Tests/RunCMake/CMP0135/CMP0135-WARN.cmake new file mode 100644 index 0000000..ab71c40 --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMP0135-WARN.cmake @@ -0,0 +1,2 @@ + +include(CMP0135-Common.cmake) diff --git a/Tests/RunCMake/CMP0135/CMakeLists.txt b/Tests/RunCMake/CMP0135/CMakeLists.txt new file mode 100644 index 0000000..5ff8d3e --- /dev/null +++ b/Tests/RunCMake/CMP0135/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.23) +project(${RunCMake_TEST} NONE) +include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/CMP0135/RunCMakeTest.cmake b/Tests/RunCMake/CMP0135/RunCMakeTest.cmake new file mode 100644 index 0000000..da92391 --- /dev/null +++ b/Tests/RunCMake/CMP0135/RunCMakeTest.cmake @@ -0,0 +1,5 @@ +include(RunCMake) + +run_cmake(CMP0135-WARN) +run_cmake(CMP0135-OLD) +run_cmake(CMP0135-NEW) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index c2677a0..515c6d8 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -149,6 +149,7 @@ if("${CMAKE_C_COMPILER_ID}" STREQUAL "LCC" OR endif() add_RunCMake_test(CMP0132) +add_RunCMake_test(CMP0135) # The test for Policy 65 requires the use of the # CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS variable, which both the VS and Xcode diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake index bfed4fa..364bf9e 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies.cmake @@ -4,6 +4,7 @@ if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) else() cmake_policy(SET CMP0114 OLD) # Test deprecated behavior. endif() +cmake_policy(SET CMP0135 NEW) include(ExternalProject) diff --git a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake index 039dec6..da823cd 100644 --- a/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake +++ b/Tests/RunCMake/ExternalProject/Add_StepDependencies_no_target.cmake @@ -4,6 +4,7 @@ if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12) else() cmake_policy(SET CMP0114 OLD) # Test deprecated behavior. endif() +cmake_policy(SET CMP0135 NEW) include(ExternalProject) diff --git a/Tests/RunCMake/ExternalProject/CMakeLists.txt b/Tests/RunCMake/ExternalProject/CMakeLists.txt index 933a57a..b94f825 100644 --- a/Tests/RunCMake/ExternalProject/CMakeLists.txt +++ b/Tests/RunCMake/ExternalProject/CMakeLists.txt @@ -3,4 +3,5 @@ project(${RunCMake_TEST} NONE) if(CMAKE_XCODE_BUILD_SYSTEM VERSION_GREATER_EQUAL 12 AND NOT RunCMake_TEST STREQUAL "Xcode-CMP0114") cmake_policy(SET CMP0114 NEW) endif() +cmake_policy(SET CMP0135 NEW) include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-Direct.cmake b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-Direct.cmake index 7ec1a00..e257425 100644 --- a/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-Direct.cmake +++ b/Tests/RunCMake/ExternalProject/NO_DEPENDS-CMP0114-NEW-Direct.cmake @@ -1,4 +1,5 @@ cmake_policy(SET CMP0114 NEW) +cmake_policy(SET CMP0135 NEW) include(ExternalProject) ExternalProject_Add(BAR SOURCE_DIR . TEST_COMMAND echo test) ExternalProject_Add_StepTargets(BAR NO_DEPENDS test) |