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 のテスト
テストのために 2 つのファイルを作ります。
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 ループが 1 つのシェルで順次実行されるため並列ビルド(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 の不要な探索も減らせます。