[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Make does not clean up the target when stderr is piped. Why?
From: |
Masahiro Yamada |
Subject: |
Re: Make does not clean up the target when stderr is piped. Why? |
Date: |
Sat, 5 Jun 2021 23:00:40 +0900 |
On Fri, Jun 4, 2021 at 9:57 PM Paul Smith <psmith@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