Makefile · Part I · Module 2 · Rules and Dependencies
Targets, Rules and Recipes
Module 1 showed you the shape of a rule and how Make decides whether to run one. This chapter goes underneath that shape. A target is a name, and Make resolves it in one of three ways; a recipe is a list of commands that Make never inspects. The gap between those two facts is where a specific and very common bug lives — a rule whose recipe succeeds, reports nothing wrong, and never finishes its job.
Foundation18 min readMakefileGNU MakePart IRulesTargets
Part I · Module 2 · Chapter 2.1 · Targets, Rules and Recipes
1. A Target Is a Name, Not a Command
Start with a question that sounds trivial and is not: when you type make report.txt, what is report.txt?
It is a name you are asking Make to resolve. It is not a command, and Make does not assume it is a file either. Make takes the name and looks for two separate things: a rule whose target is that name, and a file on disk with that name. What it finds decides what happens, and there are three possible answers.
Set up a directory with two ordinary files and one rule to see all three. Create ports.txt and clocks.txt:
clk
rst_n
wr_enclk 100MHz
clk_div 50MHzreport.txt: ports.txt clocks.txt
@echo "building report.txt"
@echo "ports:" > report.txt
@cat ports.txt >> report.txt
@echo "clocks:" >> report.txt
@cat clocks.txt >> report.txtBuild it once so report.txt exists, then ask Make for three different names:
make report.txt
make nothing
make ports.txt
make report.txtbuilding report.txt
make: *** No rule to make target 'nothing'. Stop.
make: Nothing to be done for 'ports.txt'.
make: 'report.txt' is up to date.Four commands, and the last three are Make's three answers to "what is this name?"
| What Make finds | What it says | What it means |
|---|---|---|
| no rule and no file | No rule to make target | the name is meaningless here |
| a file, but no rule | Nothing to be done for | the file exists and nothing can produce it, so there is nothing to do |
| a file and a rule | runs the recipe, or is up to date | the normal case: Make can decide |
The middle row is the one most people have never seen. ports.txt exists, and no rule in the Makefile says how to produce it, so Make shrugs: the thing you asked for is already there and it has no way to make it anyway. That is not an error — it is the honest answer.
Understanding this resolution is what lets you read an unfamiliar error. No rule to make target is almost never a broken rule; it is a name Make could not account for — a typo, a file that was supposed to be generated by a rule somebody deleted, or a prerequisite that does not exist.
2. What the Three Parts Declare
Chapter 1.2 introduced the shape. Here is what each part declares, stated precisely, because the precision is what the rest of this chapter needs.
target: prerequisite1 prerequisite2
command
commandThe target declares a name that this rule can produce. If that name corresponds to a file, the rule is promising to create or update that file. Make takes the promise at face value.
The prerequisites declare what must be up to date first. They are names too, resolved exactly the way §1 described — which is why a prerequisite can be a plain file, or another target with a rule of its own.
The recipe declares the commands to run. And here is the part worth stating bluntly: Make never reads the recipe. It does not parse it, does not look for filenames in it, and cannot tell whether the commands have anything at all to do with the target. The recipe is an opaque list of strings handed to a shell.
Those three sentences together produce the central fact of this chapter. The relationship between the target name and what the recipe actually does is asserted by you and never checked by Make.
3. A Rule With No Prerequisites
The simplest rule has a target and a recipe and nothing after the colon:
hello:
@echo "Hello from a Make recipe."make helloHello from a Make recipe.Nothing named hello is ever created, so the name never resolves to a file, so there is never anything up to date. The recipe runs every time. That is the whole mechanism behind a target like clean or run.
The absence of prerequisites says something specific: this rule depends on nothing. For an action that is honest. For a rule that produces a file it is usually a bug, and Chapter 1.3 §5 showed the stale output it causes.
4. One Prerequisite, and Many
A prerequisite list is just that — a list, separated by spaces:
summary.txt: report.txt
@wc -l < report.txt | tr -d ' ' > summary.txtreport.txt: ports.txt clocks.txt
@echo "building report.txt"
@echo "ports:" > report.txt
@cat ports.txt >> report.txt
@echo "clocks:" >> report.txt
@cat clocks.txt >> report.txtThe §1 Makefile already had the two-prerequisite form. Build it, touch one input, and the rule runs:
make report.txt # up to date
touch clocks.txt
make report.txtbuilding report.txtAny one prerequisite being newer is enough, and the recipe is all-or-nothing — Make runs the whole thing or none of it. There is no partial rebuild and no attempt to work out which input changed. Chapter 2.3 takes that decision apart properly.
What matters structurally is that the prerequisite list is a statement about requirements, not an ordering instruction and not documentation. Chapter 2.4 returns to that distinction, because people often write a prerequisite when what they wanted was "run this first".
5. The Target Name Is a Promise
Now the case that explains why §2 insisted Make never reads a recipe. Here is a rule that is wrong in a way no error message will tell you about:
report.txt:
@echo "pretending to build report.txt"
@echo "data" > summary.txtRead it carefully before running it. The target says report.txt. The recipe writes summary.txt. Nothing in the Makefile connects those two names, and Make has no opinion about it.
make report.txtpretending to build report.txtExit status zero. No warning. Now look at the directory and run it again:
ls
make report.txt
make report.txtMakefile summary.txt
pretending to build report.txt
pretending to build report.txtreport.txt was never created, and the rule runs every single time. Make did exactly what its four steps require: step 3 looked for a file called report.txt, did not find one, and concluded the target needed building. Step 4 ran the recipe. The recipe succeeded. Step 3 will reach the same conclusion on the next run, and the next, forever.
Fix the recipe so it writes the name it promised:
report.txt:
@echo "building report.txt"
@echo "data" > report.txtmake report.txt
make report.txtbuilding report.txt
make: 'report.txt' is up to date.One run builds, the second does nothing. The only change was making the file the recipe writes match the name the target declared.
This failure has a recognisable signature in a real flow: a step that never stops re-running. If a compile repeats on every invocation even though nothing changed, the first thing to check is whether the file the compiler actually produces is the file the target names — a different directory, a different extension, or an output option pointing somewhere else.
6. Several Rules in One Makefile
A Makefile is a set of rules, and Make selects among them by target name. Nothing else about their arrangement matters — not adjacency, not grouping, not order (except for the default goal, which is Chapter 2.4).
report.txt: ports.txt clocks.txt
@echo "building report.txt"
@echo "ports:" > report.txt
@cat ports.txt >> report.txt
@echo "clocks:" >> report.txt
@cat clocks.txt >> report.txt
summary.txt: report.txt
@echo "building summary.txt"
@wc -l < report.txt | tr -d ' ' > summary.txt
hello:
@echo "Hello from a Make recipe."Three rules, three independent names. Ask for any one of them and Make resolves only that name and whatever it needs:
make hello # runs one recipe; report.txt and summary.txt untouched
make summary.txt # builds report.txt first, because summary.txt needs itsummary.txt names report.txt as its prerequisite, so asking for the second builds the first. hello is connected to nothing, so nothing connects to it. A Makefile is a graph of names, and the rules are the edges you drew.
7. Files and Actions Are Structurally Different
By now the split should be visible in the examples themselves, so it is worth naming before Chapter 2.2 handles it properly.
| a rule whose target names a file | a rule whose target names an action | |
|---|---|---|
| after a successful run | the file exists on disk | nothing on disk records it |
| on the next run | Make can compare and skip | Make must run it again |
| prerequisites are | a real requirement | often none, or a list of things to do first |
| examples | report.txt, build/sim.img | hello, run, clean, test |
Both are ordinary rules — there is no separate syntax, and Make does not have two kinds of target internally. The difference is entirely whether a file by that name appears. That is also why an action target is fragile: if a file by that name does appear, the rule stops working, and that is exactly the problem Chapter 2.2 solves.
8. An RTL-Shaped Build
Now a structure closer to real work: three source files, one compiled artifact in a build directory, and one action that uses it.
The version below is portable and was executed — it stands in for a simulator by concatenating the sources into an artifact, so you can run every command yourself:
build/sim.img: src/alu.v src/regfile.v src/tb_top.v
@echo "compiling 3 sources into build/sim.img"
@mkdir -p build
@cat src/alu.v src/regfile.v src/tb_top.v > build/sim.img
run: build/sim.img
@echo "running the simulation image (stand-in for a simulator)"
@wc -l < build/sim.img | tr -d ' 'With three small .v files under src/:
make runcompiling 3 sources into build/sim.img
running the simulation image (stand-in for a simulator)
8Two rules ran because run needs build/sim.img and that file did not exist. Run it again:
running the simulation image (stand-in for a simulator)
8The compile was skipped and the run repeated — the two behaviours from §7, side by side in one command. Now edit src/alu.v and ask again:
compiling 3 sources into build/sim.img
running the simulation image (stand-in for a simulator)
9The edit made a prerequisite newer than the artifact, so the artifact was rebuilt, and the action that follows it ran against the new one.
It is worth walking that first run through Make's four steps once, because every later chapter assumes you can do this in your head:
| Step | What Make does | Result on the first run |
|---|---|---|
| 1 | resolve the name run | a rule exists; no file called run |
| 2 | bring run's prerequisites up to date | one prerequisite, build/sim.img — recurse into it |
| 2a | resolve build/sim.img | a rule exists; the file does not |
| 2b | its prerequisites are the three sources | all three exist and nothing can produce them, so they are up to date |
| 2c | decide for build/sim.img | the target is missing, so build it — the compile message prints |
| 3 | decide for run | no file named run exists, so it is never up to date |
| 4 | run run's recipe | the simulation message prints |
On the second run only steps 2c and 3 answer differently: build/sim.img now exists and nothing is newer, so its recipe is skipped, while run still names no file and so still executes.
Two details in that Makefile are worth pointing out, because both are deliberate:
The target is a path. build/sim.img is a perfectly ordinary target name that happens to contain a directory. Make compares the file at that path; it does not care that it is nested. The mkdir -p build in the recipe exists because Make does not create directories for you — it only checks and runs.
The sources are named twice. Once after the colon, where they make the rebuild work, and once in the command, where they do the work. Keeping those two lists in step by hand is a real maintenance cost; the mechanism that removes the duplication is an automatic variable, which is Chapter 3.3.
The simulator version of the same two rules looks like this. It is illustrative — vcs is a commercial tool that is not installed here, so unlike every other transcript in this chapter it was not run:
build/simv: src/alu.v src/regfile.v src/tb_top.v
mkdir -p build
vcs -full64 -sverilog src/alu.v src/regfile.v src/tb_top.v -o build/simv
run: build/simv
./build/simv -l run.logCompare it line for line with the portable version and the structure is identical. And note what the target name promises: -o build/simv must write exactly the path the target declared. Point the simulator's output option somewhere else — a different directory, or the default simv in the current directory — and you get §5's bug with a twenty-minute compile attached to it.
9. Common Mistakes
Writing a command as the target. The target is a name, so a rule like compile report.txt: declares two targets called compile and report.txt, not a command with an argument. Make will accept it and behave in a way you did not intend.
Assuming Make checks the recipe. It does not, ever. A mismatch between the target name and the file the recipe writes produces §5's silent re-run. A recipe that writes nothing at all behaves the same way.
Expecting a directory to be created. build/sim.img as a target does not make build/ exist. If the recipe does not create the directory, the redirect fails and the rule errors out.
Thinking a name must be a file. No rule to make target and Nothing to be done for are different answers to different situations (§1). The first means Make cannot account for the name at all; the second means the name is a file it cannot and need not produce.
Letting a generated file sit in the source tree without a rule. If report.txt was once generated by a rule that has since been deleted, make report.txt reports Nothing to be done and the stale file stays forever, looking authoritative.
10. Check Your Understanding
Question 1. A Makefile has a rule for coverage.rpt. You run make coverage.rpt twice; the first run prints a build message and the second prints the same build message again. The recipe exits zero both times. What is the most likely cause, and which single command confirms it?
Question 2. make src/alu.v prints Nothing to be done for 'src/alu.v'. Is that an error? What are the two facts about src/alu.v that produce exactly this message?
Question 3. A rule reads build/sim.img: src/alu.v and its recipe is a single cat src/alu.v > build/sim.img. The build directory does not exist. What happens, and whose fault is it — Make's or the recipe's?
Question 4. In §6's Makefile, make hello leaves report.txt and summary.txt untouched even though all three rules are in the same file. Why?
11. Exercise
Here is a Makefile that is meant to write a compile filelist into a build/ directory and then count its entries. It contains two separate mistakes from this chapter.
build/filelist.f: src/alu.v src/tb_top.v
@echo "writing the filelist"
@echo "src/alu.v" > filelist.f
@echo "src/tb_top.v" >> filelist.f
check: build/filelist.f
@echo "filelist has this many entries:"
@wc -l < build/filelist.f | tr -d ' 'Before running it, write down: what does make check print, what does it leave on disk, and what exit status does make return?
12. Remember
- A target is a name Make resolves, and it has three possible answers: no rule and no file (
No rule to make target), a file with no rule (Nothing to be done for), or a file with a rule (build it or report it up to date). - Make never reads a recipe. It cannot see which files the commands touch, so the link between a target's name and what the recipe does is asserted by the author and never verified.
- The target name is a promise. A recipe that succeeds without creating the declared file makes the rule re-run on every invocation, with no error — the signature is a step that never stops rebuilding.
- No prerequisites means "depends on nothing." Correct for an action, a bug for a generated file.
- Any one newer prerequisite triggers the whole recipe. A rule is all-or-nothing.
- Rules are selected by name, not by position or adjacency. A Makefile is a graph of names; the prerequisites are the edges.
- A target may be a path, and Make will not create the directory for you.
- File targets can be skipped; action targets cannot — the difference is only whether a file by that name appears on disk.
The next chapter takes that last point and makes it safe. An action target works only while no file shares its name, and sooner or later one does — a directory called test, a script called run, an artifact called clean. Chapter 2.2 shows the collision happening and the one-line declaration that prevents it.
Where this fits
Part of the Makefile curriculum.
