PonyEars
PonyEars

Reputation: 2254

Force Makefile to execute script after building any target (just before exiting)

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.

Upvotes: 5

Views: 5866

Answers (4)

Evandro Coan
Evandro Coan

Reputation: 9466

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

Upvotes: 1

janos
janos

Reputation: 124704

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

Upvotes: 2

MadScientist
MadScientist

Reputation: 100926

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

Upvotes: 4

Gavin Smith
Gavin Smith

Reputation: 3154

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.

Upvotes: 3

Related Questions