I have been reading the GNU Make manual. I knew about some of these features already, implicit rules, special targets etc. I’ve used .PHONY for a while, without *really* knowing how it works (and thus, how to properly use it).

Today, I’ve learnt about .ONESHELL (new in gmake 3.82).


The .PHONY target is usually used to optimize make so that targets like ‘all’, ‘clean’ and even ‘dist’ don’t look for files named ‘all’, ‘clean’ or ‘dist’.

TARGETS=hello goodbye
all: build dist
build: $(TARGETS)
	-$(RM) *.o
	-$(RM) $(TARGETS)

dist: hellogoodbye.tar.gz
%.tar.gz: $(TARGETS)
	tar czf $@ $^

.PHONY: all build clean dist

If there exist hello.c and goodbye.c source files in the current directory, then invoking ‘make build’ will compile and link the targets individually and ‘make dist’ will tarball them. ‘make clean && make dist’ will force a full rebuild.


In the most recent release of GNU Make, 3.82, if the target ‘.ONESHELL’ is defined, then make will execute the commands in every recipe in a single shell invocation. Combine that with the ‘SHELL’ variable, and you have an interesting tool to hand.


        @activate_this = 'venv/bin/'
        execfile(activate_this, dict(__file__=activate_this))
        import requests
        r = requests.get("")

$(VIRTUALENV): requirements.txt
        @import subprocess;run_cmd=lambda
        run_cmd("virtualenv --distribute $(VIRTUALENV)")
        activate_this = 'venv/bin/'
        execfile(activate_this, dict(__file__=activate_this))
        run_cmd("pip install -r requirements.txt")
        run_cmd("touch $(VIRTUALENV)") # Update the timestamp

freeze: venv
        @import os;run_cmd=lambda s:os.execvp(s.split()[0],s.split()[0:])
        activate_this = 'venv/bin/'
        execfile(activate_this, dict(__file__=activate_this))
        run_cmd("pip freeze")

.PHONY: freeze get

Think of the possibilities.

One of the limitations of .ONESHELL is that if it is defined, it is defined globally (for that makefile). Unlike .PHONY, .ONESHELL cannot (yet?) be given a list of targets that it will act upon. That’s why, for now, I need to use those fancy ‘run_cmd’ lines because, now that I’ve switched to python, I no longer have bash.



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s