Hi.
GNU Make deletes partially updated target files when it is interrupted. I am interested in this behavior. I did not look into the source code, but did some experiments to understand how GNU Make behaves. Ctrl-C is a normal way to terminate GNU Make. However, the INT signals will be sent to all the fore-ground processes almost at the same time. This makes it difficult to understand how it is working behind the scenes. So, I sent the INT signal to each process by running 'kill -INT <PID>' individually. [Sample code] $ cat Makefile test.txt: ./create-file.sh $@ $ cat create-file.sh #!/bin/bash echo hello > $1 sleep 30 echo bye >> $1 [Test] $ rm -f test.txt $ make ./create-file.sh test.txt # Here, open another terminal. # Run 'ps aux' to see the process ID of Make. # Run 'kill -INT <PID-of-Make> # These must be done within 30 secs. # Then you will see make: *** Deleting file 'test.txt' But, GNU Make does not exit immediately; It waits until the child process finishes. After the child process exits, GNU Make exits as well. As far as I tested, GNU Make handles 5 signals (HUP, INT, QUIT, USR1, TERM) for the automatic target deletion, but in 3 different manners. [1] HUP, INT, QUIT When GNU Make receives the INT signal, it shows the "*** Deleting file ..." message and deletes the target file. It waits until the child process finishes presumably by running the 'wait' syscall. If the child exits by a signal (i.e. WIFSIGNALED flag is set), GNU Make shows "make: *** [Makefile:2: test.txt] Interrupt" message, and exits. Otherwise (i.e. if the child exits normally), GNU Make exits without any further message. HUP and QUIT work in a similar way. [2] USR1 When GNU Make receives the USR1 signal, it does not do anything at this moment. (but GNU Make remembers it) GNU Make waits until the child process exits. If the child process exits with a signal, GNU Make deletes the target and exits. If the child exits without a signal, GNU Make shows the message "Successfully remade target file ...' and keeps the target file. [3] TERM When GNU Make receives the TERM signal, it immediately deletes the target file and exits without waiting for the child process. The child process is still running, and re-parented. As a comparison, I wrote a shell script, which deletes the target file when it is interrupted. [Sample code 2] $ cat foo.sh #!/bin/bash set -e trap "echo Deleting file test.txt; rm -f test.txt" INT ./create-file.sh test.txt echo "exiting shell script" [Test 2] $ ./foo.sh # Here, open another terminal. # Run 'ps aux' to see the process ID of the './foo.sh'. # Run 'kill -INT <PID-of-foo.sh>' Nothing happens at this moment. After the child process './create-file.sh test' finishes, you will see the "Deleting file test.txt". When Bash receives the INT signal, it just remembers the fact that it has receives the signal, then invokes the trapping handler _after_ the child process finishes. To understand how signals are handled by shells, https://www.cons.org/cracauer/sigint.html is a really interesting material. Bash handles the INT signal in WCE (Wait and Cooperative Exit) manner by default. If and only if the child fore-ground process finished by the INT handler (i.e. WIFSIGNALED=1, WTERMSIG=2), the bash script is also terminated. If a shell script traps the INT signal, it delays the handler until the current fore-ground process finishes. This avoids the race between - the INT signal handler - the child process currently running Of course, it is implementation-dependent, and a different shell may behave differently. For example, another popular shell, dash, handles the INT signal in WUE (Wait and Unconditional Exit) manner. Here, my questions: (Q1) As you see in the first test, GNU Make deletes the target file before the child process finishes. I think, if the child process writes to the target file after that, the target file might be left over. In fact, the target file 'test.txt' is left over in the example above because 'create-file.sh' writes the 'bye' message at the last moment. (Q2) If my worry is right, is it a good idea to change the behavior like shells in order to avoid the race? When GNU Make receives a signal, it just remembers that. After the child processes are all finished, it deletes the targets that could be partially updated. (Q3) Why are there 3 different ways to handle signals? (Q4) After GNU Make receives HUP, INT, QUIT, TERM signals, it gets back the handler to SIG_DFL, and sends the same signal to itself. With this, it can properly propagate WIFSIGNALED and WTERMSIG to the upper process. In contrast, when GNU Make receives the USR1 signal, it does not set WIFSIGNALED. So, from the upper process, it looks as if it exited normally. Is this an intended behavior, or should it be corrected? -- Best Regards Masahiro Yamada