Makefileメモ:理念編

なぜMakefileを使うかといえば,「コンパイルを簡単にするため」であり,もうちょい突き詰めると「コーディング→動作チェックまでの時間を短くするため」とも言える.Makefileを使えば「必要な部分のみコンパイル」ができるので,コンパイルにかける時間が短縮でき,作成開始から完成までのトータルの時間も短くすることが出来る.


にも関わらず,自分があんまりMakefileを使わなかったのは以下の理由が大きい.

  • 書き方わかんね
  • 依存関係を書くのめんどくせ
  • ファイルを追加するたびにMakefileまで書き直すのはめんどくせ

逆に言えば,これらの問題さえ解決されれば,ちょー便利ということになる.で,今回これらが解決したので,そのメモ.また問題が出てきたら文章化→ググるで.

書き方わかんね

「書き方がわからない」というのはどういう状況か.Makefileプログラミング言語と捉えることが出来るので,その方向から考えると以下の要素が挙げられる(もっと綺麗に書ける気もするが今回は気にしない).

  • 何が出来るのかわからない
  • 文法がわからない
何が出来るのかわからない

Makefileは「コンパイルが必要な部分だけコンパイルすること」が可能.しかし,ここで問題になっているのはそういうことではなく,もう一段階下の「〜が更新されていたら〜を実行する」というような知識について.他の例で言えば,「ファイルの読み書きが可能」というのに対する「file.ReadLine(line)でlineに一行分の文字列が入る」というような感じ.
要は「具体的に」何が出来るのかわからない,ということ.


ということで,以下に現在知っている「何が出来るか」を列挙する.

  • 「Aが更新されている場合,Bも更新が必要であり,そのためにCを実行する」というのが書ける
    • おそらく一番重要な「依存関係」と「生成コマンド」の設定.
  • 「使い回す値」や「長い設定」は格納しておける
    • 「使用するコンパイラ」や「指定するオプション」などを変数に格納することで(厳密にはマクロを使うことで),コンパイラの変更を容易にしたり,一行の文字数を減らして見やすくすることができる.(見やすくするのは「意味的な分割」で云々というのもあるが)
  • 「何を実行するか」の部分を一般化できる.
    • 「CPPからOBJを生成する場合,〜を実行する」という風に,ファイル名に依存せずに「〜を実行する」という「生成コマンド」の部分をまとめることができる.ただし,「CPPが更新されたらOBJも更新する」という風に「依存関係」はまとめることができないっぽい.これは自動で計算すればよし.
  • ファイル名などはマクロで参照できる
    • 「CPPからOBJを生成する場合,〜を実行する」という風にファイル名に依存せずに一般化して設定した場合,この設定には肝心のファイル名が含まれていないのでコンパイルの対象が設定できない.その場合でも,マクロを使えばファイル名を持ってこれる.
  • lsとかのコマンドが使える
    • lsやfindを使うことで,「CPPファイルを列挙する」ということが自動でできるため,Makefileを書き直す必要がなくなる.
  • コメントが書ける
    • まぁ一応.あるのとないのとじゃ結構違うし.
文法がわからない

「文法がわからない」とはつまり,「何が出来るか」はわかったけど「それをどう書けばいいか」がわからない状態といえる.要は,上で列挙した「何が出来るか」の書き方がわかんねーぜ問題.ということで,上に列挙したやつの書き方を書いてみる.

  • 「Aが更新されている場合,Bも更新が必要であり,そのためにCを実行する」というのが書ける
    • 「A1・A2が更新されている場合,Bも更新が必要であり,そのためにCとDを実行する」なら以下のように書く.A1とA2の間にはスペースを.CとDの先頭にはタブを.
B:A1 A2
	C
	D
  • 「使い回す値」や「長い設定」は格納しておける
    • HOGE=〜」とすれば「HOGE」に「〜」を格納でき,「$(HOGE)」と書けば「〜」と同じように扱える.
  • 「何を実行するか」の部分を一般化できる.
    • 以下のように書けば,「CPPからOBJを生成する場合,Cを実行する」となる.「CPPが変更されたらOBJを変更する」という意味ではないので注意(「生成コマンド」のまとめであって「依存関係」のまとめではない).また,前述の「依存関係の書き方」とは向きが異なるので注意(B←AとCPP→OBJ).
.cpp.obj:
	C
  • ファイル名などはマクロで参照できる
    • 「$」を先頭につけたもので参照.前述の「HOGE」も実際にはマクロなので,「$」で中身が展開される.「$<」のように,ファイル名を参照するマクロなどがある(リファレンスの方を参照).
  • lsとかのコマンドが使える
    • 「$(shell ls *.cpp)」とすれば,そのディレクトリに存在するCPPファイルを列挙したものが展開される.
  • コメントが書ける
    • 「#」で始まった行はコメントになる

依存関係を書くのめんどくせ

ファイルの数が一桁ならば,まだ手書きでなんとかなるが,二桁以上になると死ねる.ということで,自動で依存関係を書き出せたらいいなぁと思ってググってたら良いのがヒットした.


http://www.phys.cs.is.nagoya-u.ac.jp/~watanabe/tips/makefile.htmlに,「make dep」で依存関係を書き出せる設定が書いてある.「g++ -MM -MG $(SRC) >makefile.depend」で「makefile.depend」に依存関係を書き出せるらしい.「g++」はコンパイラで,「$(SRC)」は関連する全てのCPPファイル.その結果を「>」を使ってファイルとして「makefile.depend」に書き出しているので,「-MM -MG」というオプションが依存関係を吐き出すための処理らしい.

ファイルを追加するたびにMakefileまで書き直すのはめんどくせ

で,上のサイトには「自動でCPPファイルを探す方法」も書いてあった(すでに「書き方わかんね」の項目でも多少書いたが).「SRC=$(shell ls *.cpp)」とすれば,そのディレクトリにあるCPPファイルを列挙できる.今の自分のやり方だと,CPPファイルは別の階層にあったりするので,「SRC=$(shell find Src -name "*.cpp")」という風にして「Srcというディレクトリ(サブディレクトリ含む)にある全てのCPPファイルを探し出す」ようにしている.


依存関係を吐き出すために,「make dep」してから「make」する必要があるが,「依存関係」の「A」の部分に「dep」を加えれば自動で実行,チェックしてくれるらしい.


これで,ファイルを追加しようが依存関係が変化しようが,Makefileへの変更は不要となる.