Makefileの書き方に関する備忘録 その4


このエントリーをはてなブックマークに追加

他にもMakefileに関する記事を書いていますが、この記事だけで読んでも問題ありません。目次→Makefileの書き方に関する備忘録 - minus9d's diary

少し複雑な構成を持つC++のコード群からバイナリをビルドするための良いMakefileの例を makefile - How to place object files in separate subdirectory - Stack Overflow に見つけたので共有します。

目標

まず、以下のようなソースコードを持っているとします。srcC++ソースコードを格納するディレクトリで、その下のディレクトリ構造は決まった形をもたないものとします。includeはヘッダファイルのみを格納するディレクトリです。

/your_project
  /src
     /subdir1/a.cpp, a.h
              b.cpp, b.h
     /subdir2/subsubdir/c.cpp, c.h
     d.cpp, d.h
  /include
     e.h

これをビルドして以下のようにするのを目標とします。

/your_project
  /src
  /include
  /obj  中間生成物置き場
  /bin  最終バイナリ置き場 

Makefile

前出のページを参考に、自分なりに取捨選択して書き直したMakefileが以下です。このファイルは、your_projectディレクトリの直下に置かれているものとします。

# 参考:https://stackoverflow.com/questions/5178125/how-to-place-object-files-in-separate-subdirectory

# C++のコンパイラ
CXX          := g++

# 生成するバイナリの名前
TARGET      := target

# ディレクトリ名
SRCDIR      := src
BUILDDIR    := obj
TARGETDIR   := bin

# 拡張子名
SRCEXT      := cpp
DEPEXT      := d
OBJEXT      := o

# コンパイル、リンクのフラグ
CXXFLAGS    := -Wall -g -std=c++14
LDFLAGS     :=
INC         := -Isrc -Iinclude

#---------------------------------------------------------------------------------
# DO NOT EDIT BELOW THIS LINE
#---------------------------------------------------------------------------------

sources     := $(shell find $(SRCDIR) -type f -name *.$(SRCEXT))
objects     := $(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(subst $(SRCEXT),$(OBJEXT),$(sources)))
dependencies := $(subst .$(OBJEXT),.$(DEPEXT),$(objects))

# Defauilt Make
all: directories $(TARGETDIR)/$(TARGET)

# Remake
remake: cleaner all

# ディレクトリ生成
directories:
    @mkdir -p $(TARGETDIR)
    @mkdir -p $(BUILDDIR)

# 中間生成物のためのディレクトリを削除
clean:
    @$(RM) -rf $(BUILDDIR)

# 中間生成物のためのディレクトリと最終生成物のためのディレクトリを削除
cleaner: clean
    @$(RM) -rf $(TARGETDIR)

# 自動抽出した.dファイルを読み込む
-include $(dependencies)

# オブジェクトファイルをリンクしてバイナリを生成
$(TARGETDIR)/$(TARGET): $(objects)
    $(CXX) -o $(TARGETDIR)/$(TARGET) $^ $(LDFLAGS)

# ソースファイルのコンパイルしてオブジェクトファイルを生成
# また、ソースファイルの依存関係を自動抽出して.dファイルに保存
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)
    @mkdir -p $(dir $@)
    $(CXX) $(CXXFLAGS) $(INC) -c -o $@ $<
    @$(CXX) $(CXXFLAGS) $(INC) -MM $(SRCDIR)/$*.$(SRCEXT) > $(BUILDDIR)/$*.$(DEPEXT)
    @cp -f $(BUILDDIR)/$*.$(DEPEXT) $(BUILDDIR)/$*.$(DEPEXT).tmp
    @sed -e 's|.*:|$(BUILDDIR)/$*.$(OBJEXT):|' < $(BUILDDIR)/$*.$(DEPEXT).tmp > $(BUILDDIR)/$*.$(DEPEXT)
    @sed -e 's/.*://' -e 's/\\$$//' < $(BUILDDIR)/$*.$(DEPEXT).tmp | fmt -1 | sed -e 's/^ *//' -e 's/$$/:/' >> $(BUILDDIR)/$*.$(DEPEXT)
    @rm -f $(BUILDDIR)/$*.$(DEPEXT).tmp

# Non-File Targets
.PHONY: all remake clean cleaner

このMakefileでは、"DO NOT EDIT BELOW THIS LINE" より上にある項目を自分の環境に合わせて変更し、makeとコマンドを打つことで、バイナリがbin以下にできるようになります。

また、ソースコードの依存関係も自動で抽出するので、あるファイルを更新したあとmakeすると、そのファイルに依存するすべてのファイルに再コンパイルがかかります。

使い方は以下の通りです。