[ C++で開発 ]
更新日:
C++のビルドをGNU makeで行います。
一つのsrcディレクトリ、一つのincludeディレクトリからなるソースファイルをmakeし、一つの実行ファイルを作成します。
PROGRAM = hello.exe SRCS = Hello.cc Main.cc OBJS = $(subst .cc,.o,$(SRCS)) RM := rm CXX := g++ CC := g++ CPPFLAGS = -I../include LDFLAGS = -mno-cygwin $(PROGRAM): $(OBJS) $(LINK.o) $^ $(LOADLIBES) -o $@ .PHONY: clean clean: $(RM) $(OBJS) $(PROGRAM)
.ccファイルから.oファイルへコンパイルするルールは、GNU makeの暗黙のルールが使われています。
makeを実行すると、
$(CC) $(LDFLAGS) $(TARGET_ARCH) $^ $(LOADLIBES) -o $@マクロが展開され実行される例
g++ -mno-cygwin Hello.o Main.o -o hello.exe
$(COMPILE.cc) $(OUTPUT_OPTION) $<展開される中間マクロ
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $@ $<マクロが展開され実行される例
g++ -mno-cygwin -I../include -c -o Hello.o Hello.cc
make中で使用するコマンド名は、移植性を考慮して変数で表すのがよい習慣です。
RM := rm CXX := g++ CC := g++
再帰展開する必要がないので、単純展開変数として代入(:=)を使用しています。
ファイルが存在しないターゲット(擬似ターゲット)は、.PHONYで指定します。偶然擬似ターゲットと同じ名前のファイルが存在してしまった場合の誤動作を防ぎます。
.PHONY: clean
ソースファイルをMakefile中で指定するのではなく、その場でカレントディレクトリにあるものを対象とすることもできます。
SRCS := $(wildcard *.cc) OBJS = $(subst .cc,.o,$(SRCS))
Ver.1では、ヘッダーファイルの変更はmakeに検知されません。そこで、ヘッダーファイルの変更を検知できるように依存関係の記述を追加します。
PROGRAM = hello.exe SRCS = Hello.cc Main.cc OBJS = $(subst .cc,.o,$(SRCS)) RM := rm MV := mv CXX := g++ CC := g++ SED := sed CPPFLAGS = -I../include LDFLAGS = -mno-cygwin dependencies = $(subst .o,.d,$(OBJS)) $(PROGRAM): $(OBJS) $(LINK.o) $^ $(LOADLIBES) -o $@ .PHONY: clean clean: $(RM) $(OBJS) $(PROGRAM) $(dependencies) ifneq "$(MAKECMDGOALS)" "clean" include $(dependencies) endif %.d: %.cc $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -M $< | \ $(SED) 's,\($(notdir $*)\.o\) *:,$(dir $@)\1 $@: ,' >$@.tmp $(MV) $@.tmp $@
ソースファイル(.cc)とヘッダーファイル(.hh)の依存関係は、.dファイルに記述します。.dファイルは、gccの-Mオプションで生成される依存関係ファイルからsedコマンドを使って抽出します。(他のコンパイラではオプションと出力形式が違うので調整が必要)
.dファイルは都度修正されるので、Makefileからincludeするようにしています。
ディレクトリに対して再帰的に実行するMakefileを記述するやり方は、書籍「GNU Make 第3版」では推奨されていません。代わりに、各ディレクトリの設定を記述した個別ファイルをインクルードした1つのMakefileで全体を制御する方法を推奨しています。
[To Be Description]
変数名 | 内容 |
---|---|
$@ | ターゲットのファイル名 |
$% | ライブラリの構成指定中の要素 |
$< | 最初の必須項目のファイル名 |
$? | ターゲットより最新の必須項目をスペースで区切ったリスト |
$^ | すべての必須項目をスペースで区切ったリスト(重複は除く) |
$+ | すべての必須項目をスペースで区切ったリスト(重複を含む) |
$* | ターゲットファイル名のサフィックスを除いたファイル名(basename) |
シンボル | 内容 | デフォルトの定義 | |
CXX | C++コンパイルコマンド名 | g++ | |
CXXFLAGS | C++コンパイルオプション | 未定義 | |
CPPFLAGS | プリプロセッサ用オプション | 未定義 | |
TARGET_ARCH | 未定義 | ||
LDFLAGS | リンカ用オプション | 未定義 | |
COMPILE.cc | C++のコンパイル実行 | $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c | |
COMPILE.C | $(COMILE.cc) | ||
COMPILE.cpp | $(COMILE.cc) | ||
LINK.cc | C++のリンク実行 (ソースから一気に実行体を作る) |
$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) | |
LINK.C | $(LINK.cc) | ||
LINK.cpp | $(LINK.cc) | ||
LINK.o | オブジェクトファイルのリンク | $(CC) $(LDFLAGS) $(TARGET_ARCH) | |
OUTPUT_OPTION | -o $@ | ||
C/C++言語用のルールを抜粋しました。全てを見る場合、--print-data-base (-p)オプションを使います。
ルール | 実行 | |
%: %.o | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
%: %.cc | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
%.o: %.cc | $(COMPILE.cc) $(OUTPUT_OPTION) $< | |
%: %.C | $(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
%.o: %.C | $(COMPILE.C) $(OUTPUT_OPTION) $< | |
%: %.cpp | $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
%.o: %.cpp | $(COMPILE.cpp) $(OUTPUT_OPTION) $< | |
.cc.o: | $(COMPILE.cc) $(OUTPUT_OPTION) $< | |
.cc: | $(LINK.cc) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
.C.o: | $(COMPILE.C) $(OUTPUT_OPTION) $< | |
.C: | $(LINK.C) $^ $(LOADLIBES) $(LDLIBS) -o $@ | |
.cpp.o: | $(COMPIILE.cpp) $(OUTPUT_OPTIONS) $< | |
.cpp: | $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@ |
暗黙ルールを削除するならば、空のルールを記述します。
%.o: %.l
パターンルールは、ファイル名本体(basename)を'%'で示し、ルールを記述します。
%.o: %.c $(COMPILE.c) $(OUTPUT_OPTION) $<
パターンルール中ではファイル名の中で%は1度しか使えません。
特定のターゲットに対してのみパターンルールを適用させることができます。
$(OBJECTS): %.o: %.c $(CC) -c $(CPPFLAGS) $< -o $@
$(OBJECTS)に記述されたファイルを構築するときにのみパターンルールを適用します。
サフィックスルールは、旧来のmakeとの互換性のために残されています。GNU makeで記述する場合はパターンルールを使うのがベターです。
デフォルトでは以下の順序で変数定義の優先度が決まります。
2.を最優先にするには、変数を定義する際にoverride命令を使います。
3.を最優先にするには、makeのコマンドラインオプションで--environment-overrides (-e)を指定します。
関数名 | 内容 | |
---|---|---|
$(filter パターン...,テキスト) | テキストを空白で区切ったリストとみなし、パターンと一致したものを返す(完全一致) | |
$(filterout パターン..., テキスト) | パターンに一致しないものを返す | |
$(findstring 文字列,テキスト) | テキスト中に文字列を探し、見つかれば文字列自身、見つからねば何も返さない | |
$(subst 検索文字列,置換文字列,テキスト) | 単純置換。空白に注意 | |
$(patsubst 検索パターン,置換パターン,テキスト) | ワイルドカード(%)が使える置換 | |
$(変数:検索文字列=置換文字列) | 参照置換。単語末尾に存在する検索文字列を置換 | |
$(words テキスト) | テキスト中の単語数を返す | |
$(word n, テキスト) | テキスト中のn番目の単語を返す(n>=1) | |
$(firstword テキスト) | テキスト中の最初の単語を返す | |
$(wordlist 開始,終了,テキスト) | テキスト中の開始番目〜終了番目までの単語列を返す | |
$(sort リスト) | 重複を除去し辞書順にソート | |
$(shell コマンド) | ||
$(wildcard パターン...) | ||
$(dir リスト...) | ディレクトリ部分だけを返す | |
$(notdir リスト...) | ファイル名部分だけを返す | |
$(suffix 名前...) | サフィックスのリストを作って返す | |
$(basename 名前...) | サフィックスを取り除いた部分を返す | |
$(addsuffix サフィックス,名前...) | 名前にサフィックスを追加 | |
$(addprefix プレフィックス,名前...) | 名前にプレフィックスを追加 | |
$(join プレフィックスリスト,サフィックスリスト) | dirとnotdirを補完する役割、それぞれのリストの1番目同士、2番目同士、‥を連結 | |
$(if 条件,真のときに実行,偽のときに実行) | ||
$(error テキスト) | テキスト表示後、makeは終了ステータス2で終了する | |
$(foreach 変数,リスト,実行部) | ||
$(strip テキスト) | 前後の空白削除、途中の空白列を1つの空白に置換 | |
$(origin 変数) | 変数の出自を返却。undefined | default | environment | environment-override | file | command-line | override | automatic | |
$(worning テキスト) | makeは終了しない |
makeコマンドに、カレントディレクトリ以外からファイルを探させるための仕組みです。2つの設定方法があります。
define文を使って複数行のコマンド実行をマクロ定義することができます。
define create-jar @echo Creating $@... $(RM) $(TMP_JAR_DIR) $(MKDIR) $(TMP_JAR_DIR) $(CP) -r $^ $(TMP_JAR_DIR) cd $(TMP_JAR_DIR) && $(JAR) $(JARFLAGS) $@ . $(JAR) -ufm $@ $(MANIFEST) $(RM) $(TMP_JAR_DIR) endef
このマクロを実行するには、通常の変数と同様に呼び出します。
$(UI-JAR): $(UI_CLASSES) $(create-jar)
マクロは引数を伴うこともできます。define句の中では、$1, $2, ...のように参照します。
ifdef, ifndef
ifdef COMSPEC PATH_SEP := ; else PATH_SEP := : endif
ifeq, ifneq