Makefile の .PHONY を実際に書いて理解する

Oct 18, 2025

2 min
💬

なぜ Makefile はターゲットと同名のファイルがあるとコマンドを実行しないのでしょうか。.PHONY の役割を実際にテストしながら整理しました。

1. 概要

.PHONY は、Makefile で特定の target が実際のファイルではなく実行用の名前であることを make に知らせる宣言です。cleanallinstall のような命令型の target はファイルを生成しないことが多いため、.PHONY を付けないと同名のファイルと衝突したり、不要な探索が発生したりすることがあります。

clean という名前のファイルがすでに存在すると、make はそれを最新のファイルとみなし、clean コマンドを実行しません。

この問題を解決するのが .PHONY です。確認すべきポイントは次のとおりです。

  • .PHONY がなぜ必要なのか、どんな問題を解決するのか
  • .PHONY の有無による make の動作の違い
  • 実際のプロジェクトで .PHONY を活用するパターン

2. Phony target とは? (.PHONY)

Makefile において、target はあるファイルを作るためのルールです。

MAKEFILE
hello: hello.c
	cc hello.c -o hello

ここで hello は実際に生成されるファイル名です。make はこのファイルが存在しないか、source(hello.c)がより新しい場合に cc を実行します。ところが「ファイルを作らないコマンド」を入れると問題が起きます。

MAKEFILE
clean:
	rm *.o

ディレクトリに偶然 clean というファイルがあると、make は「すでに clean は最新」と判断し、rm *.o を実行しません。そこで使うのが .PHONY です。

MAKEFILE
.PHONY: clean
clean:
	rm *.o

こう宣言すると、clean はファイルではなくコマンド名(label)として認識され、常に実行されます。

NOTE

.PHONYmake に「この target は実際のファイルではなくコマンド用だ」と知らせる宣言です。

3. .PHONY のテスト

テストのために 2 つのファイルを作ります。

hello.c

C
#include <stdio.h>
 
int main(void) {
    puts("Hello, .PHONY!");
    return 0;
}

Makefile

テスト用の 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'" > clean

3.1. 基本動作のテスト

以下のコマンドは正常に動作します。

Bash
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 なしで衝突する状況

Bash
## 'clean' 파일 생성
make touch-clean-file
 
make clean
## → make: 'clean'은(는) 이미 업데이트되었습니다.

make cleanhellohello.o を削除するはずなのに、削除されません。

3.3. .PHONY を有効化して実行

Bash
make USE_PHONY=1 clean
## → [clean] removing artifacts...

.PHONY を有効化すると、clean ファイルがあっても make clean常に実行されます。

TIP

このテストで、.PHONYmake に「この target は実際のファイルではなくコマンド用だ」と知らせるものであることを直接確認できました。

4. .PHONY の使用が有用なケース

複数のディレクトリを同時にビルドする並列 build 環境を想定してみましょう。

MAKEFILE
SUBDIRS = foo bar baz
 
subdirs:
	for dir in $(SUBDIRS); do \
		$(MAKE) -C $$dir; \
	done

この場合、for ループが 1 つのシェルで順次実行されるため並列ビルド(make -j)ができず、一部のディレクトリでエラーが起きても全体のビルドが続行されてしまいます。.PHONY で改善できます。

MAKEFILE
SUBDIRS = foo bar baz
.PHONY: subdirs $(SUBDIRS)
 
subdirs: $(SUBDIRS)
 
$(SUBDIRS):
	$(MAKE) -C $@

ポイントは $(SUBDIRS)subdirs依存関係(prerequisite)として分離したことです。こうすると make が各ディレクトリを独立した target として認識し、make -j による並列ビルドが可能になります。ここで .PHONY は、foobarbaz という名前のディレクトリがあっても「すでに最新」と判断されないことを保証します。

5. まとめ

.PHONYMakefile で次の役割を果たします。

役割説明
ファイル名の衝突防止同名のファイルがあっても常にコマンドが実行される
コマンド専用ターゲットの支援cleaninstalltestrun など実際のファイルがないコマンド用 target を書ける
パフォーマンス向上暗黙のルール探索をスキップし、不要なチェックを減らす

TIP

実際のファイルを生成しない target には、.PHONY を習慣的に宣言するのがおすすめです。衝突防止だけでなく、make の不要な探索も減らせます。

6. 参考文献