Make

Created: 17 Dec 2020
Updated: 23 Apr 2025
makebook.png
wiki english wikibooks
gnu home docs
bsd man
posix home
fork https://justine.lol/make/
alternatives just Task Snakemake

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

makefile.png
Figure 1: from go.embeddedexpert.io
  • 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 MAKEFILE to 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: https://www.gnu.org/software/make/manual/html_node/Standard-Targets.html
    • 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

.ONESHELL: runs all commands the same shell
.POSIX: should be the first line
.SUFFIXES: disable all default inference rules
.DELETE_ON_ERROR: will delete the target if a command fails (NO BY DEFAULT)
.PHONY: make target not a regular target file

default targets

%:     ; $(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)
    1. cli
    2. makefile
    3. environment
    4. 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

implicit / predefined / default

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
SHELL eg: "bash"
MAKE_FLAGS  
MAKEFILE_LIST  
.SHELLFLAGS eg: "-eu -o pipefail -c"
.RECIPEPREFIX use instead of tabs
.DEFAULT_GOAL default target

magic / automatic variables

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

shell cmd exec and replaces \n with space

strings

https://www.gnu.org/software/make/manual/html_node/Text-Functions.html

word n,text "n"th word in in text
wordlist n,m,text text word-slicing from "n" to "m"
words text number of words
firstword text  
lastword text  
findstring needle,text returns "needle" or "" if not in text
filter pat%..,text remove words that match "pat%"
filter-out pat%..,text remove words that do NOT match "pat%"
sort text sort words, remove dups
strip text trim and squash whitespaces
subst from,to,text substitute literal words
patsubst pat,repl,text substitute pattern% words, text can use *

filenames

https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html

join list,list zipWith (<>)
wildcard glob*Path filesystem match, space separated if many
(not)dir names.. like shell's basename/dirname
abspath names.. absolute path, might not exist, no follow links
realpath names.. absolute path, must exist
basename names.. removes suffix/extension
suffix names.. extract suffix
addsuffix suffix,names..  
addprefix prefix,names..  

snippets

stu4.png
Figure 2: Stuart Feldman
  • 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)
    
  • set bash pipefail, use either of these

    SHELL = /bin/bash -o pipefail
    .SHELLFLAGS = -eu -o pipefail -c
    

gotchas

tools

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"