5

I'd like to execute a shell command when make exits, regardless of whatever target it built. Seems like make doesn't have a way to do this directly. However, here's an example of having make execute a shell command upon startup, regardless of the target:

Force Makefile to execute script before building targets

Is there any similar hack have make execute a shell command once just before exiting? I could put the command at the bottom of every target, but a) it would get executed multiple times, and b) that is ugly and difficult to manage.

Community
  • 1
  • 1
PonyEars
  • 2,144
  • 4
  • 25
  • 30
  • What's your use case? – JesperE Dec 16 '13 at 08:18
  • 1
    I need to mark a shared resource as released when exiting the make process (I mark it as busy when the makefile starts by using the approach shown in the link in my question). – PonyEars Dec 21 '13 at 08:59

4 Answers4

4

As janos says, there is no facility built into make to do it. You can use a wrapper such as he suggests. You an also, if you're willing to live with some amount of "bleah", push that into your makefile using recursion. It would look something like this, perhaps:

ifdef RECURSING

    ... normal makefile goes here ...

else

.DEFAULT all:
        @$(MAKE) RECURSING=true $@ ; r=$$? ; \
         <extra shell command here>; \
         exit $$r

endif
MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • Thanks, I like this approach the best because I can simply type make without having to remember to execute a script instead. – PonyEars Dec 15 '13 at 00:19
  • One question: I notice that I'm forced to type 'make all' with this instead of just being able to type 'make'. Is there a way to fix this? – PonyEars Dec 15 '13 at 00:46
  • Excellent, thanks! The gnu make documentation on .DEFAULT was a bit difficult to read, but I understand it now. – PonyEars Dec 15 '13 at 09:57
3

You could use two makefiles, one of which calls the other. For example

wrapper.mk:

foo bar:
        make -f other.mk $@
        @echo finished
.PHONY: foo bar

other.mk:

foo bar:
        @echo Building $@

Then running "make -f wrapper.mk foo", wrapper.mk runs other.mk to build foo. The .PHONY target means that the targets will be passed to other.mk for consideration everytime regardless of whether the files exist or not.

This requires listing all your targets in wrapper.mk - there may be ways round this using default rules or match-anything rules.

Gavin Smith
  • 3,076
  • 1
  • 19
  • 25
2

You could create a wrapper script that simply passes all arguments to make and then runs the custom action you want:

#!/bin/sh
make "$@" && ./custom_script.sh
janos
  • 120,954
  • 29
  • 226
  • 236
  • 1
    It might be possible to use a wrapper makefile instead of a wrapper shell script. This is like MadScientist's answer except there would be two makefiles instead of one. – Gavin Smith Dec 14 '13 at 19:10
  • Thanks, this is the hack I'd been using, but the downside is, one would have to remember to call the wrapper. – PonyEars Dec 15 '13 at 00:41
1

Combining @Gavin Smith and @MadScientist answers with these other questions:

  1. How to pass argument to Makefile from command line?
  2. Makefile match any target / task
  3. How to get the invoking target of makefile?
  4. Suppress "nothing to be done for 'all' "
  5. Makefile: declare all targets PHONY
  6. Makefile set global variable in target body

I built this:

Makefile

ECHOCMD:=/bin/echo -e
SHELL := /bin/bash

define DEFAULTTARGET :=
    printf 'Calling makerules.mk "%s"\n\n' "${MAKECMDGOALS}"
    make -f makerules.mk ${MAKECMDGOALS}

    printf '\n'
    printf 'Running something after all rules finished\n'
endef

%:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval IS_MAKEFILE_RUNNING_TARGETS=1)

all:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval IS_MAKEFILE_RUNNING_TARGETS=1)

makerules.mk

all:
    printf 'Calling my all rule\n'

foo:
    printf 'Calling my foo rule\n'

bar:
    printf 'Calling my bar rule\n'

xyzzy:
    printf 'Calling my xyzzy rule\n'

Usage examples:

  1. make

    printf 'Calling makerules.mk "%s"\n\n' ""
    Calling makerules.mk ""
    
    make -f makerules.mk
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule\n'
    Calling my all rule
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  2. make all

    printf 'Calling makerules.mk "%s"\n\n' "all"
    Calling makerules.mk "all"
    
    make -f makerules.mk all
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule\n'
    Calling my all rule
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  3. make all foo

    printf 'Calling makerules.mk "%s"\n\n' "all foo"
    Calling makerules.mk "all foo"
    
    make -f makerules.mk all foo
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule\n'
    Calling my all rule
    printf 'Calling my foo rule\n'
    Calling my foo rule
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  4. make all foo bar

    printf 'Calling makerules.mk "%s"\n\n' "all foo bar"
    Calling makerules.mk "all foo bar"
    
    make -f makerules.mk all foo bar
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule\n'
    Calling my all rule
    printf 'Calling my foo rule\n'
    Calling my foo rule
    printf 'Calling my bar rule\n'
    Calling my bar rule
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    
  5. make all foo bar xyzzy

    printf 'Calling makerules.mk "%s"\n\n' "all foo bar xyzzy"
    Calling makerules.mk "all foo bar xyzzy"
    
    make -f makerules.mk all foo bar xyzzy
    make[1]: Entering directory '/cygdrive/d/User/Downloads'
    printf 'Calling my all rule\n'
    Calling my all rule
    printf 'Calling my foo rule\n'
    Calling my foo rule
    printf 'Calling my bar rule\n'
    Calling my bar rule
    printf 'Calling my xyzzy rule\n'
    Calling my xyzzy rule
    make[1]: Leaving directory '/cygdrive/d/User/Downloads'
    printf '\n'
    
    printf 'Running something after all rules finished\n'
    Running something after all rules finished
    

Related questions:

  1. Stop executing makefile
  2. Force exit from a Makefile target without raising an error
  3. Suppress make rule error output
  4. Make: how to continue after a command fails?
  5. https://unix.stackexchange.com/questions/460606/make-how-to-suppress-make-error-messages-without-suppressing-other-output
  6. makefile use variable defined in target
  7. How do I get a makefile function to stop the current target?
  8. Can I make a makefile abort outside of a rule?
  9. Remove target from MAKECMDGOALS?
  10. How to detect if the makefile **`--silent`** / **`--quiet`** command line option was set?

Update

This is the same as the above, but now you have everything in a single Makefile:

  1. Getting the name of the makefile from the makefile

Makefile

ECHOCMD:=/bin/echo -e
SHELL := /bin/bash


ifeq (${IS_MAKEFILE_RUNNING_TARGETS},)

MAKEFILE_JUSTNAME := $(firstword ${MAKEFILE_LIST})

define DEFAULTTARGET :=
    printf 'Calling "%s" "%s"\n\n' "${MAKEFILE_JUSTNAME}" "${MAKECMDGOALS}"
    make -f ${MAKEFILE_JUSTNAME} ${MAKECMDGOALS}

    printf '\n'
    printf 'Running something after all rules finished\n'
endef

%:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)

all:
    @:
#   printf 'IS_MAKEFILE_RUNNING_TARGETS="%s"\n' "${IS_MAKEFILE_RUNNING_TARGETS}"

    $(if ${IS_MAKEFILE_RUNNING_TARGETS},,${DEFAULTTARGET})
    $(eval export IS_MAKEFILE_RUNNING_TARGETS=1)


else

all:
    printf 'Calling my all rule\n'

foo:
    printf 'Calling my foo rule\n'

bar:
    printf 'Calling my bar rule\n'

xyzzy:
    printf 'Calling my xyzzy rule\n'


endif
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144