CBuild Basics with Make

Build Basics with Make

As explained in Multi-File Projects, compiling a multi-file C project by hand means remembering which `.c` files changed, recompiling only those into `.o` files, and relinking everything — every single time. That bookkeeping doesn't scale. **Make** is the classic Unix build tool that automates exactly this.
Why Manual Compilation Doesn't Scale
  • You have to remember the exact compiler flags every single time you build.

  • You have to manually figure out which files changed and need recompiling.

  • Typing long multi-file gcc commands by hand is error-prone and tedious.

  • Every teammate needs to know (and type) the exact same commands, or builds become inconsistent.

Make solves all of this by letting you write the build steps down once, in a file called a Makefile, and then simply run make.

The Core Concept: Targets, Dependencies, Recipes

A Makefile is a collection of rules. Each rule has three parts:

  • Target — the file you want to produce (e.g. an executable or an object file).

  • Dependencies — the files the target depends on. If any of them is newer than the target, the target is considered out of date.

  • Recipe — the shell command(s) that actually produce the target from its dependencies.

Text
# target: dependencies
#     recipe (must start with a TAB, not spaces)
program: main.o stack.o
	gcc main.o stack.o -o program

When you run make, it checks: does program exist, and is it newer than both main.o and stack.o? If not (or if program doesn't exist yet), it runs the recipe to rebuild it. If program is already up to date, make does nothing and reports so — no wasted recompilation.

Running make

Bash
# Build the default target (the first one in the Makefile)
make

# Build a specific named target
make program

# A common convention: a "clean" target that removes build artifacts
make clean

By convention, most Makefiles include a clean target that deletes generated files (.o files, the final executable), so you can start a completely fresh build:

Text
clean:
	rm -f *.o program
Make Rebuilds Only What's Necessary

Make compares file modification timestamps. If you edit stack.c, Make notices stack.o is now older than stack.c, recompiles just that file, and then relinks program — all automatically, without you having to remember what changed.

Basic Concepts at a Glance

Term

Meaning

Target

The file a rule produces (e.g. program, main.o)

Dependency (prerequisite)

A file the target relies on; if it changes, the target is rebuilt

Recipe

The shell command(s) that build the target from its dependencies

Phony target

A target name that doesn't correspond to a real file, e.g. clean

Make's Quirky Syntax: Tabs, Not Spaces
Warning
Recipe lines in a Makefile **must** be indented with a literal tab character, not spaces. Most text editors default to inserting spaces for indentation, which will produce a cryptic error like `missing separator` when you run `make`. If you hit that error, the first thing to check is whether your editor silently converted your leading tab into spaces.
Tip
Many editors and IDEs can be configured to preserve tabs specifically for Makefiles (often based on the filename `Makefile` having no extension). It's worth setting this up once so you never have to debug a "missing separator" error caused by stray spaces again.
Note
This page covers Make's basic mental model. See Writing Makefiles for a complete, practical Makefile — including variables, pattern rules, and `.PHONY` targets — for a real multi-file project.