Reputation: 3146
I would like to run a command before and after each target in a makefile.
so something like this
pre:
@echo pre
@echo running | mailx -s "Start {target}" [email protected]
post:
@echo post
@echo post | mailx -s "Finish {target}" [email protected]
j:
long_running_command && echo $@ > $@
k: j
long_running_command2 && echo $@ > $@
I would like to run pre and post for j and k. Ideally, I would like to get an email for each task that starts and stops.
Upvotes: 3
Views: 3456
Reputation: 89093
If you only have one command per recipe, you can do this by changing the configuration for the shell the commands are run in.
Have the config file run the pre
commands directly and trap EXIT
to run the after commands in.
For example:
$ cat Makefile
SHELL := BASH_ENV=/dev/fd/3 3<<<'echo before; trap "echo after" EXIT' /bin/bash
default:
echo default
other:
echo first
echo second
$ make default
echo default
before
default
after
However this may not be what you want if a recipe runs several commands, as the before and after code will run each time.
$ make other
echo first
before
first
after
echo second
before
second
after
And I don't know of a way (outside of recursive Makefiles) to have different recipes use different shells, so this won't work if you only want to set before/after for several recipes.
Upvotes: 1
Reputation: 100926
One way to do it is to modify all the recipes in your makefile to invoke some command. You can put it into a variable so it doesn't look too gross:
START = mailto --subject 'Started target $@' [email protected]
END = mailto --subject 'Finished target $@' [email protected]
j:
$(START)
long_running_command && echo $@ > $@
$(END)
k: j
$(START)
long_running_command2 && echo $@ > $@
$(END)
The nice thing about this is you can pick and choose which targets you want it for; maybe some of them don't need it. The disadvantage is if the command fails you won't get any "end" email at all.
If you really want to do it for every single target, then you can write a shell script that mimics the shell's behavior but also sends mail, while running the command.
$ cat mailshell
#!/bin/sh
# get rid of the -c flag
shift
mailto --subject "started $*" [email protected]
/bin/sh -c "$@"
r=$?
mailto --subject "ended $* with exit code $r" [email protected]
exit $r
(note this is totally untested but you get the idea I hope). Then in your makefile, set SHELL
to that shell:
SHELL := mailshell
j:
long_running_command && echo $@ > $@
k: j
long_running_command2 && echo $@ > $@
I guess you could still pick and choose by setting SHELL
as a target-specific variable only for those targets you wanted to use the shell.
One downside of this is that if you have recipes that have multiple lines you'll get an email for each line individually. You can work around this by enabling .ONESHELL:
to force the entire recipe to be passed to a single shell. However, I believe that this may require your mailshell
tool to be more sophisticated.
Upvotes: 3