Makefile · Part I · Module 1 · Introduction to Makefiles
How Make Works: Targets, Prerequisites and Recipes
Chapter 1.1 showed what Make is for. This chapter opens it up. A rule has exactly three parts, and when you name a target Make runs exactly four steps — the same four every time, whether the Makefile has two lines or two thousand. Once you can run those steps in your head, you can read a Makefile you have never seen and say what it will do before you run it.
Foundation15 min readMakefileGNU MakePart IRulesDependencies
Part I · Module 1 · Chapter 1.2 · How Make Works: Targets, Prerequisites and Recipes
1. What Make Has to Be Told
Take a small, ordinary job. You have a file of signal names that a tool emitted in no particular order, and you want a sorted copy of it. Later you want a report built from that sorted copy plus a header you wrote by hand.
Nothing about that is difficult, but notice how much of it is knowledge you are holding, not knowledge the machine has:
- What you want — a sorted file, or a report, or a finished simulation. A name for the thing you are asking for.
- What has to exist or be finished first — the report cannot be built before the sorted copy exists, and the sorted copy cannot be built before the tool's raw output exists.
- Which commands actually do the work —
sorthere, a compiler or a simulator elsewhere.
Make has no way to infer any of the three. It does not know what a sorted file is, which of your files are inputs, or which command produces what. So a Makefile is simply those three pieces of knowledge written down in a form Make can act on — one piece per part of a rule.
If none of that is familiar yet — what Make is, and why a repeated sequence of commands ends up behind one — the previous chapter covers it, and this one assumes it.
2. The Shape of a Rule
Every rule in every Makefile has the same shape:
target: prerequisites
recipeThree parts, and each one answers one of the three questions above.
The target is the name you ask for — the part before the colon. It is usually the name of a file the rule produces, and sometimes the name of an action with no file behind it. Which of the two it is changes Make's behaviour completely, which is why §4 and §5 treat them separately.
The prerequisites are what must be up to date before this target can be built — the part after the colon, separated by spaces. They are things, not commands: files, or the names of other targets. A rule may have none at all.
The recipe is the list of commands that build the target. Every recipe line must begin with a literal tab character, not spaces. Make hands each line to a shell to run, so a recipe line is an ordinary shell command and nothing more.
That is the whole syntax. The rest of this chapter is about what Make does with it.
3. The Four Steps
When you type make <name>, Make performs the same four steps every time.
- Find the rule whose target is that name. If there is no rule and no such file, Make stops and says so.
- Bring every prerequisite up to date first. Each prerequisite is treated as a target in its own right — Make applies these same four steps to it before returning. This is how a chain of any depth gets built in the right order without you stating an order anywhere.
- Decide whether the target needs building, by asking two questions about files:
- Does the target file exist?
- Is any prerequisite newer than the target?
- Run the recipe, or do nothing. If step 3 found a reason to build, Make runs the recipe lines in order. If it found none, Make runs nothing and says the target is up to date.
Step 2 is the part people do not expect, and it is worth saying twice: Make works from the bottom of the chain upwards. Asking for the last thing in a flow is what builds everything the flow needs.
Step 3 is where the two kinds of target part company, so take the simpler kind first.
4. A Target With No Prerequisites and No File
Here is the smallest rule that does something:
hello:
@echo "Hello from a Make recipe."Run it twice:
make hello
make helloHello from a Make recipe.
Hello from a Make recipe.Both runs executed the recipe. Walk the procedure and you can see why that is not a special case but the ordinary result:
- Step 1 finds the rule for
hello. - Step 2 has nothing to do — no prerequisites.
- Step 3 asks whether the file
helloexists. It does not, because the recipe only prints a message; nothing ever creates a file calledhello. A target that does not exist always needs building. - Step 4 therefore runs the recipe. Every time, for as long as no file named
helloexists.
This is what an action target is: a name for a job, where the answer to step 3 is permanently "yes, build it". clean, run and test are the familiar examples. Chapter 1.1 named the distinction between this and a file-producing target; what step 3 adds is the mechanism behind it. They are not rebuilt because Make detected a change — they run because there is no file for Make to compare anything against.
The @ at the start of the recipe line only suppresses Make's echo of the command itself; it has no effect on any of the four steps.
5. A Rule That Produces a File
Now the other kind, where step 3 has real work to do. signals.raw is the unsorted file; the rule produces a sorted copy.
signals.txt: signals.raw
@echo "building signals.txt from signals.raw"
@sort signals.raw > signals.txtRead it as the three parts: produce signals.txt, from signals.raw, by sorting. Create signals.raw with three names in it, then follow four runs.
First run — the target does not exist.
make signals.txtbuilding signals.txt from signals.rawStep 3 found no signals.txt at all, so step 4 built it. The file now contains clk, rst_n, wr_en.
Second run — nothing has changed.
make: 'signals.txt' is up to date.The target exists, and signals.raw is not newer than it, so step 3 found no reason to build and step 4 did nothing. No sort ran. Compare this with §4: the rule is the same shape, and the behaviour differs only because this target names a file that now exists.
Third run — after editing the prerequisite. Add a fourth name to signals.raw and run again:
building signals.txt from signals.rawEditing signals.raw updated its modification time, which made it newer than signals.txt. Step 3's second question answered yes, so the recipe ran and the sorted copy now has four names in it.
Fourth run — nothing has changed again. Up to date, and nothing runs. The cycle is stable.
That is the whole mechanism. One rule, and the only thing that varies between a build and a skip is the answer to two questions about files on disk.
6. More Than One Prerequisite
A target can be built from several things. List them after the colon, separated by spaces:
report.txt: header.txt body.txt
@echo "building report.txt from header.txt and body.txt"
@cat header.txt body.txt > report.txtStep 3's second question now reads "is any prerequisite newer than the target?", and any one of them is enough. Four runs, each touching a different thing:
| What you do | What Make does |
|---|---|
First run, no report.txt yet | builds it — the target does not exist |
| Run again, nothing touched | nothing — up to date |
Touch only body.txt | builds it — one prerequisite is newer |
Touch only header.txt | builds it — the other prerequisite is newer |
Delete report.txt, run again | builds it — back to the first case |
There is no partial rebuild and no attempt to work out which input changed. A rule is all-or-nothing: either Make runs the whole recipe or it runs none of it.
Reading a rule and reading the figure are two directions through the same fact. The colon separates a thing from the things it is made of; the arrows show the content moving the other way.
7. A Prerequisite Make Has to Build First
So far every prerequisite was a file that already existed. The interesting case is a prerequisite that is itself the target of another rule. Add one:
report.txt: header.txt body.txt
@echo "3. building report.txt"
@cat header.txt body.txt > report.txt
body.txt: raw.log
@echo "2. building body.txt from raw.log"
@grep slack raw.log > body.txtbody.txt appears twice: as a prerequisite of report.txt, and as the target of its own rule. Start with only header.txt and raw.log on disk — no body.txt, no report.txt — and ask for the end of the chain:
make report.txt2. building body.txt from raw.log
3. building report.txtYou asked for report.txt and Make built body.txt first. That is step 2 doing exactly what it says: before deciding anything about report.txt, Make took each prerequisite in turn and ran the whole four-step procedure on it. For body.txt that meant finding its rule, checking raw.log, finding no body.txt on disk, and building it. Only then did Make return to report.txt and compare timestamps.
The order of the two rules in the file had nothing to do with it. report.txt's rule is written first and ran second, because execution order comes from the prerequisites, not from the layout of the file.
Now edit raw.log and ask again:
2. building body.txt from raw.log
3. building report.txtThe change propagated two levels. raw.log became newer than body.txt, so body.txt was rebuilt; rebuilding it made it newer than report.txt, so report.txt was rebuilt in turn. Neither rule mentions raw.log and report.txt in the same place — the connection exists only because the chain does.
8. The Same Shape in an RTL Flow
Nothing changes when the commands are a simulator's. A minimal design-and-verification flow has source files, a compile step that turns them into something runnable, and a run step that executes it:
simv: filelist.f
vcs -full64 -sverilog -f filelist.f -o simv
run.log: simv
./simv -l run.logThis block is illustrative. vcs is a commercial tool that is not installed here and there is no filelist.f in front of you, so treat it as a picture of the structure rather than something to type. Part II builds the real version, starting with filelists in Chapter 5.2.
What matters is that the structure is identical to §7's, and you can already predict the behaviour:
- Ask for
run.log. Step 2 bringssimvup to date first;simv's own step 2 checksfilelist.f. - If
filelist.fis newer thansimv, the design is recompiled, and the newsimvis then newer thanrun.log, so the simulation re-runs. - If nothing has been touched since the last run, Make runs neither tool and says
run.logis up to date.
That last line is the point of writing the flow this way. An engineer who types make run.log after changing nothing waits for no compile at all.
And it is worth being exact about what Make contributed, because it is less than it looks. Make did not parse any SystemVerilog, does not know what a module or a testbench is, and has no idea that vcs is a compiler. Every piece of that knowledge is in the two recipe lines an engineer wrote. What Make supplied is the ordering and the two timestamp questions — which is why every file that should trigger a recompile has to be named as a prerequisite, as the callout above shows. Make cannot discover a dependency: one it was not told about does not exist as far as step 3 is concerned.
9. Common Mistakes
Spaces where the recipe's tab belongs. The most common first error, and the error message does not mention tabs:
Makefile:2: *** missing separator. Stop.Make is saying that line 2 was neither a rule nor a recipe. Check the indentation before reading anything else into it.
Writing a command where a prerequisite belongs. Everything after the colon is a list of things, so a command put there is read as the name of a file to build:
signals.txt: sort signals.raw
@sort signals.raw > signals.txtmake: *** No rule to make target 'sort', needed by 'signals.txt'. Stop.Make looked for something called sort to bring up to date, found neither a file nor a rule, and stopped. Commands belong on the tab-indented lines, and only there.
Expecting a target to run every time. It will, as long as it names no file — but the moment a target happens to match a real file, step 3 starts answering the other way:
make: 'clean' is up to date.The recipe did not run, nothing was removed, and Make exited successfully. Nothing is broken from Make's point of view: the target exists, it has no prerequisites, so nothing is newer than it and there is nothing to do. This is the specific accident that .PHONY exists to prevent, and Chapter 2.2 is where it is handled.
Forgetting that timestamps drive the decision. "I changed the input and Make did nothing" is almost always step 3 answering honestly about modification times — a restored file, a copy that kept its original timestamp, a checkout that did not touch what you expected.
Treating an action target as if it produced a file. The reverse mistake. If compile is an action target, nothing on disk records that the compile happened, so Make will re-run it on every invocation — and anything listing it as a prerequisite will rebuild every time too. Whether a target names a file or an action is a decision with consequences, not a formatting preference.
10. Check Your Understanding
Question 1. A rule reads signals.txt: signals.raw. signals.txt exists, and you edit signals.raw and save it. You run make signals.txt. Does the recipe run, and which of step 3's two questions decided it?
Question 2. You run make signals.txt a second time with nothing touched in between, and again after deleting signals.txt. Give both outcomes and the reason for each.
Question 3. In §7's Makefile, report.txt's rule is written above body.txt's rule, but body.txt is built first. Explain why, without referring to the order of the lines in the file.
Question 4. A target named test has no prerequisites and its recipe runs a simulation. A colleague reports that make test has stopped doing anything and just prints that test is up to date. What has appeared in the directory, and which step of the procedure changed its answer?
11. Remember
- A rule is three parts: the target you can ask for, the prerequisites that must be up to date first, and the tab-indented recipe that builds it.
- Prerequisites are things, not commands. Anything after the colon is a file or another target; commands only ever go on the recipe lines.
- Make runs four steps for every target: find the rule, bring the prerequisites up to date, ask whether the target exists and whether any prerequisite is newer, then run the recipe or report nothing to do.
- Prerequisites are built first, bottom-up. Asking for the end of a chain builds the whole chain, and the order of rules in the file is not the order of execution.
- Any one newer prerequisite is enough, and a rule is all-or-nothing: the whole recipe runs, or none of it.
- A target naming a file can be skipped; a target naming an action cannot. The same rule shape behaves differently depending on whether a file by that name exists.
- The decision is about timestamps, not contents — and a target that accidentally matches a real file will stop running.
- A dependency Make was not told about does not exist. Naming
filelist.fas a prerequisite is what makes a recompile possible; leaving it out is what makes a stale result possible.
You can now read any rule and predict what Make will do with it. The next chapter puts that to work from the other end: starting with an empty file, writing a Makefile a line at a time, and watching each run confirm what you expected.
Where this fits
Part of the Makefile curriculum.
