Makefile Syntax โ Rules, Targets, and Recipes
Lesson 2: Makefile Syntax โ Rules, Targets, and Recipes
Every Makefile is built from rules. A rule says: this target depends on these prerequisites, and here's how to build it. That's the entire syntax, and it's compact enough to learn in one sitting.
The Anatomy of a Rule
target: prerequisites...
<TAB>recipe line 1
<TAB>recipe line 2
Three parts:
- Target โ the file (or named action) this rule produces.
- Prerequisites โ files that must exist and be up to date before the target is built.
- Recipe โ shell commands, each on its own line, indented with a TAB character.
*** missing separator. Stop. This trips up everyone at least once โ configure your editor to insert a literal tab in Makefiles.
Your First Makefile
Save this as Makefile:
hello.txt: hello.in
cp hello.in hello.txt
Run make and you'll see:
$ make
cp hello.in hello.txt
Make ran the recipe because hello.txt didn't exist yet. Run it again:
$ make
make: 'hello.txt' is up to date.
Because hello.txt is now newer than hello.in, there's nothing to do. This is the incremental-build behavior from Lesson 1, and you just witnessed it.
Multiple Rules and the Default Goal
Makefiles usually define many rules. The first target in the file is the default goal โ what plain make builds:
all: hello.txt report.md
hello.txt: hello.in
cp hello.in hello.txt
report.md: data.csv
python3 render_report.py data.csv report.md
Here all has no recipe of its own โ it just groups prerequisites, so make builds both hello.txt and report.md. You can still build one specifically: make hello.txt.
Phony Targets: Actions That Aren't Files
Some targets don't produce a file at all โ they're actions. clean is the classic example:
.PHONY: clean
clean:
rm -f hello.txt report.md
.PHONY tells Make: "this target doesn't correspond to a real file, so always run its recipe โ don't check timestamps." Without it, a stray file named clean would make make clean silently do nothing. Declare every action-style target as .PHONY.
Comments and Command-Line Basics
# starts a comment. And the make command itself has a few flags you'll use constantly:
| Command | What it does |
|---|---|
make | Build the default goal (first target). |
make target | Build a specific target (e.g. make clean). |
make -n | Dry run โ print commands without executing them. |
make -B | Force โ rebuild everything regardless of timestamps. |
make -f mymake | Use a file with a different name. |
make -C subdir | Change directory first, then run Make there. |
make -j4 | Run up to 4 recipes in parallel (Lesson 7). |
Rules With Multiple Targets
One rule can produce several files at once โ useful when a single command generates multiple outputs:
output.a output.b: input.txt
split_input input.txt
Make treats this as a shorthand for two rules sharing the same recipe. If either output is missing, both are rebuilt.
clean). A rule with prerequisites and no recipe is just "make sure these exist" (all). The language is small; the power comes from composing rules into graphs.
๐ง Knowledge Check
1. What must every recipe line in a Makefile start with?
2. When you run plain make, which target gets built by default?
3. What does declaring a target as .PHONY do?