1. Overview
.PHONY is a declaration in a Makefile that tells make a given target is not an actual file but a name meant to be run. Imperative targets like clean, all, and install usually don't produce files, so without .PHONY they can collide with files of the same name or trigger unnecessary rule searches.
If a file named
cleanalready exists,makeconsiders it up to date and never runs thecleancommand.
.PHONY is what solves this problem. The key points to verify are:
- Why
.PHONYis needed and what problem it solves - How
makebehaves differently with and without.PHONY - Patterns for using
.PHONYin real projects
2. What is a phony target? (.PHONY)
In a Makefile, a target is a rule for building some file.
hello: hello.c
cc hello.c -o helloHere hello is the name of a file that actually gets created. make runs cc when that file is missing or when the source (hello.c) is newer. But things break down once you add a "command that doesn't produce a file."
clean:
rm *.oIf a clean file happens to exist in the directory, make decides "clean is already up to date" and never runs rm *.o. This is where .PHONY comes in.
.PHONY: clean
clean:
rm *.oWith this declaration, clean is treated as a command name (label) rather than a file, so it always runs.
NOTE
.PHONY is a declaration that tells make: "this target is not a real file — it's a command."
3. Testing .PHONY
Let's create two files for the test.
hello.c
#include <stdio.h>
int main(void) {
puts("Hello, .PHONY!");
return 0;
}Makefile
View the full test Makefile
CC ?= cc
CFLAGS ?= -O2 -Wall
ifeq ($(USE_PHONY),1)
.PHONY: all run clean
endif
all: hello
hello: hello.o
$(CC) $(CFLAGS) -o $@ $^
hello.o: hello.c
$(CC) $(CFLAGS) -c $< -o $@
run: hello
@./hello
clean:
@echo "[clean] removing artifacts..."
@rm -f hello hello.o
## 'clean'이라는 실제 파일을 만들어 충돌 테스트
touch-clean-file:
@echo "This is a file named 'clean'" > clean3.1. Basic behavior test
The commands below work as expected.
make clean
## → [clean] removing artifacts...
make all
## → cc -O2 -Wall -c hello.c -o hello.o
## → cc -O2 -Wall -o hello hello.o
make run
## → Hello, .PHONY!3.2. The collision without .PHONY
## 'clean' 파일 생성
make touch-clean-file
make clean
## → make: 'clean'은(는) 이미 업데이트되었습니다.make clean should delete hello and hello.o, but nothing gets removed.
3.3. Running with .PHONY enabled
make USE_PHONY=1 clean
## → [clean] removing artifacts...Once .PHONY is enabled, make clean always runs even when a clean file exists.
TIP
This test let me confirm firsthand that .PHONY tells make "this target is a command, not a real file."
4. When .PHONY really pays off
Imagine a parallel build environment that builds several directories at once.
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
doneHere the for loop runs sequentially in a single shell, so parallel builds (make -j) are impossible, and even if one directory errors out, the overall build keeps going. .PHONY lets us improve this.
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@The key move is splitting $(SUBDIRS) out as prerequisites of subdirs. Now make treats each directory as an independent target, making make -j parallel builds possible. Here .PHONY guarantees that even if directories named foo, bar, or baz exist, they are never judged "already up to date."
5. Wrapping up
.PHONY plays the following roles in a Makefile.
| Role | Description |
|---|---|
| Prevents filename collisions | The command always runs even when a file of the same name exists |
| Supports command-only targets | Lets you write command targets with no real file, like clean, install, test, run |
| Improves performance | Skips implicit rule search, reducing unnecessary checks |
TIP
For targets that don't produce a real file, it's worth declaring .PHONY out of habit. Beyond preventing collisions, it also cuts down on make's unnecessary searching.