Makefileの書き方
利用するケース
@yutakikuchi_です。
C/C++を書いた時に複数ファイルから実行ファイルを生成するときやライブラリをIncludeする場合コンパイルのオプションが複雑になります。複雑なオプションを毎回コマンドラインで入力するのではなく、Makefileというコンパイルのオプションルールを記載してmake/gmakeコマンドにて実行ファイルを生成すると便利です。今回はMakefileの簡単なルールについて紹介します。
Makefileの基本
基本ルール
C++ソースのコンパイルにはg++を利用します。例えばhello.cppというファイルをコンパイルする場合は$ g++ hello.cpp -o helloと実行するとhelloという実行ファイルが生成されます。これをMakefileを使って書くと次のようになります。Makefileを作成したらgmakeとコマンドを実行するだけです。
hello: hello.cpp #ターゲット: 依存ファイル g++ -Wall hello.cpp -o hello #実行コマンド clean: rm -f *.o hello書式を簡単に説明すると、1行目に生成したいターゲットファイル名: 依存ファイル、2行目に生成するための実行コマンドを記載します。実行コマンドの先頭にはTabを入力する必要があります。viなどではControl-V、Tabとして入力すると識別されると思います。Tabではなくspaceを入れてしまうと「Makefile:2: *** 分離記号を欠いています. 中止.」とエラーが出力されてしまうので注意が必要です。
上のMakefileをgmakeコマンドで実行するとhelloという実行ファイルが生成されます。生成された実行ファイルを消去したい場合はgmake cleanと実行するとrm -f *.o helloの箇所が実行されます。g++の-Wallオプションですが、全ての警告オプションを結合してくれるもので一番厳密に文法をチェックします。$ ls drwxr-xr-x 2 yuta yuta 4096 6月 30 11:40 . drwxr-xr-x 6 yuta yuta 4096 6月 30 11:20 .. -rw-r--r-- 1 yuta yuta 71 6月 30 11:32 Makefile -rw-r--r-- 1 yuta yuta 91 6月 30 11:17 hello.cpp $ gmake g++ -Wall hello.cpp -o hello $ ls drwxr-xr-x 2 yuta yuta 4096 6月 30 11:40 . drwxr-xr-x 6 yuta yuta 4096 6月 30 11:20 .. -rw-r--r-- 1 yuta yuta 71 6月 30 11:32 Makefile -rwxr-xr-x 1 yuta yuta 8548 6月 30 11:40 hello -rw-r--r-- 1 yuta yuta 91 6月 30 11:17 hello.cpp $ gmake clean rm -f *.o hello $ ls drwxr-xr-x 2 yuta yuta 4096 6月 30 11:41 . drwxr-xr-x 6 yuta yuta 4096 6月 30 11:20 .. -rw-r--r-- 1 yuta yuta 71 6月 30 11:32 Makefile -rw-r--r-- 1 yuta yuta 91 6月 30 11:17 hello.cpp複数ファイルの結合
Makefileのターゲットファイル名:依存ファイルという書式の依存ファイル名を複数指定してみます。新たにprint.cppというファイルを定義して以下のようなMakefileに書き換えます。main処理をhello.cppに、mainの中で利用したい関数をprint.cppに記述しています。
hello: hello.cpp print.cpp g++ -Wall hello.cpp print.cpp -o hello clean: rm -f *.o hello実は上の書き方はあまり推奨されません。というのもコンパイルの実行が毎回両方のファイルに適用されてしまいます。片方だけupdateを掛けたい時もあり、次の例のように各生成ファイル毎に記述を分離するのが一般的です。gmakeを実行すると修正された各cppからオブジェクトコードを生成し、最後に実行ファイルにまとめます。
hello: hello.o print.o g++ -Wall -o hello hello.o print.o print.o: print.cpp g++ -Wall -c print.cpp hello.o: hello.cpp g++ -Wall -c hello.cpp clean: rm -f *.o hello$ gmake g++ -Wall -c hello.cpp g++ -Wall -c print.cpp g++ -Wall -o hello hello.o print.oシンボル
複雑なコンパイルオプションやファイルをincludeする指定などはmakeのシンボルを利用します。良く利用するシンボル一覧は次のものになります。またシンボルの定義等で利用する演算子についても一覧にまとめます。
シンボル 説明 default CC Cコンパイルコマンド cc CXX C++コンパイルコマンド g++ CFLAGS Cコンパイルオプション 無し CXXFLAGS C++コンパイルオプション 無し CPPFLAGS C++プリプロセッサ用オプション 無し LDFLAGS ldというリンクを呼び出すコマンドのリンクオプション 無し INCLUDES includeするheaderのディレクトリを指定する 無し LIBS 利用するライブラリを指定する 無し TARGET 生成するターゲットファイル名 無し SRCS ターゲットファイルを生成するために利用するソースコード 無し OBJS ターゲットファイルを生成するために利用するオブジェクトファイル 無し RM ファイル削除コマンド rm -f
演算子 説明 := 右辺を即時に評価 = 使用される度に右辺を再評価 ?= 条件代入。値を持ってない場合に利用 += 追加代入
特に重要なのがCXXFLAGSとLDFLAGSかと思います。CXXFLAGSにはエラーオプション、CPU情報、最適化レベル、コンパイル高速化オプションなどが設定できます。LDFLAGSについてはダイナミックリンクを貼る設定が定義可能です。
その他書き方の注意点としてINCLUDES、LIBSなどでディレクトリを指定する場合は-I,-Lといったハイフンと大文字のIとLを連結します。さらにLIBSでは指定したディレクトリ以下のどのライブラリを使うかも指定しますが、その際は-lといったハイフンと小文字のLを利用します。以下に簡単な例を記載します。INCLUDES = -I/usr/local/include -I../libmisc -I. # /usr/loca/include, ../libmisc, . の3つのディレクトリからincludeする。 LIBS = -L../libmisc -lmisc -lX11 -lGL -lGLU -lm # ../libmiscディレクトリの misc, X11, GL, GLU, mをライブラリとして指定する。自動変数
Makefile中に記号の組み合わせで変数を表すものがあります。以下に一覧で示します。
変数 説明 $@ ターゲットファイル名 $< 最初の依存ファイル名 $? ターゲットより新しい全ての依存ファイル名 $^ 全ての依存ファイル名 $+ Makefileと同じ順番の依存ファイル名 $* suffixを除いたターゲット名 $% アーカイブだった時のターゲットメンバ名 シンボルと自動変数を使用したMakefile
上で紹介したシンボルと自動変数を使ったMakefileの例を以下に示します。CXXFLAGSで指定している-O2と-pipeはコンパイル最適化レベルとコンパイルを高速にするオプションです。-O0で最適化をしないもので、通常は-O2を指定するようです。その他CXXFLAGSには-m32や-march=i386を指定する事があります。これは64bit環境でも-m32を指定すると32bit用にコンパイル、-marchでどのCPUに向けたコンパイルなのか定義できます。下ではgmakeの実行例も記載します。
CXX = g++ TARGET = hello CXXFLAGS = -Wall -O2 -pipe SRCS = hello.cpp print.cpp OBJS := $(SRCS:.cpp=.o) #SRCSの各ファイルのサフィックスの.cppを.oに変換 $(TARGET): $(OBJS) $(CXX) -o $@ $(OBJS) clean: rm -f $(TARGET) $(OBJS)$ ls drwxr-xr-x 2 yuta yuta 4096 7月 1 12:24 . drwxr-xr-x 6 yuta yuta 4096 6月 30 11:20 .. -rw-r--r-- 1 yuta yuta 178 7月 1 12:18 Makefile -rw-r--r-- 1 yuta yuta 83 6月 30 12:00 hello.cpp -rw-r--r-- 1 yuta yuta 107 6月 30 12:15 print.cpp $ gmake g++ -Wall -O2 -pipe -c -o hello.o hello.cpp g++ -Wall -O2 -pipe -c -o print.o print.cpp g++ -o hello hello.o print.o