I’ve made a detailed blog post about how all of this works.
This should go on top of every Makefile.
MAKEFLAGS += --no-builtin-rules --no-builtin-variables --warn-undefined-variables unexport MAKEFLAGS .DEFAULT_GOAL := all .DELETE_ON_ERROR: .SUFFIXES: SHELL := bash .SHELLFLAGS := -eu -o pipefail -c escape = $(subst ','\'',$(1)) define noexpand ifeq ($$(origin $(1)),environment) $(1) := $$(value $(1)) endif ifeq ($$(origin $(1)),environment override) $(1) := $$(value $(1)) endif ifeq ($$(origin $(1)),command line) override $(1) := $$(value $(1)) endif endef
Quote command arguments and use the escape
function on variables and shell output.
var := Includes ' quote test: printf '%s\n' '$(call escape,$(var))'
var := Includes space test: printf '%s\n' $(var)
var := Includes ' quote test: printf '%s\n' '$(var)'
cwd := $(shell pwd) test: printf 'In directory %s\n' '$(call escape,$(cwd))'
cwd := $(shell pwd) test: printf 'In directory %s\n' $(cwd)
cwd := $(shell pwd) test: printf 'In directory %s\n' '$(cwd)'
Use the noexpand
function on environment variables or variables that can be overridden on the command line.
has_default ?= Default value $(eval $(call noexpand,has_default)) test: echo '$(call escape,$(has_default))'
has_default ?= Default value test: echo '$(call escape,$(has_default))'
has_default ?= Default value export has_default test: echo "$$has_default"
$(eval $(call noexpand,ENV_VAR)) test: echo '$(call escape,$(ENV_VAR))'
test: echo '$(call escape,$(ENV_VAR))'