Skip to content

Intermediate · The preprocessor, multi-file programs and make · topic 39 of 60 · 25 min

Building with make

Explanation

make reads a Makefile of rules: a target, its prerequisites and a recipe (commands, indented with a TAB). It rebuilds only targets whose prerequisites changed, which saves time in big projects. Variables like CC and CFLAGS keep settings in one place, pattern rules (%.o: %.c) cover every source file, and .PHONY marks targets like clean that are not files.

Why make?

Typing gcc commands for ten files is tedious and error-prone, and recompiling everything after editing one file wastes time. make compares file modification times: if tax.c is newer than tax.o, it recompiles tax.c and relinks, and leaves everything else alone.

A first Makefile

output
# Makefile
CC = gcc
CFLAGS = -std=c17 -Wall -Wextra -g

billing: main.o tax.o invoice.o
	$(CC) $(CFLAGS) main.o tax.o invoice.o -o billing

main.o: main.c tax.h invoice.h
	$(CC) $(CFLAGS) -c main.c

tax.o: tax.c tax.h
	$(CC) $(CFLAGS) -c tax.c

invoice.o: invoice.c invoice.h tax.h
	$(CC) $(CFLAGS) -c invoice.c

.PHONY: clean
clean:
	rm -f *.o billing
Each rule: target: prerequisites, then recipe lines that MUST start with a TAB character.
bash
make            # builds the first target (billing)
make clean      # runs the clean recipe
make -j4        # build up to 4 targets in parallel

How make decides

  1. To build billing, it first makes sure main.o, tax.o and invoice.o are up to date.
  2. tax.o depends on tax.c and tax.h: if either is newer than tax.o (or tax.o is missing), it runs the recipe.
  3. If any .o changed, billing is relinked.
  4. Edit only tax.c → only tax.o is rebuilt and billing relinked.
Careful: Recipe lines must begin with a real TAB character, not spaces. If your editor converts tabs to spaces, make fails with "Makefile:5: *** missing separator. Stop." Configure your editor to keep tabs in Makefiles.

Pattern rules and automatic variables

output
CC = gcc
CFLAGS = -std=c17 -Wall -Wextra -g
OBJS = main.o tax.o invoice.o

billing: $(OBJS)
	$(CC) $(CFLAGS) $^ -o $@

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

.PHONY: clean
clean:
	rm -f $(OBJS) billing
$@ = the target, $< = the first prerequisite, $^ = all prerequisites. One rule now builds every .o.

The pattern rule above does not know that main.o also depends on tax.h, so editing a header might not trigger a rebuild. Real projects add automatic dependency generation (gcc -MMD -MP) or list header dependencies explicitly.

Beyond make

Larger and cross-platform projects often use CMake, which generates Makefiles (or Ninja or Visual Studio files) from a CMakeLists.txt description. You will still meet plain Makefiles everywhere in the Linux and embedded world, so knowing them pays off.

Think of it like this: make is like a careful cook who checks the dates on each prepared ingredient before cooking: if the chopped onions are fresher than the masala, only the masala is made again, not the whole meal.
Tip: Put the warning flags in CFLAGS once, and every file gets them. Add a debug target and a release target with different CFLAGS (-g versus -O2 -DNDEBUG).

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Building with make · 1/6

make

In this lesson you will build projects with make.

Prefer a video by a teacher? Find free videos on “makefile tutorial c beginners targets variables pattern rules” on YouTube ↗

In real life: Firmware builds in an embedded team

A team building firmware for electric-vehicle chargers uses a Makefile with a cross-compiler (CC = arm-none-eabi-gcc), separate debug and release flags, and a flash target that uploads the binary to a test board. Engineers type make flash, and only changed files are recompiled.

output
CC = arm-none-eabi-gcc
CFLAGS = -std=c17 -Wall -Wextra -Os -mcpu=cortex-m4 -mthumb
OBJS = main.o adc.o charger.o uart.o

charger.elf: $(OBJS)
	$(CC) $(CFLAGS) $^ -T board.ld -o $@

%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

Practice (0/3 done)

Task 1 · Question

make prints "Makefile:4: *** missing separator. Stop." What is the most likely cause?

Task 2 · Question

In a recipe for the rule app: main.o util.o, what does $^ expand to?

Task 3 · Practice

Write a Makefile for your money/cart/main project from the previous topic with CC and CFLAGS variables, a pattern rule for .o files, a link rule using $^ and $@, and a .PHONY clean target. Run make, touch one .c file, run make again and note which commands run. Add -MMD -MP to CFLAGS and -include $(OBJS:.o=.d) to track header dependencies.

Quick quiz

Question 1

1.How does make decide whether to rebuild a target?

Question 2

2.What does .PHONY: clean mean?

Question 3

3.What does make -j4 do?

Question 4

4.Which tool generates Makefiles for cross-platform projects?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

How does make decide whether to rebuild a target?

Answer: It compares modification times of the target and its prerequisites

Newer prerequisites trigger a rebuild.

What does .PHONY: clean mean?

Answer: clean is not a real file, so its recipe always runs when asked

Prevents confusion with a file named clean.

What does make -j4 do?

Answer: Runs up to 4 jobs in parallel

Parallel builds.

Which tool generates Makefiles for cross-platform projects?

Answer: CMake

CMake generates build files.

What does PUBLIC mean in target_include_directories(lib PUBLIC include)?Related

Answer: lib uses the folder AND so does anything that links lib

PUBLIC requirements propagate to consumers; PRIVATE ones do not.

From Building real projects: CMake, warnings, debuggers and sanitizers

What does gcc -c file.c produce?Related

Answer: An object file (file.o) that is not yet linked

Compile only, no linking.

From Splitting a program into files

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “The preprocessor, multi-file programs and make” before its exam: Libraries: static and shared.
  2. Module 8 exam: Preprocessor, multi-file programs and make · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Bits, undefined behaviour and safety. 60 topics left in the course before the final exam.

← Previous: Splitting a program into files