Heavy Watal

make

https://www.gnu.org/software/make/manual/make.html

あらかじめコンパイルの命令を Makefile に書いておくことで、 ターミナルに長いコマンドを何度も打ち込むのを避けられる。 クロスプラットフォームで依存関係を解決しつつビルドするような Makefile を自分で書くのは難しいので、 CMakeautotools を使って自動生成する。

Makefile

C++ソースコードと同じディレクトリに入れるだけでとりあえず使える例:

## Options
PROGRAM := a.out
CXX := clang++
CC := clang
CXXFLAGS := -Wall -Wextra -O3 -std=c++14
CPPFLAGS := -I/usr/local/include -I${HOME}/local/include
LDFLAGS := -L/usr/local/lib -L${HOME}/local/lib
#LDLIBS := -lboost_program_options
TARGET_ARCH := -march=native

## Dependencies
SRCS := $(wildcard *.cpp)
OBJS := $(SRCS:.cpp=.o)

-include Dependfile
Dependfile:
        ${CXX} -MM ${CPPFLAGS} ${CXXFLAGS} ${TARGET_ARCH} ${SRCS} > Dependfile

## Targets
.DEFAULT_GOAL := all
.PHONY: all clean

all: ${PROGRAM}
        @:

${PROGRAM}: ${OBJS}
        ${LINK.cpp} ${OUTPUT_OPTION} $^ ${LDLIBS}

clean:
        ${RM} ${OBJS} ${PROGRAM}

Rule

https://www.gnu.org/software/make/manual/make.html#Rules

コロンとタブを使って以下のような形式でルールを書くのが基本。 このMakefileがあるところでターミナルから make TARGET と打つと、 ターゲットよりもソースファイルが新しい場合にコマンドが実行される。

TARGET : SOURCE1 SOURCE2
        COMMAND

a.out : main.cpp sub.cpp
        g++ -O2 main.cpp sub.cpp

下記のようなパターンルールが予め定義されている。 (see Pattern Rule)

%.o : %.c
        $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
%.o : %.cpp
        $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

以下に紹介するように、ほかにも様々な変数や関数が用意されていて、 個別のファイル名などをいちいち入力しなくても済むようになっている。

ファイル名が明示的に書かれずルールのみで生成された中間ファイルは自動的に削除される。 .PRECIOUS ターゲットにその名を加えておくとそれを防げる。 逆に、名前は出すけど中間ファイルとして扱いたい場合は .SECONDARY ターゲットに加える。

Implicit Variables

CC
Cコンパイラ cc
CXX
C++コンパイラ g++
COMPILE.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
LINK.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
LINK.o
$(CC) $(LDFLAGS) $(TARGET_ARCH)
OUTPUT_OPTION
-o $@
RM
rm -f
CPPFLAGS
プリプロセッサ用オプション。 e.g., -DNDEBUG -I${HOME}/local/include
CXXFLAGS
C++コンパイラ用オプション。 e.g., -Wall -Wextra -O3 -std=c++14
LDFLAGS
ライブラリパスを指定する。 e.g., -L/usr/local/lib -L{HOME}/local/lib
LDLIBS
リンクするライブラリを指定する。 昔はLOADLIBESも同じ機能だったが非推奨。 e.g., -lboost_program_options -lz
TARGET_ARCH
マシン依存なオプションを指定する。 e.g., -march=native -m64 -msse -msse2 -msse3 -mfpmath=sse

Automatic Variables

https://www.gnu.org/software/make/manual/make.html#Automatic-Variables

$@
ターゲット
$<
必須項目の先頭
$^
必須項目のスペース区切り
重複してても削らずそのまま欲しい場合は $+
新しく更新があったファイルだけ欲しい場合は $?

Functions

https://www.gnu.org/software/make/manual/make.html#Functions

文字列関連
$(subst FROM,TO,TEXT)
$(findstring FIND,IN)
$(filter PATTERN...,TEXT)
ファイル名
$(dir NAMES...)
$(notdir NAMES...)
$(basename NAMES...)
$(addprefix PREFIX,NAMES...)
$(wildcard PATTERN)
$(abspath NAMES...)
条件分岐
$(if CONDITION,THEN,ELSE)

関数じゃない条件分岐 (ifeq, ifneq, ifdef, ifndef, else, endif) もある。

その他
$(foreach VAR,LIST,TEXT): LIST の中身をそれぞれ VAR に入れて TEXT を実行
$(file op FILENAME,TEXT): text の結果をファイルに書き出す
$(call VARIABLE,PARAMS...): $(1) $(2) などを使って定義しておいた VARIABLE を関数のように呼び出す
$(origin VARIABLE): 変数がどう定義されたかを知れる
$(error TEXT...), $(warning TEXT...), $(info TEXT...): エラーや警告をプリントする
$(shell COMMAND...): シェルを呼び出す

Targets

https://www.gnu.org/software/make/manual/make.html#Standard-Targets

all
ディレクトリ内のcppソースをコンパイル
clean
コンパイル済みオブジェクトを一掃
_
v3.81以降であれば .DEFAULT_GOAL が効くので make all と同じ
make clean
make

Options

https://www.gnu.org/software/make/manual/make.html#Options-Summary

-f file
Makefile じゃない名前のファイルを指定したければ
-j jobs
並列コンパイル。コア数+1くらいがちょうどいいらしい
-C directory
そのディレクトリに行って make
-p
自動的に作られるものも含めてすべての変数を表示