Run a Makefile Recipe in One Shell with .ONESHELL
Run all lines of a make recipe in a single shell with .ONESHELL, so cd and variables carry over. Only the last line's exit status counts, so add -e to the shell flags.
A recipe changes directory on one line and uses the new directory on the next, and the second line runs in the wrong place.
cat > plain.mk <<'EOF'
show:
@cd /tmp
@echo "now in $$(pwd)"
EOF
cat > oneshell.mk <<'EOF'
.ONESHELL:
show:
@cd /tmp
@echo "now in $$(pwd)"
@false
@echo "this line still runs"
EOF
make -f plain.mk
make -f oneshell.mk
echo "exit status: $?"now in /tmp/run/S1724-1/w
now in /tmp
this line still runs
exit status: 0By default make starts a new shell for every recipe line, so cd /tmp is forgotten before pwd; the first file printed the original directory. With .ONESHELL: in the makefile, all lines of a recipe are passed to one shell and the second file printed /tmp. The second effect is less visible: the false on the third line did not stop anything, the next line still ran, and make finished with exit status 0. See Using One Shell.
With .ONESHELL only the status of the last command decides success, so add .SHELLFLAGS := -ec to stop at the first failure. The special target applies to the whole makefile wherever it appears, which is why the demonstration uses two files, and it needs GNU Make 3.82 or later. This was run with GNU Make 4.3.