简体   繁体   中英

run before and after each target in a makefile

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}" user@foo.com
post:
       @echo post
       @echo post | mailx -s "Finish {target}" user@foo.com
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.

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 $@' me@my.host
END = mailto --subject 'Finished target $@' me@my.host

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 $*" me@my.host
/bin/sh -c "$@"
r=$?
mailto --subject "ended $* with exit code $r" me@my.host
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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM