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
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
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:
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
.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 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:
| File | Modified |
|---|---|
all | 10:00 |
release.tar | 10:01 |
app | 10: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
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.