#概要
cmakeを使っても良いのだけど、ちょっとしたC++のコードを書くときにCMakeLists.txtを一から書くのもなんとなく億劫だったので、拡張が容易かつジェネリックなMakefileを書いてみました。またMakefileをMakefileとMakefile.inの2つに分離したことにより、ソースコードを共有する複数のプロジェクトのビルドも容易にできます。
#使い方
- プロジェクトが存在するディレクトリに、後ほど説明する
MakefileとMakefile.inを入れてください。 - ソースコードを
./src以下に、ライブラリを./include以下に入れてください。 -
Makefileを編集して、各プロジェクトに対してコンパイル&リンクするソースコードを指定してください。 - 以上の作業で完了です。あとは自由に
Makefileのコマンドが使えます。
以下に使用できるコマンドをまとめました([TARGET]はMakefile内で指定するターゲット名)。
なお実行ファイルは./bin以下に、オブジェクトファイルは./obj以下に自動的に生成されます。
| コマンド | 説明 |
|---|---|
make [TARGET] |
make [TARGET].releaseと同じ |
make [TARGET].release |
最適化オプションを適用し[TARGET]をビルドする |
make [TARGET].develop |
デバッグ用に[TARGET]をビルドする |
make [TARGET].all |
[TARGET]を強制的にビルドする |
make [TARGET].run |
生成された[TARGET]の実行ファイルを実行する |
make [TARGET].debug |
生成された[TARGET]の実行ファイルをgdbでデバッグする |
make [TARGET].clear |
生成された[TARGET]の実行ファイルを消去する |
make [TARGET].clean |
[TARGET]の実行ファイルとオブジェクトファイルを消去する |
make all |
すべてのプロジェクトをコンパイルする。 |
make clear |
すべてのプロジェクトの実行ファイルを消去する。 |
make clean |
すべてのプロジェクトの実行ファイルとオブジェクトファイルを消去する。 |
#Sample & Makefileの解説
Sample
実際にやってみましょう(サンプルコードのダウンロードはこちらから)。
簡単のため、以下のような./src以下にソースコードが、./include以下にライブラリがあるようなディレクトリ構造のプロジェクトを考えてみます。ここでprog_A、prog_Bの2つのプロジェクトのソースコードがあり、それぞれutils.hppに依存するような状況だとします。
.
├── Makefile
├── Makefile.in
├── include
│ └── utils
│ └── utils.hpp
└── src
├── prog_A
│ ├── hoge.cpp
│ ├── hoge.hpp
│ └── main.cpp
├── prog_B
│ ├── fuga.cpp
│ ├── fuga.hpp
│ └── main.cpp
└── utils
└── utils.cpp
Makefileの設定
まずMakefileは以下のように設定します。
MAKEFLAGS += --no-print-directory --no-builtin-rules
.DEFAULT_GOAL := all
TARGET := prog_A prog_B
prog_A : ARGS += "BIN_NAME=prog_A"
prog_A : ARGS += "SRC_DIRS=./src/prog_A ./src/utils"
prog_B : ARGS += "BIN_NAME=prog_B"
prog_B : ARGS += "SRC_DIRS=./src/prog_B ./src/utils"
$(addsuffix .release,$(TARGET)) : OPTION += release
%.release : %
@:
$(addsuffix .develop,$(TARGET)) : OPTION += develop
%.develop : %
@:
$(addsuffix .all,$(TARGET)) : OPTION += all
%.all : %
@:
$(addsuffix .run,$(TARGET)) : OPTION += run
%.run : %
@:
$(addsuffix .debug,$(TARGET)) : OPTION += debug
%.debug : %
@:
$(addsuffix .clear,$(TARGET)) : OPTION += clear
%.clear : %
@:
$(addsuffix .clean,$(TARGET)) : OPTION += clean
%.clean : %
@:
all : $(TARGET)
clear : $(addsuffix .clear,$(TARGET))
clean : $(addsuffix .clean,$(TARGET))
$(TARGET) :
@echo "----- make $(OPTION) $@ -----"
@make -f ./Makefile.in $(ARGS) $(OPTION)
ここで各プロジェクトに最低必要なのは以下のコードとなります。
各プロジェクトに対してMakefile内に以下のコードを記述してください。
[TARGET] : ARGS += "BIN_NAME=[実行ファイルの名前]"
[TARGET] : ARGS += "SRC_DIRS=[依存するソースコードが存在する相対ディレクトリ]"
また変数TARGETに追加したターゲットを加えれば完成です。
Makefile.inの解説
次にコンパイラに詳細なオプションを与えるMakefile.inを解説します。
MAKEFLAGS += --no-builtin-rules
CXX := g++
CXXFLAGS := -std=c++17 -Wall -m64 -MMD -MP
CXXFLAGS_DEV := -O0 -g
CXXFLAGS_REL := -O3
DEBUG := gdb
LIBS := -lpthread -lglut -lm
CPPFLAGS := $(addprefix -I, ./include $(wildcard ./include/*))
CPPFLAGS += `pkg-config --cflags opencv`
ifeq "$(shell getconf LONG_BIT)" "64"
LDFLAGS := `pkg-config --libs opencv`
else
LDFLAGS :=
endif
SUFFIX := cpp
BIN_NAME :=
BIN_DIR := ./bin
BINARY := $(BIN_DIR)/$(BIN_NAME)
SRC_DIR := ./src
SOURCES := $(SRC_FILES)
SOURCES += $(foreach dir,$(SRC_DIRS),$(shell find $(dir) -regex ".*\.$(SUFFIX)"))
OBJ_DIR := ./obj
OBJECTS := $(patsubst $(SRC_DIR)/%,$(OBJ_DIR)/%,$(SOURCES:.$(SUFFIX)=.o))
DEPENDS := $(OBJECTS:.o=.d)
.DEFAULT_GOAL := release
release : CXX_FLAGS += $(CXXFLAGS_REL)
release : $(BINARY)
develop : CXX_FLAGS += $(CXXFLAGS_DEV)
develop : $(BINARY)
$(BINARY) : $(OBJECTS) $(LIBS)
mkdir -p $(@D)
$(CXX) -o $@ $^ $(LDFLAGS)
$(OBJ_DIR)/%.o : $(SRC_DIR)/%.$(SUFFIX)
mkdir -p $(@D)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
all : clean release
debug :
$(DEBUG) $(BINARY)
run:
$(BINARY)
clear :
-rm -rf $(BINARY)
clean :
-rm -rf $(OBJECTS) $(BINARY)
.PHONY : all debug run clean clear
-include $(DEPENDS)
代表的なオプションについて説明します。
| option | 説明 |
|---|---|
CXX |
C++のコンパイラの種類 |
CXXFLAGS |
オプション |
CXXFLAGS_DEV |
make develop時に与えるオプション |
CXXFLAGS_REL |
make release時に与えるオプション |
DEBUG |
デバッガのプログラム |
LIBS |
リンクするライブラリ |
CPPFLAGS |
プリプロセス時に使うオプション |
SUFFIX |
ソースコードの拡張子 |
お好みでこれらのオプションを変えることができます。また、あるプロジェクトのみに変更を適応したい場合は、Makefile内の変数ARGSにARGS += "[OPTION]=[VALUE]"とすれば設定できます。
補足
-
makeのオプションに--no-builtin-rulesを加えることで暗黙のルールを使用しないようにしています。これにより探索範囲が狭まり、さらに高速にコンパイルができるようになります。 - 今回の例では拡張子を
cppとhppに指定しましたが、Makefile.in内の変数SUFFIXを変更することで、別の拡張子を用いることができます。 - 今回の例ではソースコードの場所をディレクトリで指定しましたが、
ARGS += "SRC_FILES=[PATH/TO/FILE]"でファイルを直接指定することもできます。 -
Makefileに追記することでツールを拡張することができます。
Download
#参考文献
以下の文献を参考にさせていただきましたm(_ _)m。
http://boysenberrypi.hatenadiary.jp/entry/2014/03/15/113703
http://nantonaku-shiawase.hatenablog.com/entry/20110905/1321356759
http://d.hatena.ne.jp/tanakaBox/20070327/1174958337