help-make
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]