【汎用的に使える】便利なmakefileのサンプル

普段はVisual StudioなどのIDEを使ってるんですが、場合によってはgcc+makefileを使うこともあります。

特に組み込み向けプログラミングとか。

そんなとき、『あれっ!?Makefileってどう書くんだっけ???』となることがあるので、汎用的に使えるMakefileを作ってみました。

Makefileで実現したいこと

まず、Makefileで実現したいことをまとめておきます。

  • 実行ファイル名はフォルダ名からとってきてくれる
  • ソースディレクトリ下のファイルを自動的に列挙してくれる
  • ファイルの依存関係を考慮しつつ、必要最小限のソースをコンパイルしてくれる

IDEなら全部自動でやってくれることですね。 ここまでやっておけば、どんなプロジェクトでもこのMakefileをそのまま使えるようになりそうです。

Makefileのサンプル

ということでMakefileのサンプルです。

CC      = gcc
CFLAGS  = -g -MMD -MP
LDFLAGS = 
LIBS    = 
INCLUDE = -I ./include

SRC_ROOT = ./src
OBJ_ROOT = ./obj
TARGET_DIR  = ./bin

TARGET_NAME = $(notdir $(shell pwd)).exe

SRC_DIRS = $(shell find $(SRC_ROOT) -type d)
OBJ_DIRS = $(addprefix $(OBJROOT)/, $(SRC_DIRS))

SRCS = $(foreach dir, $(SRC_DIRS), $(wildcard $(dir)/*.c))
OBJS = $(addprefix $(OBJ_ROOT)/, $(SRCS:.c=.o)) 
DEPENDS = $(patsubst %.o,%.d, $(OBJS))

TARGET = $(TARGET_DIR)/$(TARGET_NAME)

all:$(TARGET)

$(TARGET):$(OBJS)
    @mkdir -p $(dir $@)
    $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS)

$(OBJ_ROOT)/%.o: %.c
    @mkdir -p $(dir $@)
    $(CC) $(CFLAGS) -c $< -o $@

clean:
    $(RM) -rf $(OBJ_ROOT) $(TARGET_DIR)

.PHONY: all clean

ifneq ($(MAKECMDGOALS),clean)
-include $(DEPENDS)
endif

解説

では解説します。

実行ファイル名は以下のコードで自動的に設定します。

TARGET_NAME = $(notdir $(shell pwd)).exe

makeを実行すると、srcディレクトリ下の.cをサブディレクトリも含めて全てコンパイル対象として列挙します。他の拡張子(.cppとか.asmとか)も対象に加えたい場合はSRCSに追加してください。

また、.exeはobjディレクトリに、.objはobj/src下に生成します。

ファイルの依存関係は、gccの -MMD -MPオプションで生成します。

CFLAGS = -g -MMD -MP

このオプションは、コンパイルと同時にファイルの依存関係を.dファイルとして生成してくれるとても便利なオプションです。.dファイルは.objと同じディレクトリに生成されます。

-MMD -MPオプションで生成した.dファイルは、

ifneq ($(MAKECMDGOALS),clean) 
-include $(DEPENDS)
endif

で読み込みます。

ここのポイントは、MAKECMDGOALSと-(マイナス)です。

MAKECMDGOALSにはコマンドラインで与えて指定されたゴールのリストが入ります。例えば、make cleanを実行した場合は、MAKECMDGOALSにcleanが格納されます。

つまり、上記のコードはmake clean時には.dファイルを読み込まないという意味です。もしifneq~endifがなかったらどうなるかというと、clean実行前に.dファイルを作りに行ってしまいます。無駄ですね。

次に-(マイナス)ですが、これはエラーが発生してもmakeを中断しないという意味です。一番最初のビルド時など、.dファイルが無いのでincludeできずにエラーが発生してしまいます。これでmakeを中断されたら一生ビルドできませんからね。

改良ポイント

関連するライブラリが更新されたときにビルドが走るようにしたらもっと便利になりそうですね。