「makeを使うと開発が非常に楽になる」というのは雰囲気でなんとなく分かりますが、 どういう問題をどう解決してくれるのかがイマイチ分からないので手をつけにくいですよね。
この記事ではmakefileの基本の書き方から、C/C++のコードを 依存関係を考慮して効率よくビルドできる makefileがどうできるのかを解説します。
Makeファイルを使うとコンパイルを自動化できます。
1. 基本構文
makefileの一番の基本ルールは
|
|
です。
例えば、
|
|
など。
このレシピを使うときは、これを"makefile"という名前で保存し、コマンドラインにmake main
と入力します。
普段何気なくmakeだけでビルドできていたと思いますが、これはmakeコマンドに何も引数を与えないと自動で
一番上にあるレシピを実行するというルールがあるからです。
大抵の場合、一番実行したいレシピを一番上に書くのでmake
だけで希望の動作が行えていたわけです。
なので、make clean
やmake install
というのはコマンドオプションではなく、makeファイル内の
cleanレシピ、installレシピを実行しろというコマンドだったんですね。
makeコマンドは賢いので、出力ファイル(ターゲット)がすでに存在し、依存ファイルに前回ビルドとの変更がなかった場合は 仕事を停止します。(重要: あとでこの機能を活用するので覚えておいてください。)
2. 変数を定義して使う
出力するプログラムの名前を簡単に変えられるようにできると、makefileをコピペして簡単な編集で使い回せて楽ですね。
変数は VARNAME := hogehoge
という構文で定義でき、使用するときは平文と区別するために$(VARNAME)
と、$()
で囲って展開します。
|
|
3. 複数のソースファイルに対応させる
main.cppだけでなく他にもhoge.cpp、piyo.cppがあったとしましょう。(このような状況は当たり前に発生しますよね) これらをビルドする愚直なmakefileは次のようになるかもしれません。
|
|
しかしこれでは無駄が多いです。というのも、この書き方では、3つのファイルのうちどれか一だけを 編集したときでも、すべてのファイルをコンパイルし直してしまうからです。
c++はビルドに大雑把にわけて2つのフェーズがあります。
一つはそれぞれのcppファイルをオブジェクトファイルに変換するフェーズ。 もう一つは全てのオブジェクトファイルを1つにまとめるフェーズです。 これを手動でコマンドで行うと、
|
|
このように一度オブジェクトファイルを経由し、その後リンクして全てのオブジェクトファイルを合成するという手法を取ることで、 編集したファイルのみをコンパイルし直すことができます。
main.cppだけをリコンパイルして成果物を作り直すには、
|
|
これでOKです。 しかし、いちいちこんなコマンドを入力するのは疲れてしまうので、makeを使って自動化しましょう。 だんだんmakeを使う価値が分かってきましたね。
makefileは次のようになります。
|
|
makeコマンドを発行すると、まずmain: main.o hoge.o piyo.o
の行に着目されます。
ここで、main.o
に変更があるのでmain.o: main.cpp
のレシピが実行されます。
makeはターゲットがすでに存在し、依存ファイルに編集がなかった場合何もしないので、
hoge.oやpiyo.oに対しては何もしないんですね。
4. 複数のルールを一つにまとめる
先程のmakefileの
|
|
の部分、ほぼ同じことを繰り返し書いていて、なんだかひとまとめにできそうですね。 先に修正されたMakefileをお見せします。
|
|
依存関係を示す部分はそのままで、実際の処理を記述している部分が.cpp.o
という変わったターゲット名になっていますね。
これは、サフィックスルールと呼ばれているものです。
サフィックスルールはシンプルで、
|
|
という記法になっています。
main.oが必要になったら、makeはこのサフィックスルールをみて、「main.cppをこのルールに適用すればmain.oが得られるんだな」
と解釈してくれるわけです。
手段の部分に$<
という見慣れないシンボルがありますね。これは自動変数と呼ばれています。
最低限の説明をすると、自動変数は「自動で値が代入されている」変数で、その場に応じて欲しいデータを
取得することができます。ここで使っている$<
はルール適用中の依存ファイルの名前が代入されています。
自動変数には他にもいろいろな種類があるので、詳しくは調べてみてください。
サフィックスルールによって少しすっきりしたものの、依然としてファイルが増えるたびに依存関係を記述しないといけないので、多少面倒ですね。 ソースファイルの依存関係はソース上でどのファイルがどのファイルを#includeしているかで決定されるので、プログラマとしてはもう記述済みの事実です。なんとか自動でmakeに反映することができないでしょうか。 特に、あるファイルから別のヘッダを突然includeしたくなるということは頻繁に発生しますよね?この度に書き換えますか?Makefile。 うっかり依存関係を書き忘れて一度make cleanしてからmakeしないとうまくビルドできなかったみたいな経験、誰でも一度はあるんじゃないんでしょうか。
実はあるんです。ソースファイルから依存関係を自動的に抽出してくれる魔法の方法が…
5.依存関係を自動で定義する
ソースファイルから依存関係を自動的に抽出するなんて魔法みたいなことがMakeだけで出来るんでしょうか。 残念ながらMakeの機能だけではできません。流石にCの構文を解析して依存関係を抽出するのは 第三者のアプリケーションには荷が重すぎます。
でも、gccのオプションを活用するとなんと可能です!本人に聞けって感じですね。 実は、gccには依存ファイルをmake形式で書き出すという神みたいなオプションがあるんです。 main.cにmain.hとCONST.hというincludeを書いた状態で次のコマンドを発行します。
|
|
すると、main.dというファイルがmain.oとともに出力されます main.dの中身は次のとおりです。
|
|
これをmakefileにincludeしてあげれば依存関係を自動で作ってくれるので完璧です。 また、初回のコンパイル時には.dファイルが存在していない為エラーになってしまうのを避ける為に、先頭に"-“をつけておきます。(急に雑な説明)
|
|
これでプロジェクトの変更に柔軟に対応し、効率よくビルドしてくれるMakefileができました。
また、SRCSをカレントディレクトリの全てのcppファイルを指すようにSRCS := $(wildcard *.cpp)
と書いてしまうのもパンチが効いてていいですね。
以下に参考として万能Makefile例全文を掲載しておきます。この記事で紹介しなかった機能等に関しては少しコメントを添えたので、 詳しくは検索してみてください(無責任)。
|
|