Question

I'm wondering if there's a way to implement trap in GNU make, similar to that built into BASH?

If the user presses CTRL-C, or if make itself fails (non-zero exit), I'd like to call a particular target or macro.

Was it helpful?

Solution

No. GNU make’s signal handling already leaves a lot to be desired. From within its signal handler, it calls functions like printf that are not safe to be called from within a signal handler. I have seen this cause problems, for example .DELETE_ON_ERROR rules don’t always run if stderr is redirected to stdout.

For example, on a CentOS 7.4 box:

  1. Create the following Makefile:

    .DELETE_ON_ERROR:
    
    foo:
            touch $@
            sleep 10
    
  2. Open it in vim and run :make,

  3. While it is sleeping, hit Ctrl-C

Vim/make prints

Press ENTER or type command to continue
touch foo
sleep 10
^C
shell returned 130

Interrupt: Press ENTER or type command to continue

Make was sent an interrupt signal, but foo still exists.

OTHER TIPS

At this point in time, GNU make doesn't have native support.

There is a reliable workaround however:

.PHONY: internal-target external-target

external-target:
  bash -c "trap 'trap - SIGINT SIGTERM ERR; <DO CLEANUP HERE>; exit 1' SIGINT SIGTERM ERR; $(MAKE) internal-target"

internal-target:
  echo "doing stuff here"

This catches interruptions, terminations AND any non-zero exit codes.

Note the $(MAKE) so cmdline overrides and make options get passed to submake.

On trap:

  • clear trap handler (with -)
  • do the cleanup
  • exit with non-zero exit status, so build automation tools report the failed build.

DELETE_ON_ERROR does NOT work for directories, so this is key for cleaning up after mktemp -d, for example

Replace <DO CLEANUP HERE> with valid CMD.

A simplified version of @kevinf’s answer which seems good enough for basic cases:

run:
    bash -c "trap 'docker-compose down' EXIT; docker-compose up --build"

(This example is for a reason: docker-compose up does say

When the command exits, all containers are stopped.

but it does not rm the stopped containers like docker run --rm would, so you can still see them with docker ps -a.)

Make does not support it, but using BASH tricks you can accomplish something similar.

default: complete

complete: do_mount
        echo "Do something here..."

do_mount:
        mkdir -p "$(MOUNTPOINT)"
        ( while ps -p $$PPID >/dev/null ; do \
                sleep 1 ; \
        done ; \
        unmount "$(MOUNTPOINT)" \
        ) &
        mount "$(MOUNTSOURCE)" "$(MOUNTPOINT)" -o bind

The "unmount" will run after the "make" completes. This is usually a satisfactory solution if you are attempting to cleanup operations that may occur during the build but are not cleaned up normally on "make" exit.

No. As far as I know there is no such functionality.

make produces return codes. As far as I can remember right now, it returns 0 for success, 2 for failure (please check the documentation). Therefore, would it be enough for you to wrap make inside a shell script for example?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top