docs/requirements/cli-signal-forwarding.md
When the user runs bear -- make in a terminal and presses SIGINT
(Ctrl-C), the interrupt must reach the build. Bear must not swallow
the signal, leaving make and the whole process tree underneath it
running in the background. The same applies when a CI runner sends
SIGTERM to a running Bear to abort a build: the build being
supervised must stop too.
The exit code a signalled build produces -- and Bear's exit-code
contract in general -- is owned by
cli-exit-codes; this requirement covers only how
the signal reaches the build and how the process tree is torn down.
Users interact with a single command, bear. Which interception mode
runs is owned by
interception-mode-selection; the
contract below must hold identically in every mode.
bear -- <build> runs stops the buildSIGTERM (and SIGQUIT on Unix) to a running Bear stops
the buildSIGINT arrives
as SIGINT and a SIGTERM as SIGTERM, so a build that handles the
two differently sees the real one. Bear relays the signal rather than
substituting one of its ownbear -- must add no perceptible delay
compared with running the same command directlyA child that re-detaches into its own session can still survive.
Bear stops the whole process tree by signalling the build's process
group. A child that deliberately starts a new session of its own (a
daemon that calls setsid) leaves that group and may keep running
after Bear exits. On Linux, where the host provides a usable cgroup,
Bear closes this gap by terminating the build's cgroup instead, which a
child cannot leave; where no such cgroup is available Bear falls back
to the process-group behaviour and the limitation applies.
Windows signal coverage is limited. Only SIGTERM and SIGINT
are observed. Other Windows-specific termination mechanisms (such as
CTRL_BREAK_EVENT or a parent calling TerminateProcess on
bear.exe) are not explicitly handled.
Nested bear invocations are unsupported. Running
bear -- bear -- make is not a supported configuration and its
signal/exit-code behaviour is undefined. Users who need to record
multiple separate builds into a single database should use
--append (see output-append) rather than nesting Bear.
Given a long-running build under bear --:
When the test runs
bear -- sleep 10, waits briefly for the sleep to start, then sends a termination signal to thebearprocess, then bothbearand thesleepchild terminate within one second, andbearreports a non-success exit status.
Given a build interrupted mid-compile -- the compiler's input source is
a named pipe created by mkfifo with no writer, so the compiler blocks
reading it:
When the test runs
bear -- cc -x c -c source.c -o out.owithsource.cthe pipe, waits for the compiler to block, then sends a termination signal to thebearprocess (standing in for the interactive Ctrl-C), then bothbearand the compiler terminate within the time budget.
Given a build script that installs distinct traps for SIGINT and
SIGTERM -- each trap writes its own marker file and exits with its own
code (10 for INT, 20 for TERM) -- then blocks in a long sleep:
When the test runs the script under
bear --, waits until the script reports it is ready, and sendsSIGINTto thebearprocess, then theINTmarker appears (theTERMtrap did not fire) andbearexits10; and when the test repeats the run sendingSIGTERMinstead, then theTERMmarker appears andbearexits20-- the build saw the real signal, not a substitute.
Given a build script that spawns a background grandchild (sleep 60 &),
records the grandchild's pid, then blocks in its own sleep 60:
When the test sends
SIGTERMto thebearprocess, thenbearexits with a non-success status, and the recorded grandchild pid is no longer alive -- teardown reached the whole tree, not just the direct child. (On Linux with a usable cgroup the same holds even when the grandchild starts its own session withsetsid; without one, the process-group fallback in Known limitations applies.)
Given a build script that traps SIGTERM, writes a marker file from the
trap, and exits 42 from it:
When the test sends
SIGTERMto thebearprocess while the script blocks in a long sleep, then the marker appears -- the script received the signal and its trap ran, rather than being killed outright -- andbearexits42, the script's own exit code.
Given a build script that ignores SIGTERM (an empty trap) and loops
forever:
When the test sends
SIGTERMto thebearprocess, then bothbearand the build still end within the time budget -- the grace period expires and the build is forced -- andbearexits with a non-success status.
Given wrapper mode selected by configuration, and a build that invokes
the compiler through $CC -c on a named pipe with no writer, so the
compiler blocks mid-compile under the wrapper's supervision:
When the test sends
SIGTERMto thebearprocess, then bothbearand the blocked compiler end within the time budget, andbearexits with a non-success status -- the teardown contract holds in wrapper mode just as in preload mode.