Traffine I/O

日本語

2022-05-14

Makefile

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の例です。

Makefile
CC = gcc
CFLAGS = -Wall -Wextra

hello: hello.c
	$(CC) $(CFLAGS) -o hello hello.c

clean:
	rm -f hello

この例では、2つの変数(CCCFLAGS)を定義し、helloターゲットをビルドするためのルールで使用しています。cleanルールは、生成されたhello実行可能ファイルを削除するために使用されるフェイクターゲットです。

基本的なMakefileの構文とルール

Makefileは、ルール、変数の割り当て、コメントから構成されています。基本的な構文の概要は次のとおりです。

ルール

ルールは、target、そのdependencies、そしてrecipeから構成されます。フォーマットは次のようになります。

Makefile
target: dependencies
	recipe

recipeは、タブ文字でインデントされたシェルコマンドのシリーズです。

変数

Makefileの変数は、=演算子を使用して割り当てられます。

Makefile
VAR_NAME = value

変数をルール内で使用するには、その名前を括弧で囲んでドル記号で接頭辞を付けます。

  • 例: $(VAR_NAME)

コメント

Makefileのコメントは、#文字で始まり、行末まで続きます。

Makefile
# This is a comment

フェイク

フェイクターゲットは、実際のファイルを表さない特別なターゲットであり、関連するタスクをグループ化する方法として機能します。フェイクターゲットを定義するには、ルールの前に.PHONYディレクティブを追加します。

Makefile
.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を保守しやすく、読みやすくすることができます。変数は、次の演算子のいずれかを使用して割り当てることができます。

  • =: 再帰的な変数割り当て
  • :=: 単純な変数割り当て(再帰的でない)
  • ?=: 条件付き変数割り当て(変数がまだ設定されていない場合にのみ)
  • +=: 変数に追加

次に、変数の使用例を示します。

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の動作をカスタマイズするのに役立ちます。

条件文は、ifeqifneqifdef、およびifndefディレクティブを使用して記述されます。

Makefile
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関数を使用して作成することができます。

Makefile
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を簡単にすることができます。

以下は、パターンルールとワイルドカードを使用した例です。

Makefile
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)

all: main

main: $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

依存関係の管理

Makefileは、ファイルが変更されたときに必要なタスクのみが実行されるように、ターゲット間の依存関係を自動的に管理することができます。

依存関係を管理するには、コンパイラによって生成された依存ファイルを含めるために、-includeディレクティブを使用することができます。

Makefile
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に含めています。

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!