On Fri, Jun 4, 2021 at 9:57 PM Paul Smith <psm...@gnu.org> wrote: > > On Fri, 2021-06-04 at 12:31 +0900, Masahiro Yamada wrote: > > GNU Make cleans up partially updated targets if the user interrupts > > before the build rules complete. > > > > If GNU Make does not do this, they will not be updated in the next > > run of 'make' because their timestamps are new while the contents are > > incomplete. > > > > This issue was asked in the Linux kernel ML [1], but I can reproduce > > it in simple test code. Please see the following case. > > It's pretty easy to see what's happening if you use strace. > > The thing to remember is that when you use ^C you're sending the signal > to the entire process group. That means that not only does make get a > SIGINT, but also your "cat" program gets a SIGINT, and dies. > > When the SIGINT signal handler is called in GNU make, it tries to clean > up IN THE SIGNAL HANDLER. This is a long-standing problem, since the > very first version of GNU make as far as I know: it does all kinds of > things in its signal handler which are not really valid in a signal > handler context. > > I have a partial solution to fix it, but it's actually extremely > difficult due to the really crappy signal handling model that POSIX > provides and my current solution still suffers from race conditions > that need to be resolved before it can be used. I haven't had time to > get back to this. > > Anyway: one of the things make will do in its signal handler is try to > write error messages to stderr. If you are piping the output of stderr > to a program, and that program has died, then writing to it will > generate a SIGPIPE. > > Because the write is inside the SIGINT signal handler, make is no > longer catching signals like SIGPIPE, so when it receives that SIGPIPE > it will simply exit immediately. > > A workaround for your situation, until this whole signal handler thing > can be resolved, is to ignore SIGINT in the program receiving the piped > data so it doesn't die when you hit ^C. > > This will work for example: > > $ make 2>&1 | (trap "" 2; cat) > echo hello > test.txt > sleep 10 > ^Cmake: *** Deleting file 'test.txt' > make: *** [/tmp/x10.mk:3: test.txt] Interrupt >
make 2>&1 | tee build.log is a very common use case, and people often miss to add (trap "" 2; ...), I think. I was wondering if I could take care of this in Makefile somehow. I wrote the following test Makefile. Do you think it will work reliably ? [Makefile with manual target cleanup] # If stderr is piped to another command, # we cannot rely on Make to clean up the target. # So, we do it manually. # I do not know how to detect if it is the write end of a pipe, # but at least, we can know it if it is not associated with a terminal. ifneq ($(shell test -t 2 || echo y),) manual_cleanup = trap "rm -f $@; exit 130" INT; endif build_command = echo hello > $@; sleep 10; echo bye >> $@ test.txt: $(manual_cleanup)$(build_command) $ rm -f test.txt ; make 2>&1 | cat trap "rm -f test.txt; exit 130" INT;echo hello > test.txt; sleep 10; echo bye >> test.txt ^C$ ls test.txt ls: cannot access 'test.txt': No such file or directory -- Best Regards Masahiro Yamada