ソフトウェアまわりの備忘録

研究室で培ったソフトウェアの知識を貯めておく備忘録。

バイナリファイルの整理とワイルドカードの利用(第四回)

前回までに

  • 依存関係の自動解決
  • 多段make

について示した。今回はバイナリファイルの整理と関数を利用した方法を紹介する。

wildcard関数の利用

カレントディレクトリのMakefile

Makefileで一番面倒臭く、エラーの原因になるのはファイルの追加とファイル面の変更だろう。同じ階層内であれば拡張子を基にコンパイルするリストに自動追加できる。今までソースファイルを

SRCS = main.cpp dirA/sample1.cpp dirB/sample2.cpp dirC/sample3.cpp dirC/dirD/sample4.cpp

としていたところを$(wildcard *.<拡張子>)とする。上の例なら

SRCS = $(wildcard *.cpp)\
    $(wildcard dirA/*.cpp)\
    $(wildcard dirB/*.cpp)\
    $(wildcard dirC/*.cpp)\
    $(wildcard dirC/dirD/*.cpp)

とする。これでソースファイルの名前を変更してもファイルを削除、追加しても自動的にリストを更新してくれる。

オブジェクトファイル、依存リストが散らかる

次にコンパイル後の中間ファイル(オブジェクトファイル.o、依存関係リスト.d)の整理を考える。これまではソースファイルのある階層にそのままオブジェクトファイルを作成して最後にリンクしていた。しかし、ファイル数が増えてくると一つのファイルに対して似た名前のファイルが二つ作られるため、コンパイル後には同じ階層に拡張子だけが異なる同じ名前のファイルが乱立して見にくくなる。そこでオブジェクトファイルや依存リストをobjというディレクトリにまとめる。

ここで前提条件が一つ。objディレクトリ内にdirA、dirBなどとディレクトリを作ってあげてから実行していただきたい。

今回の最終的なMakefileは以下のようになる。

GCC    = g++
CFLAGS = -O2 -MMD -Wall -Wextra
INCLUDE= -I./include
SUBDIR = dirA dirB dirC dirC/dirD
OBJDIR = obj
ifeq "$(strip $(OBJDIR))" ""
  OBJDIR = .
endif
SRCS  = $(wildcard *.cpp)\
				$(wildcard dirA/*.cpp)\
				$(wildcard dirB/*.cpp)\
				$(wildcard dirC/*.cpp)\
				$(wildcard dirC/dirD/*.cpp)

OBJS   = $(addprefix $(OBJDIR)/, $(SRCS:.cpp=.o))

DEPS   = $(OBJS:.o=.d)
TILS   = $(SRCS:.cpp=.cpp~)
TARGET = main

$(OBJDIR)/%.o: %.cpp
	@[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)
	$(GCC) $(CFLAGS) -o $@ -c $< $(INCLUDE)

$(TARGET): $(OBJS)
	$(GCC) $(CFLAGS) -o $@ $+

default: $(OBJS) $(TARGET)

clean:
	$(RM) $(OBJS) $(DEPS) $(TARGET)

-include $(DEPS)

主な変更点は3箇所である。まず、objディレクトリが存在するかどうかチェックするために

ifeq "$(strip $(OBJDIR))" ""
  OBJDIR = .
endif

というものを書いている。もし存在しなければカレントディレクトリ(.)にobjというディレクトリを作成する。

次に、

$(OBJDIR)/%.o: %.cpp
	@[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)
	$(GCC) $(CFLAGS) -o $@ -c $< $(INCLUDE)

とオブジェクトファイルの生成ルールを変更する。ディレクトリ/%.oとするとそのディレクトリ内にファイルが生成される。次に、OBJS = $(addprefix $(OBJDIR)/, $(SRCS:.cpp=.o))とするとaddprefixを使うと、変数の先頭に「/」前の任意文字列を追加できる。最後に、 @[ -d $(OBJDIR) ] || mkdir -p $(OBJDIR)の[]はtestコマンドである。test -dとすることでディレクトリが存在するかどうか判定できる。もしなければmkdir -p によりディレクトリを作成する。その後コンパイルを行う。

これでobjというディレクトリ内に中間ファイルが生成される。

さらなる課題

一応これでカレントディレクトリのMakefileでコンパイルしたいディレクトリを指定することにより、自動的にソースファイルリストが作成される。重要な問題点は、コンパイル前にobjディレクトリ内にdirAやdirB、dirCなどのディレクトリ構造を作ってあげなければいけない。もちろんディレクトリがないとエラーが発生する。これを判定するコマンドが必要である。

また、SRCSにディレクトリリストを作る場合も変更するのはディレクトリ名だけにしたい。wildcardを含む長い文を並べるのはよろしくない。このあたりの整理も行いたい。

と要望はどんどん細かく複雑になるが、一般性のあるMakefileを目指して頑張ります。

cmakeは絶対に使わない!ことを目標にして