Hi,
I want to document an observation i made when building a test project on Windows 11 with CMake 3.31.6 via a Powershell command line in Windows Terminal using the Ninja and MinGW Makefiles generators. When the Terminal window is closed while the build is still running, CMake doesn’t seem to forward the appropriate events to the make
or ninja
executing the generated makefiles.
Background: For a project, i was trying to perform parts of the build as a cross compilation in a Docker/Podman container using add_custom_target
to call the docker
/podman
executables for setting up the container and starting it (in non-detached mode). This worked fine, except for cancelling the build process in VSCode, which i used as an IDE for the CMake project. Although the (Bash) script running inside the container handled signals like SIGTERM
and SIGHUP
by cancelling the container build, the container just kept running, requiring it to be stopped manually with docker stop
.
After a lot of unsuccessful experiments with the container configuration, i set up a reduced test project (see below) which seems to point to cmake --build
(which VSCode calls) might be partly causing this problem. I tested in a Windows Terminal window and checked which signals the script running inside the container receives when i simply close the window:
It turns out that when the container is run directly with docker run
, it receives a SIGTERM
, so does it when it is run via ninja test
or make test
with the CMake generated Ninja or MinGW makefiles. This indicates that ninja
and make
forward an appropriate event to the docker
subprocess invoked by them. However, when run with cmake --build . --target test
, the container receives no signal at all and keeps running (maybe CTRL_CLOSE_EVENT
in handler set up by SetConsoleCtrlHandler not handled or propagated correctly?).
Another interesting observation is, that when docker
is called via WSL2 with wsl -- docker run ...
in add_custom_target
, cancelling the build in VSCode is working fine, with the container receiving a SIGHUP
signal - this of course also works when invoked via Windows Terminal (though in this case with a SIGTERM
). This confuses me, as in this case wsl.exe
seems to receive some sort of event which is appropriately handled, even when called via cmake --build
. I’ll use that as a workaround for now.
My first thought was “CMake should handle its subprocesses like make/ninja” but when seeing that it worked wsl.exe
, i thought “maybe make and ninja should do it like WSL?”. Or maybe signal/event handling in Windows console applications is just a general mess? Maybe some of you can make sense of this. It would be nice if this could work more robustly, but i’m not sure if CMake can do much about it.
Here is the test project:
build
script running inside the container:
#!/usr/bin/env bash
handler_SIGHUP()
{
echo "SIGHUP received"
exit 1
}
handler_SIGTERM()
{
echo "SIGTERM received"
exit 1
}
handler_SIGKILL()
{
echo "SIGKILL received"
exit 1
}
handler_SIGINT()
{
echo "SIGINT received"
exit 1
}
handler_SIGABRT()
{
echo "SIGABRT received"
exit 1
}
trap handler_SIGHUP SIGHUP
trap handler_SIGTERM SIGTERM
trap handler_SIGKILL SIGKILL
trap handler_SIGINT SIGINT
trap handler_SIGABRT SIGABRT
for i in {1..100000}; do
echo "${i}"
sleep 1
done
Dockerfile
:
FROM debian:12.9
RUN groupadd builder
RUN useradd -g builder -s /bin/bash builder
RUN mkdir /build \
&& chown builder:builder /build
COPY build /bin/
RUN chmod +x /bin/build
USER builder
ENTRYPOINT [ "build" ]
WORKDIR /build
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.25)
# No languages, this project works without a compiler.
project(test LANGUAGES)
find_program(
CONTAINER_MANAGEMENT_EXECUTABLE
NAMES "docker" "podman"
DOC "Location of container management executable (docker or podman)."
REQUIRED
)
add_custom_target(
"test"
COMMAND "${CONTAINER_MANAGEMENT_EXECUTABLE}"
build
--tag "test-image"
--file "${CMAKE_CURRENT_SOURCE_DIR}/Dockerfile"
.
COMMAND "${CONTAINER_MANAGEMENT_EXECUTABLE}"
run
--name "test-container"
--rm
"test-image"
USES_TERMINAL
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
add_custom_target(
"cleanup"
COMMAND "${CONTAINER_MANAGEMENT_EXECUTABLE}"
image rm "test-image" --force
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)