Search code examples
bashshellmakefilelockingsh

Lock a whole target in a Makefile


I have a Makefile with a target that executes multiple system-wide operations (e.g. installing packages), so I want to lock the target in a way that other make processes will wait on that target, thus preventing parallel execution.

I tried using flock as explained in this answer, but I think the particularities of makefiles are getting in the way.

This is what I have right now:

LOCKFILE=/var/lock/makefile.lock
LOCKFD=200

mytarget:
    # make sure to release the lock in any case
    eval "exec $(LOCKFD)>$(LOCKFILE)"; trap "flock -xn $(LOCKFD) && rm -f $(LOCKFILE)" EXIT
    # get exclusive waiting lock
    flock -x $(LOCKFD)
    [regular target operations...]
    # release lock and delete the lock file
    flock -u $(LOCKFD); flock -xn $(LOCKFD) && rm -f $(LOCKFILE)

It fails with this error, because the file descriptor 200 is not properly set:

$ make mytarget
# make sure to release the lock in any case
eval "exec 200>/var/lock/makefile.lock"; trap "flock -xn 200 && rm -f /var/lock/makefile.lock" EXIT
# get exclusive waiting lock
flock -x 200
flock: 200: Bad file descriptor
Makefile:57: recipe for target 'mytarget' failed

There has to be something wrong with the eval definition, but I don't see what.


Solution

  • Make runs every command line in a new shell. I guess your fd is gone after eval. You can use a single command line and separate commands with ";". If you want to split the command line for clarity you need to end all but the last line with "\". See the make manual, Splitting Recipe Lines. Or use a shell script.