August 21, 2018

Makefile .ONESHELL

Suppose you wanted to take user input in your Makefile and then use it in subsequent commands you might try

newfile:
  @printf "Filename: "
  @read FILE
  touch $$FILE

This would not work because each command in that recipe is run in a separate shell. You’d have to cram it all in one line

newfile:
  @printf "Filename: " && read FILE && touch $$FILE

This works but is not super readable, fortunately make provides a phony target .ONESHELL which basically lets you write a full script inline in the recipe. The full contents of the recipe are passed to a single shell to be executed so you might write

.ONESHELL:
newfile:
  @printf "Filename: "
  @read FILE
  touch $$FILE

This would be almost correct except for the fact that the second @ would be passed verbatim to your shell, therefore with .ONESHELL only the first character is checked to see if it is special. So your final recipe looks like

.ONESHELL:
newfile:
  @printf "Filename: "
  read FILE
  touch $$FILE

Note: The default make command that ships with MacOS does not support .ONESHELL just yet, I had to brew install make and then invoke it via gmake to get things to work.