Files
Daksh Mamodiya 899e7231ab CTest: Report failure when a launched command is killed by a signal
cmCTestLaunch, which backs ctest --launch and ctest --instrument,
derived its exit code from the child's SpawnResult and ExitStatus but
ignored TermSignal.  On POSIX a command killed by a signal reports
ExitStatus 0 and TermSignal N, so the launcher reported success for a
crashed or killed command, masking failed compile/link/custom rules on
the dashboard and in instrumentation snippets.

Classify the child with the ordered triple SpawnResult, TermSignal,
ExitStatus, mapping a signal-killed child to a non-zero exit code.
Native Windows is unaffected: abnormal termination already arrives as a
non-zero exit code there.

Fixes: #27921
2026-07-06 15:46:37 +02:00

218 lines
7.4 KiB
CMake

include(RunCTest)
set(LANG NONE)
set(CASE_CTEST_BUILD_ARGS "")
set(RunCMake_USE_LAUNCHERS TRUE)
set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
function(run_ctest_build CASE_NAME)
set(CASE_CTEST_BUILD_ARGS "${ARGN}")
run_ctest(${CASE_NAME})
endfunction()
run_ctest_build(BuildQuiet QUIET)
run_ctest_build(ParallelLevel PARALLEL_LEVEL 1)
block()
set(LANG CXX)
configure_file("${RunCMake_SOURCE_DIR}/BuildFailure.cxx" "${RunCMake_BINARY_DIR}/BuildFailure/BuildFailure.cxx" COPYONLY)
set(CASE_CMAKELISTS_SUFFIX_CODE [=[
add_executable(BuildFailure BuildFailure.cxx)
]=])
set(CASE_CMAKELISTS_PREFIX_CODE [[
if(NOT CTEST_USE_LAUNCHERS)
message(FATAL_ERROR "CTEST_USE_LAUNCHERS not set")
endif()
]])
set(CASE_TEST_PREFIX_CODE "")
set(CASE_TEST_SUFFIX_CODE [[
if (ctest_build_return_value)
message("ctest_build returned non-zero")
else()
message("ctest_build returned zero")
endif()
]])
run_ctest(BuildFailure)
endblock()
function(run_BuildChangeId)
set(CASE_TEST_PREFIX_CODE [[
set(CTEST_CHANGE_ID "<>1")
]])
run_ctest(BuildChangeId)
endfunction()
run_BuildChangeId()
function(run_SubdirTarget)
set(CASE_CMAKELISTS_SUFFIX_CODE [=[
file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/subdir/CMakeLists.txt [[
add_custom_target(target_in_subdir COMMAND ${CMAKE_COMMAND} -E touch target_in_subdir.out VERBATIM)
]])
add_subdirectory(subdir)
]=])
set(CASE_CTEST_BUILD_ARGS TARGET target_in_subdir)
run_ctest(SubdirTarget)
endfunction()
run_SubdirTarget()
set(RunCMake_USE_CUSTOM_BUILD_COMMAND TRUE)
set(RunCMake_BUILD_COMMAND "${FAKE_BUILD_COMMAND_EXE}")
run_ctest(BuildCommandFailure)
unset(RunCMake_BUILD_COMMAND)
set(RunCMake_USE_CUSTOM_BUILD_COMMAND TRUE)
set(RunCMake_BUILD_COMMAND "${FAKE_BUILD_COMMAND_EXE}")
run_ctest(BuildDirectories)
unset(RunCMake_BUILD_COMMAND)
set(RunCMake_USE_LAUNCHERS FALSE)
set(RunCMake_BUILD_COMMAND "${COLOR_WARNING}")
run_ctest(IgnoreColor)
unset(RunCMake_BUILD_COMMAND)
set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPreset")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
@ONLY)
run_ctest_build(BuildPreset PRESET my-build-preset)
unset(RunCMake_TEST_SOURCE_DIR)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetVar")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
@ONLY)
set(CASE_TEST_PREFIX_CODE [[set(CTEST_BUILD_PRESET "my-build-preset")]])
run_ctest(BuildPresetVar)
unset(CASE_TEST_PREFIX_CODE)
unset(RunCMake_TEST_SOURCE_DIR)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetGenericVar")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json"
@ONLY)
set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-build-preset")]])
run_ctest(BuildPresetGenericVar)
unset(CASE_TEST_PREFIX_CODE)
unset(RunCMake_TEST_SOURCE_DIR)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFile")
set(custom_presets_file
"${RunCMake_BINARY_DIR}/BuildPresetFromFile/custom-presets.json")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${custom_presets_file}"
@ONLY)
run_ctest_build(BuildPresetFromFile
PRESET my-build-preset
PRESETS_FILE "${custom_presets_file}")
unset(RunCMake_TEST_SOURCE_DIR)
unset(custom_presets_file)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFileVar")
set(custom_presets_file
"${RunCMake_BINARY_DIR}/BuildPresetFromFileVar/custom-presets.json")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${custom_presets_file}"
@ONLY)
set(CASE_TEST_PREFIX_CODE
"set(CTEST_BUILD_PRESET \"my-build-preset\")
set(CTEST_PRESETS_FILE \"${custom_presets_file}\")")
run_ctest(BuildPresetFromFileVar)
unset(CASE_TEST_PREFIX_CODE)
unset(RunCMake_TEST_SOURCE_DIR)
unset(custom_presets_file)
run_ctest_build(BuildPresetBadFile
PRESET my-build-preset
PRESETS_FILE /nonexistent/path/presets.json)
# Helper for tests that verify -D variables reach ctest_build() in -M/-T mode.
# Accepts a case name followed by the -D arguments to pass to ctest.
# Sets up a source directory with CMakePresets.json and a binary directory with
# DartConfiguration.tcl, then runs ctest with -M Experimental -T Build -V.
function(run_build_cli_var_test CASE_NAME)
set(case_source_dir "${RunCMake_BINARY_DIR}/${CASE_NAME}")
set(case_binary_dir "${RunCMake_BINARY_DIR}/${CASE_NAME}-build")
file(MAKE_DIRECTORY "${case_source_dir}")
configure_file(
"${RunCMake_SOURCE_DIR}/CMakePresets.json.in"
"${case_source_dir}/CMakePresets.json"
@ONLY)
file(REMOVE_RECURSE "${case_binary_dir}")
file(MAKE_DIRECTORY "${case_binary_dir}")
file(WRITE "${case_binary_dir}/DartConfiguration.tcl"
"BuildDirectory: ${case_binary_dir}\n"
"SourceDirectory: ${case_source_dir}\n"
"MakeCommand: \"${CMAKE_COMMAND}\" --build \"${case_binary_dir}\"\n")
set(RunCMake_TEST_SOURCE_DIR "${case_source_dir}")
set(RunCMake_TEST_BINARY_DIR "${case_binary_dir}")
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${CASE_NAME}
${CMAKE_CTEST_COMMAND}
-M Experimental
${ARGN}
-T Build
-V)
endfunction()
# Verify that CTEST_BUILD_PRESET passed via -D on the command line reaches
# ctest_build() when ctest is run with -M/-T.
run_build_cli_var_test(BuildPresetCLIVar
-D "CTEST_BUILD_PRESET=my-build-preset")
# Verify that CTEST_BUILD_CONFIGURATION passed via -D on the command line
# reaches ctest_build() when ctest is run with -M/-T.
run_build_cli_var_test(BuildConfigurationCLIVar
-D "CTEST_BUILD_PRESET=my-build-preset"
-D "CTEST_BUILD_CONFIGURATION=my-config")
# Verify that CTEST_CONFIGURATION_TYPE passed via -D on the command line
# reaches ctest_build() when ctest is run with -M/-T.
run_build_cli_var_test(ConfigurationTypeCLIVar
-D "CTEST_BUILD_PRESET=my-build-preset"
-D "CTEST_CONFIGURATION_TYPE=my-type")
set(RunCMake_USE_CUSTOM_BUILD_COMMAND FALSE)
if(RunCMake_GENERATOR MATCHES "Ninja")
function(run_NinjaLauncherSingleBuildFailure)
set(LANG C)
set(RunCMake_USE_LAUNCHERS TRUE)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/NinjaLauncherSingleBuildFailure")
configure_file("${RunCMake_SOURCE_DIR}/error.c" "${RunCMake_TEST_SOURCE_DIR}/error.c" COPYONLY)
set(CASE_CMAKELISTS_SUFFIX_CODE [=[
add_executable(error error.c)
]=])
run_ctest(NinjaLauncherSingleBuildFailure)
endfunction()
run_NinjaLauncherSingleBuildFailure()
# A build rule that is terminated by a signal (rather than exiting non-zero)
# must still be reported as a build error. POSIX-only: it relies on signal
# semantics.
if(UNIX)
function(run_NinjaLauncherSignalBuildFailure)
set(RunCMake_USE_LAUNCHERS TRUE)
set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/NinjaLauncherSignalBuildFailure")
# A tiny script that terminates its own shell with SIGTERM. Kept in a
# file (rather than an inline "-c" command) so the "$$" is not mangled by
# build-tool command-line escaping.
file(WRITE "${RunCMake_TEST_SOURCE_DIR}/signal.sh" "kill -s TERM $$\n")
set(CASE_CMAKELISTS_SUFFIX_CODE [=[
add_custom_command(
OUTPUT signal.out
COMMAND /bin/sh "${CMAKE_CURRENT_SOURCE_DIR}/signal.sh"
VERBATIM)
add_custom_target(signal ALL DEPENDS signal.out)
]=])
run_ctest(NinjaLauncherSignalBuildFailure)
endfunction()
run_NinjaLauncherSignalBuildFailure()
endif()
endif()