Makefileのincludeとモジュラー構造

includeディレクティブを使用して大規模なMakefileを変数、ルール、環境固有の設定用の再利用可能なモジュールに分割して整理します。

Advanced Patterns

詳細な説明

includeによるモジュラーMakefile

大規模プロジェクトはMakefileを複数のファイルに分割することで恩恵を受けます。includeディレクティブは他のMakefileの内容を読み込んで挿入します。

基本的なinclude

# Makefile
include config.mk
include rules.mk

all: $(TARGET)
# config.mk
CC     ?= gcc
CFLAGS ?= -Wall -O2
TARGET = myapp
SRCS   = $(wildcard src/*.c)
OBJS   = $(SRCS:.c=.o)
# rules.mk
%.o: %.c
	$(CC) $(CFLAGS) -c -o $@ $<

$(TARGET): $(OBJS)
	$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)

-includeによるサイレントインクルード

-include local.mk
-include $(DEPS)

-include(またはsinclude)バリアントはファイルが存在しなくてもエラーになりません。以下に不可欠です:

  • 最初のビルド時に存在しない依存関係ファイル.d
  • 開発者がオプションで作成するローカルオーバーライドlocal.mk

環境固有のオーバーライド

ENV ?= development

include config/$(ENV).mk
# config/development.mk
CFLAGS += -g -O0 -DDEBUG
LDFLAGS += -fsanitize=address
# config/production.mk
CFLAGS += -O3 -DNDEBUG -flto
LDFLAGS += -flto -s

環境の切り替え:make ENV=production

モノレポパターン

SERVICES = auth api gateway worker

define SERVICE_RULES
.PHONY: build-$(1) test-$(1)
build-$(1):
	$(MAKE) -C services/$(1) build
test-$(1):
	$(MAKE) -C services/$(1) test
endef

$(foreach svc,$(SERVICES),$(eval $(call SERVICE_RULES,$(svc))))

build-all: $(addprefix build-,$(SERVICES))
test-all: $(addprefix test-,$(SERVICES))

$(MAKE) -Cパターンは各サービスディレクトリのサブMakefileに委譲し、foreach/eval/callパターンは各サービスのビルドとテストターゲットを生成します。

重要なポイント

  • includeパスはMakefileの場所ではなく、作業ディレクトリに対して相対的
  • includeの前に定義された変数はインクルードされたファイルで利用可能
  • 循環インクルードは検出されず、無限ループを引き起こす
  • 現在のMakefileのパスを取得するにはMAKEFILE_LISTを使用する

ユースケース

Makefileを環境設定、共有ルール、サービス固有のターゲットに分割して保守性を向上させる大規模プロジェクトやモノレポのビルドシステムを整理する場合に使用します。

試してみる — Makefile Generator

フルツールを開く