unmake
follows a relatively stiff reading of the POSIX make
standard. POSIX discipline helps to maintain a higher degree of portability for makefiles, across many different operating environments.
makefile
.PHONY: all
all:
@echo "Hello World!"
Usage:
$ make
Hello World!
See the fixtures directory for more examples.
- The lowercase
makefile
filename has faster precedence order thanMakefile
. .PHONY
marks logical targets likeall
, intended to re-run whenever the task is called for.- The first non-special target with no prerequisites is the default target, conventionally named
all
. - Minimal UNIX or Windows-specific arguments supplied to the
echo
command. - No single quotes (
'
) string arguments, which may break in certain Windows environments. - Hyphen-minus (
-
) and.IGNORE
suppress the overallmake
exit code. - Hyphen-minus (
-
) /.IGNORE
can turn make commands into soft assertions, which can emit console messages without short-circuiting the build task tree. - Hyphen-minus (
-
) /.IGNORE
are often associated withuninstall
andclean*
task idempotence. .IGNORE
without a prerequisite is unsafe to apply globally.- At (
@
) /.SILENT
can reduce log noise and mitigate certain types of sensitive data leaks. - At (
@
) /.SILENT
are generally safe to apply to globally. - At (
@
), hyphen-minus (-
), and/or plus (+
) prefixes can be combined for the same command And prerequisites may appear in.SILENT
and/or.IGNORE
special targets. For example, anuninstall
orclean*
command is likely to appear to be simultaneously.PHONY:
,.SILENT:
, and.IGNORE: <task>
.
unmake targets POSIX 202x Issue 8, Draft 3.
https://www.opengroup.org/austin/
Note that the standard POSIX make documentation sometimes uses the terms blank lines and comments interchangeably.
We do our best to catch many of the more obvious POSIX make mistakes, but some mistakes may continue to slip up nonetheless. For example:
- Accidents hiding inside macro expressions
- Accidents hiding inside command output
- Accidents hiding inside
include
chains - Misuse of reserved target names
- Behavior during live
make
script execution - GNU/BSD/etc. extensions beyond the POSIX standard
- Variance in make implementation adherence to POSIX
- Older make implementation releases targeting older POSIX standards
- Clock and file system timestamp variance
- Files not named like
makefile
or*.mk
- Logic errors
make users are advised to generally familiarize with basic make usage, syntax, and semantics, in order to resolve problems faster.
Note that make is not aware of the significance of the makefile's own timestamp when calculating whether a build is out of date. Whenever altering a makefile's contents, we recommend to reset the build, then redo the build. Such as running make clean
, then make
.
UNIX text files use UTF-8. Other encodings are likely to cause text processing problems with make and other UNIX components.
<utf-16>
<utf-8>
<ascii>
- Configure EditorConfig and text editors to use UTF-8 for all text files, other than certain Windows-centric files.
The legacy line endings CRLF (\r\n
) and CR (\r
) are out of spec. make expects LF (\n
).
In fact, the POSIX standard specifies that text files the LF line ending, as well as a final LF End Of Line (EOL) character at the end of any non-zero-byte text file.
We briefly list some illustrative makefile contents expected to fail parsing.
all:<CRLF>
echo "Hello World!"
all:<CR>
echo "Hello World!"
We briefly list some illustrate makefile contents expected to pass parsing.
all:<LF>
echo "Hello World!"<LF>
- Note that recent editions of Notepad are LF-aware, but may continue to accidentally default to CRLF when creating new files. Use a more capable text editor, such as emacs, nano, Notepad++, vim, VSCode, etc.
- Configure EditorConfig and your text editor, to default most any non-Windows-centric text files to LF line endings and a final EOL.
- If necessary, relaunch the text editor and resave the file.
- Reset git's configuration (both the git-config and gitattributes systems) to the default behavior, which tends to preserve file endings as written
- Apply tofrodos's
fromdos
utility on affected files
make generally expects rule commands to be indented with a tab (\t
).
Command lines preceded by an escaped newline (\\\n
), as in a command line argument continuation, may omit the tab, or indent arguments with additional tabs.
Whitespace may present issues in macro expansions.
Whitespace may present issues in otherwise empty lines.
Whitespace may present issues leading include lines, macro definitions, or rules.
all:
echo "Hello World!"
all:
<spaces>echo "Hello World!"
all:
<mixed spaces and tabs>echo "Hello World!"
M = "Hello World!"
all:
echo $( M )
<space>include foo.mk
<space>
<space><space>
<tab>
all:
<tab>echo "Hello World!"
M = "Hello World!"
all:
echo $(M)
include foo.mk
- Configure EditorConfig and your text editor, to use hard tabs for indentation in makefiles. Reindent the rule declarations. Resave the file.
- Indent
*.go
files with hard tabs as well. - Other languages are steadily converging on four spaces for indentation, like C, C++, CSS, HTML, Java, JavaScript, Python, Ruby, Rust, shell, ...
- LISP and free-form text files, may not conform to K&R style C indentation. They use variable width indentation, which EditorConfig can model as essentially a form of single space indentation.
- Consider indenting any multiline command arguments an additional tab column deeper than its parent command, for readability.
- Note that Markdown and ebooks sometimes present indentation quirks.
- Consider enabling visible whitespace markers in your text editor.
- When in doubt, consult a hex editor.
Rule declarations generally require at least one of the following: A prerequisite, an inline command, and/or an indented command.
Rules intentionally set to empty, should generally feature a trailing semicolon (test:;
).
Ancient makefiles once used a convention of an empty prerequisite, in order to force make to freshly rebuild other targets. However, modern make enjoys the standard .PHONY
special target for this.
Note that certain special targets are allowed at parse level to be declared with zero prerequisites + zero inline commands + zero indented commands: .POSIX:
, .IGNORE:
, .NOTPARALLEL:
, .PHONY:
, .PRECIOUS:
, .SILENT:
, .SUFFIXES:
, and .WAIT:
. They may or may not accept a semicolon inline command, even a blank one.
test:
DIR: FORCE
ls DIR
FORCE:
test: unit-test
test:
echo "Hello World!"
test:; echo "Hello World!"
test:;
# test:
<remove extraneous rules>
.PHONY: DIR
DIR:
ls DIR
- Give the rule something useful to do: Introduce at least one prerequisite, indented command, and/or inline command.
- Use
.PHONY
to denote targets that should always be freshly rebuilt. - Explicitly mark empty rules with reset notation (
<target>:;
) - Certain special targets may be allowed to be empty. Other special targets may reject the
<target>:;
reset notation. - Comment out temporarily empty rules
- Remove extraneous rules
Due to incompatible semantics between different make implementations for the :=
operator, the POSIX standard discourages this operator.
M := hello
M = hello
M ::= hello
M :::= hello
M ?= hello
M != echo hello
M += hello
- Identify the desired behavior, and apply a corresponding POSIX compliant macro assignment operator.
Macro definitions take the form <name> <assignment operator> [<value>]
.
The first two tokens, <name>
, and <assignment operator>
are required. The <value>
may be omitted, when declaring a blank macro.
=
=1
A
(Or the relevant POSIX make assignment operator)
A = 1
A=
(Or the relevant POSIX make assignment operator)
- Bind a specific macro name to some value.
- Bind a specific macro name to a blank value.
include lines use whitespace to delimit separate file paths. However, POSIX prohibits include lines from using double-quotes (""
).
include "foo.mk"
include foo.mk
- Remove double quotes from include paths
- Avoid spaces in file and directory names
- Simplify path names
When declaring rules with special targets, avoid supplying multiple targets.
Note that .WAIT
is ignored when specified as a direct target.
.SILENT .IGNORE:
.SILENT test: unit-test
.WAIT test lint: unit-test
.SILENT:
.IGNORE:
.SILENT: unit-test
test: unit-test
test: unit-test .WAIT lint
- Limit special targets to once per rule declaration.
- Avoid using the
.WAIT
special target as a direct target. Instead, use.WAIT
as a prerequisite modifier.
Note that escaped newlines are distinct from newline literals, for example, \n
in the UNIX command printf "Hello World!\n"
.
POSIX make presents two distinct semantics for multiline expressions, using escaped newlines (\\\n
).
The portability risk of multiline rule commands is relatively minor. Most make implementations set the SHELL
macro to a POSIX compatible sh interpreter able to pre-process escaped newlines, even when make itself runs in certain non-POSIX shells.
Note that POSIX prohibits escaped newlines in include lines.
When an escaped newline occurs in a macro definition value, then the escaped newline is replaced with a single space.
When an escaped newline occurs in a rule command, then the escaped newline is preserved and forwarded as a part of the final multiline command to be executed.
A subsequent rule command lines after a preceding escaped newline, is allowed to optionally omit the usual tab indentation. Regardless, a tab in the first column there is not preserved when make generates the final multiline command.
Note that in both cases of multiline macro definitions and multiline rule commands, whitespace sensitivity may lead to subtle processing errors.
Escaped newlines that directly meet the end of the file (<eof>
), or subsequent lines that don't make sense for the preceding line, may trigger parse errors.
Escaped newlines featuring whitespace between the backslash and the line feed, may trigger parse errors.
We encourage makefile authors to generally limit use of multiline makefile instructions to rule commands, where they can help to tidy up commands with long lists of arguments.
include \
foo.mk
include foo.mk
NAMES \
= Alice\
Bob\
Charlie
NAMES = Alice Bob Charlie
NAMES = Alice\
Bob\
Charlie
.PHONY: \
all \
foo \
clean
all: foo
foo: foo.c
gcc -o foo foo.c
clean:
rm -f foo
provision :
pip install bashate yamllint
provision :
pip install \
bashate \
yamllint
provision :
pip install -r requirements-dev.txt
- Configure EditorConfig to remove most trailing whitespace.
- Handle whitespace carefully.
- Handle multiline termination carefully.
- Consider using multilines for complex macro definitions, long prerequisite lists, complex commands, or commands expected to grow longer over time.
- When possible, track build-time packages with a package manager-specific configuration file.
- Consider moving complex rule logic to a separate makefile, script, or compiled application.
- For classic Windows environments, acquire make from WSL, Chocolatey, etc., which provide POSIX compatible interpreters.
- For vintage environments like MS-DOS, FreeDOS, or OS/2 Warp, move complex rule commands to a dedicated script.
Implementation-specific syntax may trigger parse errors. For example, syntax specific to GNU make or BSD make.
.for i in 1 2 3
test-double-${i}:
@echo "2 * ${i}" | bc
.endfor
- Consider moving complex logic to a separate makefile, script, or compiled application.
- For clarity, consider renaming GNU-specific makefiles to
GNUmakefile