Makefileの作成についての備忘録

Makefileに関するすばらしいサイト
トリビアなmakefile入門
Makefileの関数 - Qiita

Makefileで良く使う処理

ソースファイル*.cppから対応するオブジェクトファイル*.o名の列挙を自動化する。

LIBSRCS = hoge.cpp huga.cpp
LIBOBJS = $(LIBSRCS:%.cpp=%.o)

test:
  @echo "$(LIBOBJS)" # hoge.o huga.o

自動変数と呼ばれるものの一部を掲載。

test: hoge.o huga.o
	@echo "target=$@, list=$^, head=$<" # target=test, list=hoge.o huga.o, head=hoge.o 

whildcardマクロを使って、ファイル名を列挙するサンプル

FIND_DIR := ./
FOUND_HEADER := $(wildcard $(FIND_DIR)*.h)

test:
  @echo "$(FOUND_HEADER)" # ./hoge.h ./huga.h

realpathは引数で指定したファイル名から実在するものだけ、パスをスペース区切りで返す関数。notdirはパスからスペース区切りで、ファイル名だけを抽出する関数。以下のようにすることによって、ターゲットのソースファイルに対応し、かつ実在するヘッダファイルだけを列挙できる。

Makefileの依存リストにヘッダを自動で追加することができそう。ただし、ヘッダしか存在しないものには対応できない。

TARGET_SRCS = hoge.cpp huga.cpp main.cpp
test:
	@echo "$(notdir $(realpath $(TARGET_SRCS:%.cpp=%.h)))" # hoge.h huga.h

MM, MPオプションで依存関係を自動作成する

Makefileに依存するファイル名を列挙すると、makeコマンドを実行時にそのファイルの更新日付をチェックして新しくなっている場合のみリビルドされる仕組みになっている。

ただし、依存ファイルには、オブジェクトファイルソースファイルを記述するのが一般的で、ヘッダファイルは記述しない。そのため、ヘッダファイルの更新があっても再コンパイルされないので注意する必要がある。

かといってここに手作業でヘッダファイルを追加するのはとても非効率で、ヘッダファイルの依存関係についてどうにかならないかと思っていたら、ちゃんと方法があった。

コンパイラ(gcc, g++, clang)に-MMオプションを付与すると、コンパイラがソースを解析して、インクルードなどを文字列で出力することができる。

> clang -MM main.cpp
main.o: main.cpp hoge.h huga.h

また、-MMDとするとコンパイル時にこの依存関係の文字列を*.dファイルとして生成してくれる。

> clang -MMD main.cpp
> cat main.d
main.o: main.cpp hoge.h huga.h

この依存関係情報が出力された*.dファイルを-include文で指定すると、依存関係を的確にチェックしてくれるようになる。ただし、依存していたヘッダファイルが不要になって削除するとエラーになってしまうらしい。このときに追加で-MPオプションを追加すると、よろしくやってくれる。

自分用のサンプルMakefile

問題ある点はあるかもしれないが、とりあえず動作する。TARGET1(共有ライブラリ用)とTARGET2(実行ファイル)の2つの定義になっている。

CXX = clang
CXXFLAGS = -Wall -g -fpic -I. -I/usr/include -std=c++14
LDFLAGS = -Wall -L/usr/lib -g -fPIC

LIBS = -lstdc++ -lm
LIBS2 = -lstdc++ -lm -lusb

TARGET2 = main
SRCS = main.cpp
OBJS = $(SRCS:%.cpp=%.o)
DEPS := $(SRCS:%.cpp=%.d)

TARGET1 = libhoge.so
LIBSRCS = hoge.cpp huga.cpp
LIBOBJS = $(LIBSRCS:%.cpp=%.o)
LIBDEPS := $(LIBSRCS:%.cpp=%.d)

all: $(TARGET1) $(TARGET2)

# 実行ファイル作成
$(TARGET2): $(OBJS) $(TARGET1)
	$(CXX) $(LDFLAGS) $^ -o $@ $(LIBS)

$(OBJS): $(SRCS) 
	$(CXX) $(CXXFLAGS) -c -MMD -MP $(SRCS) 

# 共有ライブラリ作成
$(TARGET1): $(LIBOBJS) 
	$(CXX) $(LDFLAGS) -shared $^ -o $@ $(LIBS2)

$(LIBOBJS): $(LIBSRCS)
	$(CXX) $(CXXFLAGS) -c -MMD -MP $(LIBSRCS)

-include $(DEPS) $(LIBDEPS)

clean:
	-rm -f *.o *.so *.out *.a *.d $(TARGET1) $(TARGET2)