| wiki | english wikibooks |
| gnu | home docs |
| bsd | man |
| posix | home |
| original | |
| fork | https://justine.lol/make/ |
| alternatives | just Task Snakemake |
- 1976, at Bell Labs
- by Stuart Feldman
cli
make [-f Makefile] [VAR="foo"] [OPTIONS] [TARGETS]
- bare run of "make", defaults to running the first target found
- you can give more than 1 target
| -f | –(make)file | file | |
| -o | –old-file | target | never remake TARGET |
| -k | –keep-going | keep on errors | |
| -s | –silent | silent command print | |
| -i | –ignore-errors | ||
| -e | –environment-overrides | env>make vars | |
| -B | –always-make | make ALL targets | |
| -j | –jobs | n_jobs | parallel jobs |
| -n | –dry-run | ||
| -t | –touch | touch targets, without running | |
| -d | –debug | [abvijmn] | print debug |
| –warn-undefined-variables | when referenced | ||
| -r | –no-builtin-rules | ||
| -R | –no-builtin-variables |
language
- defines a dependency tree (DAG) of
rules(aka recipe)- between his structure and dependencies
- made of
targets(the vertices) - final exe is the root vertex
- use
include MAKEFILEto include another Makefile
target / output
- targets without deps are human made ones
- order does not matter
- but if multiple % patterns match, will use the last one matched (deps stills stacked?)
- multiple targets: for same dependencies
- recommended phony ones:
- all, help, clean, check, dist, run, deps, serve, deploy
- test: make it depend of the final binary
- (un)install: use PREFIX=/usr/local and DESTDIR
special targets
https://www.gnu.org/software/make/manual/html_node/Special-Targets.html
| .ONESHELL: | runs all commands the same shell |
| .POSIX: | must be the first line |
| .SUFFIXES: | disable all default inference rules |
| .DELETE_ON_ERROR: | make target be deleted if a command fails (NO BY DEFAULT) |
| .PHONY: | unconditionally make targets given |
| .PRECIOUS: | make target not being deleted, can be a pattern |
| .SECONDARY: | with no target, makes all targets secondary |
default targets
- https://www.gnu.org/software/make/manual/html_node/Catalogue-of-Rules.html
- https://www.gnu.org/software/make/manual/html_node/Suffix-Rules.html
%: ; $(CC) $*.o -o $* %.c: ; $(CC) -c $(CPPFLAGS) $(CFLAGS) %.cpp: ; $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) %.o: ; $(CC) $(LDFLAGS) n.o $(LOADLIBES) $(DLIBS)
prerequisites / dependency
can be split into different targets with the same name (?
| * | wildcard, filesystem matches, use it with $(wildcard) |
| % | wildcard |
commands / actions
- uses /bin/sh by default
- TAB indented
- use (\) for long ones
- use (;) to make one command of many
- each one runs on his own shell
- whole makefile fails if a command returns 1
prefixes
- ignore errors @ not print the command, use it to add indent comments + run even on no-exec mode
variables / macros
- are ALWAYS strings
- undefined variables are treated as empty strings (see –warn-undefined-variables)
- order of precedence (see override)
- cli
- makefile
- environment
- predefined
referencing
$a # for length=1 vars $(ab) # for length>1 vars ${ab} # " $(ab:.cpp=.o) # string replace
assignments
- ONLY evaluated when they are used
| = | dynamic/macro, evaluated each time is used |
| := | one time, evaluated procedurally |
| ::= | " |
| ?= | safe, only if undefined |
| += | appends with a space |
| != | exec shell command and assign it |
target specific
This defined a variable visible by Make.
%.o: CFLAGS = -O
This defines a variable also (? visible by shell subcommands.
%.o: export PAGER = more
goal specific
ifeq (,$(filter debug,$(MAKECMDGOALS)) CFLAGS += -Og endif
implicit / predefined
https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html
| CC | For compiler and compiler flags |
| CXX | " |
| CFLAGS | " |
| CPPFLAGS | |
| CXXFLAGS | |
| LDFLAGS | for flags passed to compiler when linking |
| LDLIBS | For flags about libraries when linking |
| MAKEFILE_LIST | list of Makefile's |
| MAKECMDGOALS | the target given |
| MAKEFLAGS | |
| SHELL | eg: "bash" |
| .SHELLFLAGS | eg: "-eu -o pipefail -c" |
| .RECIPEPREFIX | use instead of tabs |
| .DEFAULT_GOAL | default target |
magic / automatic
https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
| $@ | target's name (always one) |
| $(@D) | target's dir(name) |
| $(@F) | target's base(name) |
| $< | 1st prerequisite |
| $(<D) | 1st prerequisite's dir(name) |
| $(<F) | 1st prerequisite's base(name) |
| $^ | all prerequisites |
| $+ | all prerequisites, with dups |
| $? | new prerequisites (than the target) |
| $* | what "%" wildcard matched |
| $$ | literal "$" |
| $% | target's name, WHEN (ar)chive member ? |
| $¦ | ? order-only prerequisites ? |
functions
- https://www.gnu.org/software/make/manual/html_node/Functions.html
- do NOT add spaces between arguments, functions will see it
| shell | cmd | exec and replaces \n with space |
| foreach | varname, list , text | foreach V in L, runs and returns each TEXT |
| let | varname…,[list],text | simple expanded, only in scope |
| if | cond, expr, expr | conditional expansion, expands only needed |
| or | cond, [cond…] | expands to first non-empty cond |
| and | cond, [cond…] | expands to last if all non-empty |
| intcmp | l,r[,lp,eqp,rp] | integer comparison of l & r |
strings
https://www.gnu.org/software/make/manual/html_node/Text-Functions.html
| returns | ||
|---|---|---|
| word | n,text | text[n] |
| wordlist | n,m,text | text[n:m] |
| firstword | text | text[0] |
| lastword | text | text[-1] |
| words | text | len(text) |
| findstring | needle,text | find |
| filter | pat%…,text | remove |
| filter-out | pat%…,text | remove-if-not |
| sort | text | sort¦uniq |
| strip | text | trim and squeeze whitespaces |
| subst | from,to,text | s/from/to/ |
| patsubst | pat,repl,text | pattern% substitution, text can use * |
filenames
https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html
| join | xs… , xs… | zipWith (<>) |
| wildcard | glob*Path | filesystem match, space separated if many |
| (not)dir | xs… | like shell's basename/dirname |
| abspath | xs… | absolute path, might not exist, no follow links |
| realpath | xs… | absolute path, must exist |
| basename | xs… | removes suffix/extension |
| suffix | xs… | extract suffix |
| addsuffix | suffix, xs… | |
| addprefix | prefix, xs… |
control flow
https://www.gnu.org/software/make/manual/html_node/Conditional-Syntax.html
if(n)def VARNAME if(n)eq ($(CC),gcc) else # if... endif
snippets
autogenerated help target
help: # https://blog.ovhcloud.com/pimp-my-makefile/ @grep '^[^.#]\+:\s\+.*#' Makefile | \ sed "s/\(.\+\):\s*\(.*\) #\s*\(.*\)/`printf "3[93m"``printf "3[0m"` []/" | \ expand -t20 help: # https://lobste.rs/s/7svvkz/using_bsd_make#c_mar0yk @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
run N targets concurrently with –jobs=N
.PHONY: dev dev/dep1 dev/dep2 dev:; $(MAKE) -j2 dev/dep1 dev/dep2 dev/dep1: sleep 10 dev/dep2: sleep 60
dynamically set a variable at runtime (aka inside a target)
task1: $(eval ROBOTS = $(shell curl example.com/robots.txt)) @echo $(ROBOTS)
set bash pipefail, use either of these
SHELL = /bin/bash -o pipefail .SHELLFLAGS = -eu -o pipefail -c
$(foreach) expansion
dirs := a b c d files := $(foreach dir,$(dirs),$(wildcard $(dir)/*))
dynamically generating targets, each with their correspondent dependency
IMGS := $(wildcard org/images/*/*.jpg) THUMBS := $(subst .jpg,.thumb.jpg,$(IMGS)) $(THUMBS): %.thumb.jpg: %.jpg convert $< -resize 240x $@
gotchas
- no support for filenames with spaces
- filenames with
:must be escaped (aka "multiple target patterns. Stop." error) =assignment is perpetually evaluated (aka a macro definition)$needs to be always escaped with$$to be sent to commands as such\tfor indentation, NOT spaces- Will keep an incomplete target on failure, unless
.DELETE_ON_ERROR: - What is an
intermediatetarget? And how to stop them from being deleted? - to do Process Substitution we need to use bash -c, escape $ with $ and \, one for make other for bash itself
tools
| linter | checkmake unmake |
| visualizer | MakefileViz makefile2graph makefile-graph |
| TAP output | make2tap |
| library | makext |
trivia
Why the tab in column 1?
"Yacc was new, Lex was brand new. I hadn't tried either, so I figured this would be a good excuse to learn. After getting myself snarled up with my first stab at Lex, I just did something simple with the pattern newline-tab. It worked, it stayed. And then a few weeks later I had a user population of about a dozen, most of them friends, and I didn't want to screw up my embedded base. The rest, sadly, is history." -- Stuart Feldman, from "The Art of Unix Programming"