シンプルで応用の効くmakefileとその解説
makefile
は一度作るとそれ以降編集する機会が少なくなるので意外と真面目に考える人は少なく、ネット上でもまとまった情報は多くない。
Linux系OS上(正確に言うとGNU MakeとGCC)で複数のC/C++
ソースファイルから1つの実行ファイルを作成(make
)するための汎用的なmakefile
テンプレートを作った。名前はまだない。適宜ディレクトリ構成や設計などに従ってmakefile
をカスタマイズする必要があると思うがそのベースにする。
このmakefileのいいところ
- コンパイル対象となるソースファイルをワイルドカードで指定できる。
- ヘッダファイル、ライブラリ、オブジェクトファイルなどコンパイル、リンクに関連するどのファイルが外部で変更されていてもきちんと 差分 コンパイルされる。
- makefile自体に説明書(この記事)がある。
makefile
COMPILER = g++
CFLAGS = -g -MMD -MP -Wall -Wextra -Winit-self -Wno-missing-field-initializers
ifeq "$(shell getconf LONG_BIT)" "64"
LDFLAGS =
else
LDFLAGS =
endif
LIBS =
INCLUDE = -I./include
TARGET = ./bin/$(shell basename `readlink -f .`)
SRCDIR = ./source
ifeq "$(strip $(SRCDIR))" ""
SRCDIR = .
endif
SOURCES = $(wildcard $(SRCDIR)/*.cpp)
OBJDIR = ./obj
ifeq "$(strip $(OBJDIR))" ""
OBJDIR = .
endif
OBJECTS = $(addprefix $(OBJDIR)/, $(notdir $(SOURCES:.cpp=.o)))
DEPENDS = $(OBJECTS:.o=.d)
$(TARGET): $(OBJECTS) $(LIBS)
$(COMPILER) -o $@ $^ $(LDFLAGS)
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
-mkdir -p $(OBJDIR)
$(COMPILER) $(CFLAGS) $(INCLUDE) -o $@ -c $<
all: clean $(TARGET)
clean:
-rm -f $(OBJECTS) $(DEPENDS) $(TARGET)
-include $(DEPENDS)
使用方法
make
基本的にはmake
するだけで依存関係を考慮して差分コンパイルされる。
ソースファイルはもちろん、ヘッダファイル、ライブラリなどが更新されている場合も自動的に検出して差分コンパイルされる。
make all
強制的に全ソースをコンパイルしたい場合はmake all
する。
このコマンドは全ての中間ファイル(オブジェクトファイル、依存関係ファイル)と実行ファイルを削除してから全ソースをコンパイルする。
make clean
全ての中間ファイル(オブジェクトファイル、依存関係ファイル)と実行ファイルを削除する。
初期設定状態のmakefile
下記のようなディレクトリツリーで初期設定のmakefile
を用いてmake
を実行した場合、以下の矢印(<-)ようにファイルが生成される。
同一のディレクトリに存在する全てのcppファイルがコンパイル(及びリンク)対象となる。
example
|-- makefile
|-- bin
| `-- example <- (TARGET) 実行ファイル
|-- include
| `-- example.h
|-- obj <- (OBJDIR) 中間ファイル生成先ディレクトリ
| |-- example.d <- (DEPENDS) 依存関係ファイル
| `-- example.o <- (OBJECTS) オブジェクトファイル
`-- source
`-- example.cpp
解説
コンパイラの指定 (COMPILER
)
コンパイラはCOMPILER
の値を用いる。初期値はg++
。C言語のみの場合はgcc
に変更しても良いが基本的にはg++
で問題ない。
コンパイルオプション (CFLAGS
)
コンパイルオプションとしてCFLAGS
の値を用いる。-D
オプションによる#define
の追加、最適化オプション、コードカバレッジ用の-coverage
などを用いる場合はここに記述する。
以下は初期値の解説。
-Wall -Wextra -Winit-self -Wno-missing-field-initializers
-Wall
: コンパイルワーニングのレベルを最大にする。-Wextra
: 歴史的理由により-Wall
を使用でも抑制される警告を抑制しない。つまり可能な限り全ての警告を出す。-W*
(それ以外) : 詳しくはWarning Options - Using the GNU Compiler Collection (GCC)を参照されたし。
-g
- デバッグオプション。
gdb
でのデバッグを可能にする。
-MMD -MP
- ソースファイルの依存関係を中間ファイルに出力する。
- 依存関係ファイルはソースファイル名の拡張子を
.d
に変更したものとなり、OBJDIR
で指定したディレクトリに生成される。 - この依存関係ファイルは
makefile
最後のinclude
文にてインクルードされることで依存しているヘッダファイル等が変更された場合に自動的に再コンパイルされるようになる。
リンクオプション (LDFLAGS
)
リンクオプションとしてLDFLAGS
の値を用いる。初期値は空。
動的ライブラリをリンクする-l
オプションを用いる場合はここに記述する。
パスの通っていない動的ライブラリをリンクするならここにそのファイル名(*.so
みたいな)を書いても良い。
ライブラリの指定 (LIBS
)
静的リンクするライブラリとしてLIBS
の値を用いる。初期値は空。
静的ライブラリ(*.a
)を用いる場合、空白区切りでファイル名を記述する。
ここで指定したライブラリが更新された場合、make
は再コンパイルが必要だと認識する。
インクルードパスの指定 (INCLUDE
)
インクルードパスとしてINCLUDE
の値を用いる。初期値は-I./include
。
ソースファイル中の#include
ファイル検索パスに加えるパスを-I
オプションにて指定する。-I
オプションとディレクトリ名の間に空白を書くことはできない。
複数ディレクトリを指定したい場合は-I
オプションを空白区切りで複数指定する。
実行ファイルの指定 (TARGET
)
実行ファイル名としてTARGET
の値を用いる。
以下は初期値 ./bin/$(shell basename \`readlink -f .\`)
の解説。
実行ファイルの生成先のディレクトリは./bin
。
生成される実行ファイル名は $(shell basename \`readlink -f .\`)
である。
これはmakefile
の存在するディレクトリの名前。
中間ファイル生成先ディレクトリの指定 (OBJDIR
)
中間ファイル生成先ディレクトリとしてOBJDIR
の値を用いる。初期値は./obj
。
このフォルダにオブジェクトファイル(*.o
)や依存関係ファイル(*.d
)が生成される。
ソースファイルの指定 (SOURCES
)
コンパイル対象となるソースファイルとしてSOURCES
の値を用いる。初期値は$(wildcard $(SRCDIR)/*.cpp)
。
SRCDIR
に存在する拡張子cpp
のファイル全てをコンパイル対象とすることを意味する。別の拡張子(.c
など)に変更したい場合は、makefile
内のcpp
を全て変更する。
オブジェクトファイルの指定 (OBJECTS
)
オブジェクトファイルとしてOBJECTS
の値を用いる。
以下は初期値 $(addprefix $(OBJDIR)/, $(notdir $(SOURCES:.cpp=.o)))
の解説。
オブジェクトファイルの生成先ディレクトリはOBJDIR
。
オブジェクトファイル名はソースファイル(SOURCES
)の拡張子を.o
に置換したもの。
OBJDIR
が空の場合はmakefile
と同一のディレクトリに生成される。
依存関係ファイルの指定 (DEPENDS
)
依存関係ファイルとしてDEPENDS
の値を用いる。
初期値$(OBJECTS:.o=.d)
はオブジェクトファイルの拡張子を.d
に置換したもの。
そんじゃーね。に花束を。