1. 개요
.PHONY는 Makefile에서 특정 target이 실제 파일이 아니라 실행용 이름임을 make에 알려주는 선언입니다. clean, all, install 같은 명령형 target은 파일을 생성하지 않는 경우가 많기 때문에, .PHONY를 붙이지 않으면 같은 이름의 파일과 충돌하거나 불필요한 탐색이 발생할 수 있습니다.
clean이라는 이름의 파일이 이미 존재하면,make는 그걸 최신 파일로 인식해clean명령을 실행하지 않습니다.
이 문제를 해결하는 것이 .PHONY 입니다. 핵심 확인 포인트는 다음입니다.
.PHONY가 왜 필요한지, 어떤 문제를 해결하는지.PHONY유무에 따른make의 동작 차이- 실제 프로젝트에서
.PHONY를 활용하는 패턴
2. Phony target 이란? (.PHONY)
Makefile에서 target은 어떤 파일을 만들기 위한 규칙입니다.
hello: hello.c
cc hello.c -o hello여기서 hello 는 실제로 만들어지는 파일 이름입니다. make 는 이 파일이 없거나 source(hello.c)가 더 최신이면 cc 를 실행합니다. 그런데 "파일을 만드는 게 아닌 명령"을 넣으면 문제가 생깁니다.
clean:
rm *.o디렉터리에 우연히 clean 파일이 있으면, make 는 "이미 clean 이 최신"이라 판단하고 rm *.o 를 수행하지 않습니다. 이때 쓰는 것이 .PHONY 입니다.
.PHONY: clean
clean:
rm *.o이렇게 선언하면 clean 은 파일이 아니라 명령 이름(label)으로 인식되어 항상 실행됩니다.
NOTE
.PHONY 는 make 에게 "이 target 은 실제 파일이 아니라 명령용"이라고 알려주는 선언입니다.
3. .PHONY 테스트
테스트를 위해 두 파일을 만듭니다.
hello.c
#include <stdio.h>
int main(void) {
puts("Hello, .PHONY!");
return 0;
}Makefile
테스트용 전체 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. 기본 동작 테스트
아래 명령들은 정상 동작합니다.
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. .PHONY 없이 충돌하는 상황
## 'clean' 파일 생성
make touch-clean-file
make clean
## → make: 'clean'은(는) 이미 업데이트되었습니다.make clean 이 hello, hello.o 를 지워야 하는데 삭제되지 않습니다.
3.3. .PHONY 활성화 후 실행
make USE_PHONY=1 clean
## → [clean] removing artifacts....PHONY 가 활성화되면 clean 파일이 있어도 make clean 이 항상 실행됩니다.
TIP
이 테스트로 .PHONY 가 make 에게 "이 target 은 실제 파일이 아닌 명령용"임을 알려준다는 점을 직접 확인했습니다.
4. .PHONY 사용이 유용한 경우
여러 디렉터리를 동시에 빌드하는 병렬 build 환경을 가정해봅시다.
SUBDIRS = foo bar baz
subdirs:
for dir in $(SUBDIRS); do \
$(MAKE) -C $$dir; \
done이 경우 for 루프가 한 셸에서 순차 실행되어 병렬 빌드(make -j)가 불가능하고, 일부 디렉터리에서 에러가 나도 전체 빌드가 계속됩니다. .PHONY 로 개선할 수 있습니다.
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
subdirs: $(SUBDIRS)
$(SUBDIRS):
$(MAKE) -C $@핵심은 $(SUBDIRS) 를 subdirs 의 의존성(prerequisite)으로 분리한 것입니다. 그러면 make 가 각 디렉터리를 독립 target 으로 인식해 make -j 병렬 빌드가 가능해집니다. 여기서 .PHONY 는 foo, bar, baz 라는 이름의 디렉터리가 있어도 "이미 최신"으로 판단하지 않도록 보장합니다.
5. 마무리
.PHONY 는 Makefile 에서 다음 역할을 합니다.
| 역할 | 설명 |
|---|---|
| 파일 이름 충돌 방지 | 같은 이름의 파일이 있어도 항상 명령이 실행됨 |
| 명령 전용 타깃 지원 | clean, install, test, run 등 실제 파일이 없는 명령용 target 작성 |
| 성능 향상 | 암시적 규칙 탐색을 건너뛰어 불필요한 검사 감소 |
TIP
실제 파일을 생성하지 않는 target 에는 .PHONY 를 습관적으로 선언하는 것이 좋습니다. 충돌 방지뿐 아니라 make 의 불필요한 탐색도 줄여줍니다.