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))'