thecodex.expert · The Codex Family of Knowledge
C

Makefiles

A recipe line indented with spaces instead of an actual tab character produces a cryptic "missing separator" error — the single most common frustration make causes newcomers, and it’s purely a whitespace issue.

C17 / C23 Recipes need a real TAB Last verified:
Canonical Definition

Each rule in a Makefile has the form target: dependencies, followed by one or more indented recipe lines (the shell commands to build that target). make compares the target file's modification timestamp against each dependency's; if any dependency is newer, or the target doesn't exist yet, make runs the recipe to rebuild it — otherwise it skips that target entirely, which is what makes incremental rebuilds fast on a large project. The recipe lines must be indented with an actual TAB character, not spaces; using spaces produces the notorious "missing separator" error.

A basic Makefile

Running just "make" builds the first target in the file by default (myapp here); make automatically checks main.c's timestamp against myapp's before deciding whether a rebuild is even necessary.

CMakefile
myapp: main.c
	gcc -Wall -o myapp main.c

# NOTE: the line above MUST be indented with a real TAB, not spaces —
# spaces here cause a "missing separator" error
Cterminal
$ make          // builds myapp, but only if main.c is newer than myapp (or myapp doesn't exist)
$ make          // running again immediately: "myapp is up to date" — nothing rebuilt

Multiple targets and dependencies

Changing only utils.c means make only recompiles utils.o and relinks myapp — main.o, whose source file didn't change, is left alone, since its dependency (main.c) isn't newer than it.

CMakefile
myapp: main.o utils.o
	gcc -o myapp main.o utils.o

main.o: main.c utils.h
	gcc -Wall -c main.c

utils.o: utils.c utils.h
	gcc -Wall -c utils.c

clean:
	rm -f myapp main.o utils.o

Variables: avoiding repetition

$(CC) and $(CFLAGS) let the compiler and its flags be defined once and reused across every rule — changing the compiler or a flag means editing exactly one line, not every rule in the file.

CMakefile
CC = gcc
CFLAGS = -Wall -Wextra -std=c17

myapp: main.o utils.o
	$(CC) -o myapp main.o utils.o

main.o: main.c
	$(CC) $(CFLAGS) -c main.c

Sources

1
Free Software Foundation. GNU Make Manual, gnu.org/software/make/manual/make.html.