Skip to content
VLSI Mentor

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 — sort here, 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:

Azvya Education Pvt. Ltd.VLSI Mentor
The shape of every rule
target: prerequisites
	recipe

Three 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.

  1. Find the rule whose target is that name. If there is no rule and no such file, Make stops and says so.
  2. 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.
  3. Decide whether the target needs building, by asking two questions about files:
    • Does the target file exist?
    • Is any prerequisite newer than the target?
  4. 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Makefile
hello:
	@echo "Hello from a Make recipe."

Run it twice:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
make hello
make hello
Azvya Education Pvt. Ltd.VLSI Mentor
Actual output
Hello 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 hello exists. It does not, because the recipe only prints a message; nothing ever creates a file called hello. A target that does not exist always needs building.
  • Step 4 therefore runs the recipe. Every time, for as long as no file named hello exists.

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.

Azvya Education Pvt. Ltd.VLSI Mentor
Makefile
signals.txt: signals.raw
	@echo "building signals.txt from signals.raw"
	@sort signals.raw > signals.txt

Read 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
make signals.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Actual output
building signals.txt from signals.raw

Step 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.

Azvya Education Pvt. Ltd.VLSI Mentor
Representative output — Make 3.x quotes the name differently
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:

Azvya Education Pvt. Ltd.VLSI Mentor
Actual output
building signals.txt from signals.raw

Editing 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Makefile
report.txt: header.txt body.txt
	@echo "building report.txt from header.txt and body.txt"
	@cat header.txt body.txt > report.txt

Step 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 doWhat Make does
First run, no report.txt yetbuilds it — the target does not exist
Run again, nothing touchednothing — up to date
Touch only body.txtbuilds it — one prerequisite is newer
Touch only header.txtbuilds it — the other prerequisite is newer
Delete report.txt, run againbuilds 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.

Three boxes. On the left, two input files stacked vertically: header.txt, written by hand, and body.txt, the tool's output. On the right, one output file, report.txt, produced by the recipe. An arrow runs from each input box to the output box. Both inputs feed the single output; neither input depends on the other.header.txtwritten by handreport.txtthe targetbody.txtthe tool's output12
Figure 1 — the direction that confuses people. A rule is written output-first, `report.txt: header.txt body.txt`, while the arrows show where the content actually comes from. Make follows these arrows backwards from the target you asked for to find what it depends on, then compares the timestamp of each input against the output.

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:

Azvya Education Pvt. Ltd.VLSI Mentor
Makefile
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.txt

body.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:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
make report.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Actual output
2. building body.txt from raw.log
3. building report.txt

You 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Actual output
2. building body.txt from raw.log
3. building report.txt

The 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Makefile — illustrative; the tool names are examples, not a runnable flow
simv: filelist.f
	vcs -full64 -sverilog -f filelist.f -o simv

run.log: simv
	./simv -l run.log

This 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 brings simv up to date first; simv's own step 2 checks filelist.f.
  • If filelist.f is newer than simv, the design is recompiled, and the new simv is then newer than run.log, so the simulation re-runs.
  • If nothing has been touched since the last run, Make runs neither tool and says run.log is 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Actual output when the recipe is indented with spaces
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:

Azvya Education Pvt. Ltd.VLSI Mentor
Wrong — sort is a command, not a prerequisite
signals.txt: sort signals.raw
	@sort signals.raw > signals.txt
Azvya Education Pvt. Ltd.VLSI Mentor
Actual output — representative wording
make: *** 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:

Azvya Education Pvt. Ltd.VLSI Mentor
Actual output after a file named clean appears in the directory
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.f as 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.