Skip to content

Why should all be phony?

A file named all does not normally stop Make from checking its prerequisites. So what does .PHONY change? Follow the timestamps, implicit rules, and a missing intermediate file.

On this page

Many makefiles start with something like this:

.PHONY: all
all: app

The usual explanation is that all is a name for a group of targets, and a file named all should not interfere with that group.

That is a good reason to declare it phony. But it leaves a question: if a file named all exists, does Make actually stop working on its prerequisites?

For ordinary prerequisites, it does not. Let's start there, then look at the cases where .PHONY: all changes what happens.

These examples use GNU Make. The behavior below was checked with GNU Make 4.3. Recipe lines in the makefiles begin with a tab.

What all means

The name all has no special meaning to Make. It is a convention for grouping the targets you want a plain make to work on.

In the opening example, all is the default goal because it is the first eligible target. .PHONY does not become the default: its name starts with a period and contains no slash. You could call the grouping target build, and Make would treat it the same way. GNU Make: Rules

A prerequisite can itself be a target with prerequisites. Make follows those relationships to work out what needs doing. In this context, updating a target means bringing it up to date. The recipe does whatever work that requires; Make does not necessarily change a file itself.

A newer all still leads to work

Consider a tiny report build. Copying the source keeps the example easy to follow; a real report might be generated by another program.

all: report.txt

report.txt: source.txt
	cp source.txt report.txt
An ordinary grouping target, without .PHONY

Suppose all three files exist. report.txt was modified at 10:00, source.txt at 10:01, and all at 10:02.

The file all is newer than both other files. Even so, running make prints:

cp source.txt report.txt

Make first works through the ordinary prerequisites of all. To handle report.txt, it checks source.txt. The report is older than its source, so Make runs the copy command. Only after handling the report does Make finish deciding what work all itself needs. GNU Make: How Make Works

The same applies if report.txt is missing: Make creates it. An existing all file does not suppress this ordinary prerequisite.

This is why “a file named all stops the build” is too broad an explanation for a grouping target with no recipe. We need to say which work gets skipped.

What declaring all phony changes

.PHONY: all tells Make that all names an action or group, rather than a file whose timestamp determines whether it is current. Make always treats that target as out of date, and skips implicit-rule lookup for it.

This does not make its prerequisites phony. A real report.txt still follows its own timestamp checks. GNU Make: Phony Targets

The effect is easy to see if all has a recipe of its own:

all: report.txt
	@printf 'Build finished\n'

report.txt: source.txt
	cp source.txt report.txt
A grouping target with a completion message

If the report is current and a newer file named all exists, Make skips the completion message. Add .PHONY: all, and the message appears each time you run make, while the current report stays untouched.

For an all target with no recipe, there is no completion message to skip. But implicit rules are still relevant.

An omitted recipe invites an implicit rule

Here is the smallest example:

all:
No explicit recipe for all

Suppose the directory also contains all.c, but no file named all. With its usual built-in rules enabled, GNU Make can find a rule for building an executable from a C file. A dry run with make -n can print:

cc     all.c   -o all

The exact compiler command depends on your configuration. The important part is that Make found a recipe even though we did not write one for all.

Omitting a recipe allows Make to search for an implicit one. Declaring all phony prevents that search. It also avoids the work of searching when no rule would match. GNU Make: Using Implicit Rules

An empty recipe answers a different question

We can explicitly give all nothing to do:

all: part ;

The semicolon introduces an empty recipe. Make no longer needs to search for an implicit recipe for all. But all is still an ordinary target: an existing file with that name still has a timestamp.

An empty recipe says what to execute. A phony declaration says how to treat the target. They are different instructions.

The GNU Make manual's section on empty recipes warns that using an empty recipe for a grouping target can mean its “prerequisites may not be remade properly if the target file actually does exist.”

That warning needs more context than the ordinary report example. A missing intermediate file gives us a concrete case where it happens.

The missing intermediate case

all: part ;

.INTERMEDIATE: part

part: source
	cp source part
An explicitly declared intermediate prerequisite

.INTERMEDIATE: part tells GNU Make to treat part as a temporary build result. This changes how it handles a missing part.

Imagine compiling a program through a temporary generated source file. Once the final program is current, that temporary file can be removed. Recreating it on every run just because it is missing would waste work. GNU Make can leave such a missing intermediate alone when the final target does not need rebuilding. It also normally removes an intermediate that it created during the run. GNU Make: Chains of Implicit Rules

Try the makefile above in a fresh directory. Create source and a newer file named all, leaving part missing:

printf 'input\n' > source
touch -t 202601011000 source
touch -t 202601011001 all
make

GNU Make 4.3 reports:

make: 'all' is up to date.

Make treats the existing all as a current final file. The missing intermediate is not enough to make it rebuild that file, so part stays missing.

Now add .PHONY: all to the makefile and run make again:

cp source part
rm part

The phony target cannot be current based on the unrelated all file. Make builds part, then removes it because it is intermediate. The final absence of part does not mean its recipe was skipped; the command output shows the work and the cleanup.

Remove .INTERMEDIATE: part instead, keeping all ordinary, and Make creates the missing part and leaves it in place. That line is essential to this particular example. GNU Make can also infer intermediate files while chaining implicit rules; the explicit declaration makes the example small.

This demonstrates behavior consistent with the manual's warning. The manual does not identify this as its specific intended example. It is also an edge case, not a reason to introduce .INTERMEDIATE into a simple grouping rule.

A release archive can miss a rebuilt program

There is a more practical timestamp trap in the other direction: a target that depends on all.

Someone adding a release archive might write this, intending “build everything before packaging”:

all: app ;

release.tar: all
	tar -cf release.tar app
The packaging portion of a larger makefile

The program app would also have its own build rule. For this example, suppose it has just been rebuilt and is already current. These files exist:

FileModified
all10:00
release.tar10:01
app10:02

Run make release.tar. Make checks app, then handles all. The empty recipe does not change the existing all file, so its timestamp stays at 10:00.

The archive's direct prerequisite is all. Since release.tar is newer than that file, Make skips the packaging command. The archive still contains the old program, even though the new app is sitting beside it.

If all were missing, this example would behave differently: its empty recipe never creates it, and it would keep causing the archive to rebuild. Adding .PHONY: all also makes the archive rebuild, whether or not the file exists. But it then rebuilds on every make release.tar, even when app has not changed.

For an archive that should track changes to the program, name that dependency directly:

.PHONY: all
all: app

release.tar: app
	tar -cf release.tar app
The archive depends on the file it packages

Now .PHONY: all describes the grouping target, while release.tar: app describes the archive's input. Plain make builds the program; make release.tar updates the archive when needed.

This is why the manual generally advises against using a phony target as a prerequisite of a real file. Doing so forces work whenever that file is considered. GNU Make: Phony Targets

There is an important difference between this example and the manual's empty recipe warning: the archive is a dependent of all. It is not a prerequisite of all. The skipped packaging does not demonstrate Make skipping all's prerequisites. Also, plain make would select all, so we must request release.tar to observe this problem.

What to put in your makefile

Declare a grouping target phony when its name does not represent an output file:

.PHONY: all
all: app report.txt

Its ordinary prerequisites still get their own checks, and current output files are left alone. all itself stays out of date, its own recipe runs if it has one, and Make does not look for an implicit recipe to create an all file.

Use actual input files as prerequisites of generated files. That gives Make the timestamp relationships it needs to skip work correctly.

If you are learning these pieces for the first time, the minimal makefile lesson introduces them one at a time. The phony target reference is a shorter reminder.