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)
clean:
-$(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.
VIRTUALENV=venv
SHELL=/usr/bin/python
get: $(VIRTUALENV)
@activate_this = 'venv/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
import requests
r = requests.get("http://httpbin.org/get")
print(r.text)
$(VIRTUALENV): requirements.txt
@import subprocess;run_cmd=lambda s:subprocess.call(s.split())
run_cmd("virtualenv --distribute $(VIRTUALENV)")
activate_this = 'venv/bin/activate_this.py'
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/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
run_cmd("pip freeze")
.ONESHELL:
.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.