Skip to content
vara::works.dev
Go back

๐Ÿ› ๏ธ Makefile Cheatsheet

๐Ÿ› ๏ธ Makefile Cheatsheet

Table of contents

Open Table of contents

๐Ÿš€ Introduction

A practical Make utility cheatsheet covering GNU Make syntax, rules, variables, and functions.

๐Ÿงฉ Syntax

target: dependencies
    command
    command
    command
    ...

๐Ÿงช Basic Examples

๐Ÿ‘‹ Hello World

Create a simple Makefile that prints โ€œHello, World!โ€ when you run make hello.

hello:
    echo "Hello, World!"

Make Output:

% make hello 
echo "Hello, World!"
Hello, World!

Explanation:

When running make hello, Make checks whether the target hello is up to date by looking for a file named hello. Since the target has no prerequisites and no such file exists, Make decides it needs to be built and executes the associated command.

๐Ÿ”— Simple Example with Dependency

Create a Makefile that compiles a C program.

hello: hello.c
    cc hello.c -o hello

hello.c:

#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

Output:

# First run
% make
cc hello.c -o hello
# Second run
% make
make: `hello' is up to date.

How It Works:

Note: Make uses file timestamps to determine if a target needs to be rebuilt. If any dependency is newer than the target, the commands will be executed to update the target.

๐Ÿงฑ Multiple Targets and Dependencies

Create a Makefile that compiles multiple C programs.

hello: hello.o
    cc hello.o -o hello

hello.o: hello.c
    cc -c hello.c -o hello.o

hello.c:
    echo '#include <stdio.h>\n\nint main() {\n    printf("Hello, World!\\n");\n    return 0;}' > hello.c

Rule: Rebuild the target if any prerequisite is missing or older than it. Rebuilding a prerequisite triggers rebuilds up the chain.

๐ŸŽฏ Rules & Targets

๐Ÿงน Target Clean

clean target is commonly used to remove outputs of other targets.

.PHONY: clean
clean:
    rm -f *.o hello
hello: hello.c
    cc hello.c -o hello
# To prevent conflicts with a file named 'clean'
.PHONY: clean
clean:
    rm -f hello hello.o hello.c

๐Ÿงพ Types of Prerequisites

Syntax:

target: normal-prerequisites | order-only-prerequisites
    commands

๐Ÿ“ฆ Target all

all: target1 target2

target1:
    echo "Building target 1"
target2:
    echo "Building target 2"

๐ŸŽฏ Multiple Targets

target1 target2: 
    echo "$@"
# Equivalent to:
# target1:
#     echo "target1"
# target2:
#     echo "target2"

โธ๏ธ Double Colon Rules

target::
    echo "First rule for target"
target::
    echo "Second rule for target"

๐Ÿงฎ Variables

Example:

files = file1 file2

file1_var = file1_var_result
file2_var = file2_var_result

some_file: $(files)
    echo "Variables" $(files)

file1:
    echo "This is file 1" ${file1_var}
file2:
    echo "This is file 2" ${file2_var} > file2

Output:

% make
echo "This is file 1" file1_var_result > file1
echo "This is file 2" file2_var_result > file2
echo "Variables" file1 file2
Variables file1 file2

โœ๏ธ Assignment Operators

# = Assignment example
var1 = "= Assignment: Hello $(var2)"
var2 = " - World"
# =: Assignment expands var2 when var1 is used
var2 := ":= Assignment: Hello $(var2) : $(var3)"
var3 := " -- World"
# =?: Assignment expands var2 when var1 is defined
var4 = "?: Assignment: Hello "
var5 ?= "$(var4) -- World" # this will be set
var4 ?= "$(var4) -- Universe" # this will not set
#=+ Assignment example
var6 = "+= Assignment: Hello"
var6 += " -- World"


all:
    echo "[var1] : $(var1)"
    echo "[var2] : $(var2)"
    echo "[var3] : $(var3)"
    echo "[var4] : $(var4)"
    echo "[var5] : $(var5)"
    echo "[var6] : $(var6)"

Output:

[var1] : = Assignment: Hello := Assignment: Hello  - World : 
[var2] : := Assignment: Hello  - World : 
[var3] :  -- World
[var4] : ?: Assignment: Hello 
[var5] : ?: Assignment: Hello  -- World 
[var6] : += Assignment: Hello  -- World

๐Ÿค– Automatic Variables

๐ŸŽฏ Target-specific Variables

target1: VAR := "Value for target1"

target1:
    echo "In target1: VAR = $(VAR)"

target2: 
    echo "In all target: VAR = $(VAR)"

๐Ÿงฉ Pattern-specific Variables

%.tgt: VAR := "Value for $@"

s.tgt:
    echo "In $@: VAR = $(VAR)"
a.b:
    echo "In $@: VAR = $(VAR)"

๐Ÿšซ Override Directive

VAR1 := "default"
override VAR1 := "forced"
VAR2 := "normal"

.PHONY: vars
vars:
    echo "VAR1 = $(VAR1)"
    echo "VAR2 = $(VAR2)" 
% make -s VAR1=var1_override_value VAR2=val2_override_value
VAR1 = forced
VAR2 = val2_override_value

๐Ÿšฉ MAKEFLAGS Variable

var = 10
all:
    echo "[$(var)] : MAKEFLAGS : $(MAKEFLAGS)"

Output:

make -s -i
[10] : MAKEFLAGS : si

๐ŸŒŸ Patterns & Wildcards

๐ŸŒ  Wildcard - *

files := $(wildcard *.c)

tgt: $(files)
    echo "C source files: $^"
    ls -lrt $?

Other scenarios:

incorrect_assignment := *.c # treated literally as the string "*.c"
correct_assignment := $(wildcard *.c) # expands to matching .c files

all: first second third

# Fails, string *.c is used as literal dependency - not available
first: $(incorrect_assignment)

# stays as *.c literal, shell expands it at runtime
second: *.c

third: $(correct_assignment)

๐Ÿช„ Wildcard - %

๐Ÿงฑ Static Pattern Rules

Syntax:

targets...: target-pattern: prereq-patterns...
    commands    

Example:

objects = file1.o file2.o file3.o

all: $(objects)
    cc $^ -o all

$(objects): %.o: %.c
    cc -c $< -o $@

all.c:
    echo "int main() { return 0; }" > all.c

# Note: all.c does not use this rule because Make prioritizes more specific matches when there is more than one match.
%.c:
    touch $@

clean:
    rm -f *.c *.o all

With Filter:

obj_files = foo.result bar.o lose.o
src_files = foo.raw bar.c lose.c

all: $(obj_files)
# Note: PHONY is important here. Without it, implicit rules will try to build the executable "all", since the prereqs are ".o" files.
.PHONY: all 

# Ex 1: .o files depend on .c files. Though we don't actually make the .o file.
$(filter %.o,$(obj_files)): %.o: %.c
    echo "target: $@ prereq: $<"

# Ex 2: .result files depend on .raw files. Though we don't actually make the .result file.
$(filter %.result,$(obj_files)): %.result: %.raw
    echo "target: $@ prereq: $<"

%.c %.raw:
    touch $@

clean:
    rm -f $(src_files)

๐Ÿ“ Pattern Rules

%.c:
   touch $@

program: first.c second.c
    cc $^ -o program

โš™๏ธ Commands & Execution

๐Ÿ”Š Command Echoing/Silencing

target:
    @echo "This command will not be echoed"
    echo "This command will be echoed"

๐Ÿ’ต Double Dollar Sign ($$)

Example: print the current working directory using pwd command:

target:
    echo "Current directory: $$(pwd)"

โ–ถ๏ธ Command Execution

target1:
    cd ..
    echo "Current directory: $$(pwd)"

target2:
    cd ..; echo "Current directory: $$(pwd)"

๐Ÿš Default Shell

SHELL := /bin/bash

target:
    echo "Using shell: $$BASH_VERSION"

๐Ÿšจ Error Handling

all: target1 target2 target4

target1: target3
    echo "**target1**:Building target1 on when make -i is used"

target3:
    echo "**target3**: Building target3 by default"
    false
    echo "**target3**: This command will only run if 'make -i' is used."

target2:
    echo "**target2**: Building target2 on when make -i or -k is used"
    false
    echo "**target2**: This command will run only if 'make -i' is used."

target4:
    -false
    echo "**target4**: This command will run because the error is ignored. only if  make -i or -k is used."

Outputs:

**target3**: Building target3 by default
make: *** [target3] Error 1
**target3**: Building target3 by default
make: *** [target3] Error 1
**target2**: Building target2 on when make -i or -k is used
make: *** [target2] Error 1
**target4**: This command will run because the error is ignored. only if  make -i or -k is used.
make: Target `all' not remade because of errors.
**target3**: Building target3 by default
**target3**: This command will only run if 'make -i' is used.
**target1**:Building target1 on when make -i is used
**target2**: This command will run only if 'make -i' is used.
**target4**: This command will run because the error is ignored. only if  make -i or -k is used.

๐Ÿ›‘ Killing a Make Process

๐Ÿงฐ make arguments

๐Ÿš€ Advanced Features

๐Ÿ” Recursive Make

contents := "subdir: \n\techo \"Running for Recursive Make\""

all:
    echo "$(contents)"
    echo "building sub-makefile in subdir..."
    mkdir -p subdir
    echo "$(contents)" > subdir/Makefile
    $(MAKE) -C subdir

๐Ÿ“ค export Directive

VAR1 := "Hello"
VAR2 := "World"
export VAR1
all:
    echo "Exported shell variable: $$VAR1"
    echo "Non exported make variable: ${VAR2}"
.EXPORT_ALL_VARIABLES:
VAR1 := "Hello"
VAR2 := "World"
all:
    echo "Exported shell variable: $$VAR1"
    echo "Exported shell variable: $$VAR2"

๐Ÿงพ Define Directive

define cann_script
    echo "Starting cann process..."
    sleep 1
    echo "Cann process completed"
endef

cann1: cann2
    $(cann_script)
    echo "Cann1 target finished."

cann2: 
    $(cann_script)
    echo "Cann2 target finished."

๐Ÿ”€ Conditional Directives

VAR := value

eqtest:
ifeq ($(VAR), value)
    echo "VAR is equal to value : $(VAR)"
else
    echo "VAR is not equal to value : $(VAR)"
endif

neqtest:
ifneq ($(VAR), other_value)
    echo "VAR is not equal to other_value : $(VAR)"
else
    echo "VAR is equal to other_value : $(VAR)"
endif

ifdeftest:
ifdef VAR
    echo "VAR is defined : $(VAR)"
else
    echo "VAR is not defined"
endif

ifndeftest:
ifndef VAR1
    echo "VAR1 is not defined"
else
    echo "VAR1 is defined : $(VAR1)"
endif

Output:

VAR is equal to value : value
VAR is not equal to other_value : value
VAR is defined : value
VAR1 is not defined

๐Ÿ› ๏ธ Functions

๐Ÿงฑ Function Syntax

$(function-name arguments) or ${function-name arguments}

๐Ÿ“ Text Functions

FunctionDescriptionExample
subst from,to,textReplace all occurrences of from with to in text.$(subst .c,.o,src.c src2.c) โ†’ src.o src2.o
patsubst pattern,replacement,textReplace words in text matching pattern with replacement.$(patsubst %.c,%.o,src.c tgt.p) โ†’ src.o tgt.p
strip textRemove leading and trailing whitespace from text.$(strip Hello World ) โ†’ Hello World
findstring find,textSearch for find in text. Returns find if found, else empty.$(findstring World,Hello World!) โ†’ World
filter pattern...,textReturn words in text that match any of the patterns.$(filter %.c %.h,src.c tgt.p src2.h) โ†’ src.c src2.h
filter-out pattern...,textReturn words in text that do not match any of the patterns.$(filter-out %.c %.h,src.c tgt.p) โ†’ tgt.p
sort listSort words in list lexicographically and remove duplicates.$(sort banana apple orange apple) โ†’ apple banana orange
word n,textReturn the nth word from text (1-based).$(word 3,one two three four) โ†’ three
wordlist s,e,textReturn words from s to e (inclusive) from text.$(wordlist 2,4,one two three four) โ†’ two three four
words textReturn the number of words in text.$(words one two three four) โ†’ 4
firstword textReturn the first word from text.$(firstword one two three) โ†’ one
lastword textReturn the last word from text.$(lastword one two three) โ†’ three

๐Ÿ“ File Functions

FunctionDescriptionExample
dir names...Return the directory part of each file name.$(dir src/main.c inc/header.h) โ†’ src/ inc/
notdir names...Return the file name part (without directory).$(notdir src/main.c inc/header.h) โ†’ main.c header.h
suffix names...Return the suffix (extension) of each file name.$(suffix src/main.c README) โ†’ .c
basename names...Return the base name (without suffix).$(basename src/main.c inc/h.h) โ†’ src/main inc/h
addsuffix suffix,names...Add suffix to each file name.$(addsuffix .o,main util) โ†’ main.o util.o
addprefix prefix,names...Add prefix to each file name.$(addprefix src/,main.c util.c) โ†’ src/main.c src/util.c
join list1,list2Join corresponding elements from two lists.$(join a b,c d) โ†’ ac bd
wildcard pattern...Return list of existing files matching patterns.$(wildcard src/*.c) โ†’ src/main.c src/util.c
realpath names...Return absolute paths (resolves symlinks).$(realpath ./src/main.c) โ†’ /path/to/src/main.c
abspath names...Return absolute paths (does not resolve symlinks).$(abspath ./src/main.c) โ†’ /path/to/src/main.c

โ“ Conditional Functions

FunctionDescriptionExample
if condition,then,elseIf condition is non-empty, return then; else else.$(if $(VAR),Set,Not Set)
or cond1,cond2,...Return first non-empty condition.$(or $(V1),$(V2),default)
and cond1,cond2,...Return first empty condition, or last if all non-empty.$(and $(V1),$(V2))
intcmp lhs,rhs[,lt,eq,gt]Compare integers lhs and rhs. Returns lt, eq, or gt arg.$(intcmp 5,10,lt,eq,gt) โ†’ lt

๐Ÿงช Let Function

Syntax: $(let var [var ...], list, text)

Example:

result = $(let x y z, 10 20 30, $(shell echo "$(x), $(y), $(z) => SUM: $$(($(x)+$(y)+$(z))))"))
all:
    echo $(result)

Output:

% make             
echo  10, 20, 30 => SUM: 60

๐Ÿ”„ Foreach Function

Syntax: $(foreach var, list, text)

Example:

items = apple banana cherry
all:
    echo $(foreach item, $(items), Item: $(item))

๐Ÿ“„ File Function

Example:

long_text = This is a very long text that needs to be written to a file for use in a command line argument.
all:
    $(file >long_arg.txt, $(long_text))
    cat long_arg.txt

Output:

% make all
cat long_arg.txt
 This is a very long text that needs to be written to a file for use in a command line argument.

๐Ÿ“ž Call Function

Syntax: $(call function-name, arg1, arg2, ...)

Example:

define greet
Hello, $(1)
endef
all:
    echo $(call greet, World)

Output:

% make all
Hello, World

๐Ÿ’Ž Value Function

Syntax: $(value var)

Example:

VAR1 := $HOME

all:
    echo "Expanded VAR1: $(VAR1)"
    echo "Literal VAR1: $(value VAR1)"

Output:

% make -s    
Expanded VAR1: OME
Literal VAR1: /Users/turingx

๐Ÿ“š References


Share this post:

Next Post
๐Ÿงฉ From Bytes to Characters: What Really Lies Inside a File