mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
install: Compare parallel index freshness at whole-second resolution
The install handler runs `cmake --install -j` in parallel only when the CMakeFiles/InstallScripts.json index is at least as new as CMakeFiles/cmake.check_cache. Both files are written during a single generate step, so any ordering between them is sub-second. On filesystems that do not order two near-simultaneous writes by their sub-second modification time (e.g. NFS on AIX), a fresh index can appear older than the marker and the install spuriously falls back to serial. Compare the two modification times at whole-second resolution. This ignores sub-second jitter within one generate step while still detecting a genuinely stale index from an older CMake reconfigure, which is always at least a configure run older. Fixes: #27919
This commit is contained in:
@@ -64,13 +64,20 @@ cmInstallScriptHandler::cmInstallScriptHandler(
|
||||
this->Directories.push_back(cmSystemTools::GetFilenamePath(script));
|
||||
};
|
||||
|
||||
int compare = 1;
|
||||
// Trust the parallel install index unless the cache marker is newer. Both
|
||||
// files are written back-to-back while generating this build tree, so any
|
||||
// ordering between them lives in a sub-second window that some filesystems
|
||||
// (e.g. NFS on AIX) do not resolve by write time. Compare at whole-second
|
||||
// resolution: a genuinely stale index left by an older CMake reconfigure is
|
||||
// always at least a configure run (seconds) older, while the sub-second
|
||||
// jitter of a single generate step is ignored.
|
||||
bool indexIsFresh = false;
|
||||
if (cmSystemTools::FileExists(file)) {
|
||||
cmSystemTools::FileTimeCompare(
|
||||
cmStrCat(this->BinaryDir, "/CMakeFiles/cmake.check_cache"), file,
|
||||
&compare);
|
||||
long int const cacheTime = cmSystemTools::ModifiedTime(
|
||||
cmStrCat(this->BinaryDir, "/CMakeFiles/cmake.check_cache"));
|
||||
indexIsFresh = cacheTime <= cmSystemTools::ModifiedTime(file);
|
||||
}
|
||||
if (compare < 1) {
|
||||
if (indexIsFresh) {
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
auto jsonReader =
|
||||
std::unique_ptr<Json::CharReader>(rbuilder.newCharReader());
|
||||
|
||||
@@ -42,7 +42,7 @@ function(install_test test)
|
||||
endif()
|
||||
set(INSTALL_MANIFEST ${RunCMake_TEST_BINARY_DIR}/${INSTALL_MANIFEST})
|
||||
set(RunCMake-check-file check-manifest.cmake)
|
||||
run_cmake_command(${test}-install ${CMAKE_COMMAND} -E env --unset=NINJA_STATUS ${CMAKE_COMMAND} --install . ${ARGS_ARGS})
|
||||
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . ${ARGS_ARGS})
|
||||
unset(RunCMake-check-file)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -75,19 +75,7 @@ function(install_fail_test test fixture)
|
||||
endif()
|
||||
run_cmake(${fixture})
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
if (ARG_PARALLEL)
|
||||
# The install runs in parallel only when CMakeFiles/InstallScripts.json is
|
||||
# at least as new as CMakeFiles/cmake.check_cache; otherwise the handler
|
||||
# falls back to running the top-level cmake_install.cmake serially. Both
|
||||
# files are written during the same configuration, and their relative
|
||||
# modification times are not reliable on every filesystem, so make the JSON
|
||||
# newest explicitly to keep this test from intermittently exercising the
|
||||
# serial fallback (which omits the per-script parallel diagnostics).
|
||||
file(TOUCH_NOCREATE
|
||||
${RunCMake_TEST_BINARY_DIR}/CMakeFiles/InstallScripts.json)
|
||||
endif()
|
||||
run_cmake_command(${test}
|
||||
${CMAKE_COMMAND} -E env --unset=NINJA_STATUS
|
||||
${CMAKE_COMMAND} --install . ${ARG_INSTALL_ARGS})
|
||||
endfunction()
|
||||
|
||||
@@ -100,3 +88,31 @@ install_fail_test(serial-fatal install-fail FAIL_MODE fatal)
|
||||
# by a later success (and the later component is not installed).
|
||||
install_fail_test(serial-mask install-component-mask
|
||||
INSTALL_ARGS --component comp_fail --component comp_ok)
|
||||
|
||||
# The stale-index guard exercised by out-of-date-json above must be
|
||||
# recoverable: once CMakeFiles/InstallScripts.json looks older than
|
||||
# CMakeFiles/cmake.check_cache, a reconfigure rewrites both files in one
|
||||
# generate step so the install runs in parallel again. The handler compares
|
||||
# their modification times at whole-second resolution, so the freshly generated
|
||||
# index is not spuriously demoted to the serial fallback on filesystems that
|
||||
# commit the two configure-time writes out of order within a second.
|
||||
function(reconfigure_parallel_test test)
|
||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-install)
|
||||
set(RunCMake_TEST_OPTIONS -DINSTALL_PARALLEL=ON -DCMAKE_INSTALL_PREFIX=install)
|
||||
set(RunCMake_TEST_OUTPUT_MERGE 1)
|
||||
if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
list(APPEND RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Debug)
|
||||
endif()
|
||||
run_cmake(install)
|
||||
set(RunCMake_TEST_NO_CLEAN 1)
|
||||
run_cmake_command(${test}-sleep ${CMAKE_COMMAND} -E sleep 2)
|
||||
run_cmake_command(${test}-touch
|
||||
${CMAKE_COMMAND} -E touch ${RunCMake_TEST_BINARY_DIR}/CMakeFiles/cmake.check_cache)
|
||||
run_cmake_command(${test}-reconfigure ${CMAKE_COMMAND} .)
|
||||
set(INSTALL_MANIFEST ${RunCMake_TEST_BINARY_DIR}/install_manifest.txt)
|
||||
set(INSTALL_COUNT 5)
|
||||
set(RunCMake-check-file check-manifest.cmake)
|
||||
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install .)
|
||||
unset(RunCMake-check-file)
|
||||
endfunction()
|
||||
reconfigure_parallel_test(out-of-date-json-reconfigure)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
\[1\/5\] .*
|
||||
\-\- Installing:[^
|
||||
]*
|
||||
\[2\/5\] .*
|
||||
\-\- Installing:[^
|
||||
]*
|
||||
\[3\/5\] .*
|
||||
\-\- Installing:[^
|
||||
]*
|
||||
\[4\/5\] .*
|
||||
\-\- Installing:[^
|
||||
]*
|
||||
\[5\/5\] .*
|
||||
\-\- Installing:[^
|
||||
]*
|
||||
Reference in New Issue
Block a user