複数プロジェクトが併存できるC++用の汎用的なMakefileを作ってみた

  • 2
    いいね
  • 0
    コメント

概要

cmakeを使っても良いのだけど、ちょっとしたC++のコードを書くときにCMakeLists.txtを一から書くのもなんとなく億劫だったので、拡張が容易かつジェネリックなMakefileを書いてみました。またMakefileMakefileMakefile.inの2つに分離したことにより、ソースコードを共有する複数のプロジェクトのビルドも容易にできます。

使い方

  1. プロジェクトが存在するディレクトリに、後ほど説明するMakefileMakefile.inを入れてください。
  2. ソースコードを./src以下に、ライブラリを./include以下に入れてください。
  3. Makefileを編集して、各プロジェクトに対してコンパイル&リンクするソースコードを指定してください。
  4. 以上の作業で完了です。あとは自由に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_Aprog_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は以下のように設定します。

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を解説します。

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内の変数ARGSARGS += "[OPTION]=[VALUE]"とすれば設定できます。

補足

  • makeのオプションに--no-builtin-rulesを加えることで暗黙のルールを使用しないようにしています。これにより探索範囲が狭まり、さらに高速にコンパイルができるようになります。
  • 今回の例では拡張子をcpphppに指定しましたが、Makefile.in内の変数SUFFIXを変更することで、別の拡張子を用いることができます。
  • 今回の例ではソースコードの場所をディレクトリで指定しましたが、ARGS += "SRC_FILES=[PATH/TO/FILE]"でファイルを直接指定することもできます。
  • Makefileに追記することでツールを拡張することができます。

Download

https://github.com/Tamflex/makefile_template

参考文献

以下の文献を参考にさせていただきました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