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

Merge topic 'fix-IS_NEWER_THAN-usage' into release-3.20

b0da6712 FetchContent: Don't update timestamps if files don't change
404cddb7

 ExternalProject: Fix misuse of IS_NEWER_THAN in timestamp checks
Acked-by: Kitware Robot's avatarKitware Robot <kwrobot@kitware.com>
Merge-request: !5825
parents 33d93089 b0da6712
Pipeline #215627 passed with stages
in 58 minutes and 19 seconds
...@@ -2579,11 +2579,24 @@ function(_ep_write_command_script ...@@ -2579,11 +2579,24 @@ function(_ep_write_command_script
endif() endif()
if(genex_supported) if(genex_supported)
# Only written at generation phase # Only written at generation phase. This will only change the file's
# timestamp if the contents change.
file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_content}") file(GENERATE OUTPUT "${script_filename}" CONTENT "${script_content}")
else() else()
# Written immediately, needed if script has to be invoked in configure phase # Update the file immediately, needed if script has to be invoked in the
file(WRITE "${script_filename}" "${script_content}") # configure phase (e.g. via FetchContent). We need to be careful to avoid
# updating the timestamp if the file contents don't change. The file(WRITE)
# command always updates the file, so avoid it if we don't need to call it.
set(doWrite TRUE)
if(EXISTS "${script_filename}")
file(READ "${script_filename}" existing_content)
if(existing_content STREQUAL script_content)
set(doWrite FALSE)
endif()
endif()
if(doWrite)
file(WRITE "${script_filename}" "${script_content}")
endif()
endif() endif()
endfunction() endfunction()
...@@ -3916,7 +3929,12 @@ function(_ep_do_preconfigure_steps_now name) ...@@ -3916,7 +3929,12 @@ function(_ep_do_preconfigure_steps_now name)
if(NOT need_to_run) if(NOT need_to_run)
foreach(dep_file ${script_file} ${_EPdepends_${STEP}}) foreach(dep_file ${script_file} ${_EPdepends_${STEP}})
if(NOT EXISTS ${dep_file} OR ${dep_file} IS_NEWER_THAN ${stamp_file}) # IS_NEWER_THAN is also true if the timestamps are the same. On some
# file systems, we only have second resolution timestamps and the
# likelihood of having the same timestamp is high. Use the negative
# form to ensure we actually get a true "is newer than" test.
if(NOT EXISTS ${dep_file} OR
NOT ${stamp_file} IS_NEWER_THAN ${dep_file})
set(need_to_run TRUE) set(need_to_run TRUE)
break() break()
endif() endif()
......
...@@ -7,7 +7,8 @@ set(quiet "@quiet@") ...@@ -7,7 +7,8 @@ set(quiet "@quiet@")
set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject")
include(${script_dir}/captured_process_setup.cmake) include(${script_dir}/captured_process_setup.cmake)
if(NOT "@gitclone_infofile@" IS_NEWER_THAN "@gitclone_stampfile@") if(EXISTS "@gitclone_stampfile@" AND EXISTS "@gitclone_infofile@" AND
"@gitclone_stampfile@" IS_NEWER_THAN "@gitclone_infofile@")
if(NOT quiet) if(NOT quiet)
message(STATUS message(STATUS
"Avoiding repeated git clone, stamp file is up to date: " "Avoiding repeated git clone, stamp file is up to date: "
......
...@@ -7,7 +7,8 @@ set(quiet "@quiet@") ...@@ -7,7 +7,8 @@ set(quiet "@quiet@")
set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject") set(script_dir "@CMAKE_CURRENT_FUNCTION_LIST_DIR@/ExternalProject")
include(${script_dir}/captured_process_setup.cmake) include(${script_dir}/captured_process_setup.cmake)
if(NOT "@hgclone_infofile@" IS_NEWER_THAN "@hgclone_stampfile@") if(EXISTS "@hgclone_stampfile@" AND EXISTS "@hgclone_infofile@" AND
"@hgclone_stampfile@" IS_NEWER_THAN "@hgclone_infofile@")
if(NOT quiet) if(NOT quiet)
message(STATUS message(STATUS
"Avoiding repeated hg clone, stamp file is up to date: " "Avoiding repeated hg clone, stamp file is up to date: "
......
...@@ -27,6 +27,36 @@ run_cmake_with_options(ManualSourceDirectoryRelative ...@@ -27,6 +27,36 @@ run_cmake_with_options(ManualSourceDirectoryRelative
-D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT:STRING=WithProject" -D "FETCHCONTENT_SOURCE_DIR_WITHPROJECT:STRING=WithProject"
) )
function(run_FetchContent_TimeStamps)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/TimeStamps)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
file(MAKE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
# First run should execute the commands
run_cmake(TimeStamps)
# Ensure that the file checks we use in the TimeStampsRerun-check.cmake script
# will not be defeated by file systems with only one second resolution.
# The IS_NEWER_THAN check returns TRUE if the timestamps of the two files are
# the same, which has been observed where filesystems only have one second
# resolution.
set(cmpTimeStamp ${RunCMake_TEST_BINARY_DIR}/cmpTimeStamp.txt)
set(checkTimeStamp ${RunCMake_TEST_BINARY_DIR}/cmpTimeStampCheck.txt)
file(TOUCH ${cmpTimeStamp})
file(TOUCH ${checkTimeStamp})
if("${cmpTimeStamp}" IS_NEWER_THAN "${checkTimeStamp}")
execute_process(
COMMAND ${CMAKE_COMMAND} -E sleep 1.125
COMMAND_ERROR_IS_FATAL LAST
)
endif()
# Run again with no changes, no commands should re-execute
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake(TimeStampsRerun)
endfunction()
run_FetchContent_TimeStamps()
function(run_FetchContent_DirOverrides) function(run_FetchContent_DirOverrides)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides-build) set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/DirOverrides-build)
file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}") file(REMOVE_RECURSE "${RunCMake_TEST_BINARY_DIR}")
......
.* *download executed
.* *patch executed
include(FetchContent)
# Do nothing for an update because it would result in always re-running the
# patch step. We want to test that a patch step that only depends on the
# download step is not re-run unnecessarily.
FetchContent_Declare(customCommands
PREFIX ${CMAKE_CURRENT_BINARY_DIR}
DOWNLOAD_COMMAND "${CMAKE_COMMAND}" -E echo "download executed"
UPDATE_COMMAND ""
PATCH_COMMAND "${CMAKE_COMMAND}" -E echo "patch executed"
)
set(FETCHCONTENT_QUIET FALSE)
FetchContent_MakeAvailable(customCommands)
set(cmpFile ${RunCMake_TEST_BINARY_DIR}/cmpTimeStamp.txt)
set(scriptDir ${RunCMake_TEST_BINARY_DIR}/tmp)
set(stampDir ${RunCMake_TEST_BINARY_DIR}/src/customcommands-stamp)
set(errorMessages)
if(NOT EXISTS "${cmpFile}")
list(APPEND errorMessages " ${cmpFile} is missing")
else()
foreach(script IN ITEMS mkdirs download patch)
set(scriptFile "${scriptDir}/customcommands-${script}.cmake")
if(NOT EXISTS "${scriptFile}")
list(APPEND errorMessages " ${scriptFile} is missing")
elseif(NOT "${cmpFile}" IS_NEWER_THAN "${scriptFile}")
list(APPEND errorMessages " ${scriptFile} was unexectedly updated")
endif()
endforeach()
# special case, not a script, has different extension
set(repoInfoFile "${scriptDir}/customcommands-download-repoinfo.txt")
if(NOT EXISTS "${repoInfoFile}")
list(APPEND errorMessages " ${repoInfoFile} is missing")
elseif(NOT "${cmpFile}" IS_NEWER_THAN "${repoInfoFile}")
list(APPEND errorMessages " ${repoInfoFile} was unexectedly updated")
endif()
foreach(step IN ITEMS download patch)
set(stampFile "${stampDir}/customcommands-${step}")
if(NOT EXISTS "${stampFile}")
list(APPEND errorMessages " ${stampFile} is missing")
elseif(NOT "${cmpFile}" IS_NEWER_THAN "${stampFile}")
list(APPEND errorMessages " ${stampFile} was unexectedly updated")
endif()
endforeach()
endif()
if(errorMessages)
list(JOIN errorMessages "\n" RunCMake_TEST_FAILED)
endif()
include(${CMAKE_CURRENT_LIST_DIR}/TimeStamps.cmake)
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