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)를 충분히 활용할 수 없고(각 하위 makefile 내부 target 은 병렬로 빌드되지만 디렉터리는 한 번에 하나씩만 처리됨), 일부 디렉터리에서 에러가 나도 전체 빌드가 계속된다. .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 의 불필요한 탐색도 줄여준다.