落書き以上、技術メモ以下

気になったことをまとめておきます。

汎用的に使えそうなMakefileを書いてみた

Makefileを書いたのでここにメモしておきます。

参考にしたのはこちらのサイトです。

シンプルで応用の効くmakefileとその解説 - URIN HACK

[Bash] ファイルやディレクトリの存在をチェックする方法

Makeでヘッダファイルの依存関係に対応する - wagavulinの日記

GNU make 日本語訳(Coop編) - テキスト変形関数

作成したMakefile

CC      = g++
CFLAGS  = -g -MMD -MP
LDFLAGS = 
LIBS    = 
INCLUDE = -I ./include
SRC_DIR = ./src
OBJ_DIR = ./build
SOURCES = $(shell ls $(SRC_DIR)/*.cpp) 
OBJS    = $(subst $(SRC_DIR),$(OBJ_DIR), $(SOURCES:.cpp=.o))
TARGET  = target_name
DEPENDS = $(OBJS:.o=.d)

all: $(TARGET)

$(TARGET): $(OBJS) $(LIBS)
  $(CC) -o $@ $(OBJS) $(LDFLAGS)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp 
   @if [ ! -d $(OBJ_DIR) ]; \
      then echo "mkdir -p $(OBJ_DIR)"; mkdir -p $(OBJ_DIR); \
      fi
  $(CC) $(CFLAGS) $(INCLUDE) -o $@ -c $< 

clean:
  $(RM) $(OBJS) $(TARGET) $(DEPENDS)

-include $(DEPENDS)

.PHONY: all clean

Makefileの解説

コンパイラの宣言

CC      = g++

使用するコンパイラをCCに宣言します。 ここではC++のプログラムを考えているのでg++を宣言しています。

コンパイラオプションの宣言

CFLAGS  = -g -MMD -MP

使用するコンパイラオプションを宣言します。 ここで使用しているのは次の3つです

  • -g

デバッグ情報を生成

ソースファイルがインクルードするヘッダファイルを調べて,オブジェクトファイルの依存関係としてとして*.dファイルに出力する。システムヘッダディレクトリにあるものは除外される。

  • -MP

ソースファイルがインクルードするヘッダファイルが依存するものがないとして依存関係を出力する。

すなわち,ソースファイルがfoo.hというヘッダファイルをインクルードしている場合に

foo.h:

という依存関係を出力する

-MMDと-MPのオプションを使用しているのは,ヘッダファイルが更新された時にmakeが行われるようにするためである。詳しくは後述する。

ライブラリに関する宣言

LDFLAGS =  

リンク対象に含めるライブラリを指定します。例えば,数学ライブラリを使用するときには-lmを追加します。

LIBS    = 

使用する静的ライブラリ(.a)や,動的ライブラリ(.so)を宣言します。

ヘッダファイルのディレクトリを宣言

INCLUDE = -I ./include

使用するヘッダファイルが置かれたディレクトリを宣言します。 ここではMakefileが置かれたディレクトリの下に作成したincludeディレクトリにヘッダファイルが置かれたとしています。 -Iオプションはヘッダファイルを探索するディレクトリを追加するのに使用します。

ソースファイルのディレクトリを宣言

SRC_DIR = ./src

ソースファイルが置かれたディレクトリを宣言します。 ここではMakefileが置かれたディレクトリの下に作成したsrcディレクトリにソースファイルが置かれたとしています。

中間ファイルなどを置くディレクトリを宣言

OBJ_DIR = ./build

オブジェクトファイル(.o)や,ソースファイルの依存関係を示す中間ファイル(.d)を置くディレクトリを宣言します。 ここではMakefileが置かれたディレクトリの下に作成したbuildディレクトリに置くとしています。

ソースファイルの宣言

SOURCES = $(shell ls $(SRC_DIR)/*.cpp) 

実際に使用するソースファイルを宣言しています。

ここではSRC_DIRで宣言したディレクトリにある全ての.cppファイルを使用することを想定しています。

そうでない場合は,shellのコマンドを上手く使って使用しないものは除けばいいと思います。

オブジェクトファイルの宣言

OBJS    = $(subst $(SRC_DIR),$(OBJ_DIR), $(SOURCES:.cpp=.o))

ターゲットである実行ファイルが依存するオブジェクトファイルを指定しています。

SOURCESで指定したソースファイルから作成されたオブジェクトを全て使用することを想定しています。

substを使用して,SRC_DIRからOBJ_DIRに変換し,拡張子も.cppから.oにするようにしています。

ターゲットネームの宣言

TARGET  = target_name

ターゲットとなる実行ファイルの名前を宣言してください。

依存関係を示す中間ファイルの宣言

DEPENDS = $(OBJS:.o=.d)

依存関係を示す中間ファイル(*.d)を宣言します。

OBJSで宣言したものを.oから.dに変換するだけです。

allターゲット

all: $(TARGET)

先頭にあるので,普通にmakeするとTARGETがmakeされることになります。

TARGET依存関係

$(TARGET): $(OBJS) $(LIBS)
  $(CC) -o $@ $(OBJS) $(LDFLAGS)

ターゲットとなる実行ファイルの依存関係です。

オブジェクトの依存関係

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp 
   @if [ ! -d $(OBJ_DIR) ]; \
      then echo "mkdir -p $(OBJ_DIR)"; mkdir -p $(OBJ_DIR); \
      fi
  $(CC) $(CFLAGS) $(INCLUDE) -o $@ -c $< 

オブジェクトの依存関係です。

OBJ_DIRディレクトリが存在しない場合にディレクトリを作成し,オブジェクトを生成します。

clean

clean:
  $(RM) $(OBJS) $(TARGET) $(DEPENDS)

cleanを実行するとOBJS,TARGET,DEPENDSを全て削除します。

DEPENDSのインクルード

-include $(DEPENDS)

ソースファイルの依存関係が明記された.dファイルをインクルードします。

これによって,ヘッダファイルのみが更新された場合でもmakeが実行される事になります。

.PHONYターゲット
.PHONY: all clean

ファイルを生成しないターゲットを明記します。