Makefileとは
Makefileは、ソフトウェア開発プロジェクトにおいて、ソースコードのコンパイルやリンク、実行ファイルの生成、そして依存関係の管理を行うためのビルド自動化ツールです。Makefileは、特定のターゲットとその依存関係を構築する方法を記述するタスク(ルール)を定義し、実行するための便利な方法を提供します。Makefileは、make
ユーティリティに依存しており、Makefileを読み込み、適切なタスクを自動的に実行します。
Makefileの使い方
Makeのインストール
Makefileを使用するには、システムにmake
ユーティリティをインストールする必要があります。LinuxやmacOSなどのUnixベースのシステムには、通常、make
が事前にインストールされています。しかし、インストールされていない場合は、次のコマンドを使用できます。
Linux(Debian / Ubuntu)の場合:
$ sudo apt-get update
$ sudo apt-get install build-essential
macOSの場合(Homebrewを使用する場合):
$ brew install make
Windowsの場合、次の方法でmake
をインストールできます。
- WSL(Windows Subsystem for Linux)を使用: Microsoft StoreからLinuxディストリビューションをインストールし、Linuxのインストール手順に従います。
- MSYS2またはCygwinを使用: Windows向けのこれらのUnixライクな環境のいずれかをダウンロードしてインストールし、Linuxのインストール手順に従います。
Makefileの作成
Makefileを作成するには、プロジェクトのルートディレクトリに、拡張子のない新しいファイルMakefile
を作成するだけです。ファイルの作成と編集には、任意のテキストエディタを使用できます。
以下は、Cプログラムをコンパイルする単純なMakefileの例です。
CC = gcc
CFLAGS = -Wall -Wextra
hello: hello.c
$(CC) $(CFLAGS) -o hello hello.c
clean:
rm -f hello
この例では、2つの変数(CC
とCFLAGS
)を定義し、hello
ターゲットをビルドするためのルールで使用しています。clean
ルールは、生成されたhello
実行可能ファイルを削除するために使用されるフェイクターゲットです。
基本的なMakefileの構文とルール
Makefileは、ルール、変数の割り当て、コメントから構成されています。基本的な構文の概要は次のとおりです。
ルール
ルールは、target
、そのdependencies
、そしてrecipe
から構成されます。フォーマットは次のようになります。
target: dependencies
recipe
recipe
は、タブ文字でインデントされたシェルコマンドのシリーズです。
変数
Makefileの変数は、=
演算子を使用して割り当てられます。
VAR_NAME = value
変数をルール内で使用するには、その名前を括弧で囲んでドル記号で接頭辞を付けます。
- 例:
$(VAR_NAME)
コメント
Makefileのコメントは、#
文字で始まり、行末まで続きます。
# This is a comment
フェイク
フェイクターゲットは、実際のファイルを表さない特別なターゲットであり、関連するタスクをグループ化する方法として機能します。フェイクターゲットを定義するには、ルールの前に.PHONY
ディレクティブを追加します。
.PHONY: clean
clean:
rm -f hello
Makefileの実行
プロジェクト用のMakefileを作成したら、make
ユーティリティを使用して実行できます。デフォルトでは、make
は現在のディレクトリにあるMakefile
という名前のファイルを検索し、ファイルで最初に見つかったルールを実行します。ターゲットをビルドするには、その名前を引数として指定することもできます。
デフォルトのターゲットをビルドするには、次のようにmake
を実行します(通常、Makefileで最初に定義されたターゲットです)。
$ make
特定のターゲットをビルドするには、makeコマンドにその名前を引数として指定します。
$ make clean
この例では、このコマンドは、clean
ルールを実行し、hello
実行可能ファイルを削除します。
複数のターゲットを指定するには、名前をスペースで区切って指定します。
$ make target1 target2 target3
別の名前のMakefileを実行するか、別のディレクトリにあるMakefileを実行する場合は、-f
または--file
オプションを使用できます。
$ make -f path/to/MyMakefile
高度なMakefileのテクニック
変数の定義と使用
Makefileの変数を使用すると、値を保存して再利用することができ、Makefileを保守しやすく、読みやすくすることができます。変数は、次の演算子のいずれかを使用して割り当てることができます。
=
: 再帰的な変数割り当て:=
: 単純な変数割り当て(再帰的でない)?=
: 条件付き変数割り当て(変数がまだ設定されていない場合にのみ)+=
: 変数に追加
次に、変数の使用例を示します。
CC := gcc
CFLAGS := -Wall -Wextra
SOURCES := main.c util.c
OBJECTS := $(SOURCES:.c=.o)
main: $(OBJECTS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
条件文とループ
Makefileは、条件文やループをサポートしており、環境やターゲットプラットフォームに基づいてMakefileの動作をカスタマイズするのに役立ちます。
条件文は、ifeq
、ifneq
、ifdef
、およびifndef
ディレクティブを使用して記述されます。
ifeq ($(OS),Windows_NT)
EXE_EXT := .exe
else
EXE_EXT :=
endif
hello: hello.c
$(CC) $(CFLAGS) -o hello$(EXE_EXT) hello.c
ループは、foreach
およびeval
ディレクティブ、call
およびvalue
関数を使用して作成することができます。
TARGETS := target1 target2 target3
RULES := $(foreach target,$(TARGETS),$(eval $(call create_rule,$(target))))
define create_rule
$(1): $(1).c
$(CC) $(CFLAGS) -o $(1) $(1).c
endef
$(RULES)
パターンルールとワイルドカード
パターンルールを使用すると、ファイル拡張子やパターンに基づいて複数のターゲットに対する汎用ルールを定義できます。ワイルドカードを使用することで、Makefileを簡単にすることができます。
以下は、パターンルールとワイルドカードを使用した例です。
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
all: main
main: $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
依存関係の管理
Makefileは、ファイルが変更されたときに必要なタスクのみが実行されるように、ターゲット間の依存関係を自動的に管理することができます。
依存関係を管理するには、コンパイラによって生成された依存ファイルを含めるために、-include
ディレクティブを使用することができます。
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
DEPS := $(SRCS:.c=.d)
all: main
main: $(OBJS)
$(CC) $(CFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -MMD -c -o $@ $<
-include $(DEPS)
この例では、-MMD
フラグは、各ソースファイルに対して依存関係ファイルを生成するようにコンパイラに指示し、それらを-include
ディレクティブを使用してMakefileに含めています。