--- title: Makefile subtitle: best practices layout: nosidebar links: - {rel: stylesheet, href: 'assets/css/bash.css'} features: - note: This should go on top of every Makefile. sections: - do: - | 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 - note: Quote command arguments and use the `escape` function on variables and shell output. sections: - do: - | var := Includes ' quote test: printf '%s\n' '$(call escape,$(var))' dont: - | var := Includes space test: printf '%s\n' $(var) - | var := Includes ' quote test: printf '%s\n' '$(var)' - do: - | cwd := $(shell pwd) test: printf 'In directory %s\n' '$(call escape,$(cwd))' dont: - | cwd := $(shell pwd) test: printf 'In directory %s\n' $(cwd) - | cwd := $(shell pwd) test: printf 'In directory %s\n' '$(cwd)' - note: Use the `noexpand` function on environment variables or variables that can be overridden on the command line. sections: - do: - | has_default ?= Default value $(eval $(call noexpand,has_default)) test: echo '$(call escape,$(has_default))' dont: - | has_default ?= Default value test: echo '$(call escape,$(has_default))' - | has_default ?= Default value export has_default test: echo "$$has_default" - do: - | $(eval $(call noexpand,ENV_VAR)) test: echo '$(call escape,$(ENV_VAR))' dont: - | test: echo '$(call escape,$(ENV_VAR))' --- I've made a [detailed blog post] about how all of this works. {: .alert .alert-info } [detailed blog post]: {{ site.baseurl }}{% post_url 2020-05-20-makefile-escaping %} {% for feature in page.features %} {{ feature.note | markdownify }} {% for section in feature.sections %}
{{ guide }}
{{ guide }}