この記事を読んだ後の姿: リリンクしないMakefileの書き方を実装できて、人に説明することができる。人と一緒に解決することが出来る。
対象読者: なんとなくMakefileは書ける。動く仕組みは分からない。リリンクしてしまった時に、回消する為の思考プロセスが理解できない。
そもそもMakeとは
Makefile の目的はこちらを読んで欲しい
まとめると
- Makeは、プログラムのソースファイルから実行ファイルやその他の非ソースファイルの生成を制御するツール
- Make を使えば、エンドユーザはどのようにビルドやインストールが行われるかの詳細を知らなくても、あなたのパッケージをビルドしてインストールすることができます -1. なぜなら、これらの詳細はあなたが提供する makefile に記録されているから
- Make は、どのソースファイルが変更されたかに基づいて、 どのファイルを更新する必要があるかを自動的に判断します。また、ある非ソースファイルが別の非ソースファイルに依存している場合に備えて、ファイルを更新する適切な順序も自動的に決定する
- その結果、いくつかのソースファイルを変更してから Make を実行しても、プログラムのすべてを再コンパイルする必要はありません。変更したソースファイルに直接または間接的に依存している非ソースファイルのみを更新します
とある。
このうち、3, 4の要素に関わるのが リリンクである。
リリンクというとリンク処理のことだけを指しているが、要は解決されたターゲットを再生成しようとビルド処理するのが無駄だということだ。
リリンクしないように注意する点を紹介する
そもそもMake は、Makeにある
target: source
の依存関係を参照し解決する。
解決の仕方は、そのtarget やsource のタイムスタンプの比較によって行われる。
実行は以下の流れで行われる
- 実行されたtarget の source をtargetに make を実行する
a. target が存在しない, あるいは source が target より新しい場合、コマンドを実行する
b. source が target より古い場合、何もしない
1 のステップの時点で再起的に実行されているのがわかるだろうか。
relink は target と source の設定が間違っていることで発生する。
例
コマンドがターゲットを生成しないケース
target: source
echo "hello"
これでは、target が生成されないため、コマンドが毎回実行される
また、上は分かりやすいが
target: source
gcc -o target.out source.c
このような場合も、厳密には target名と生成されるファイル名が一致していないために コマンドが毎回実行されてしまう
対策としては
- $@ を使う
- make の変数を利用する
target: source
gcc -o $@ $^
$@ はコマンドのターゲット名を表すのでコーディングミスを防ぐことができる
TARGET = target
$(TARGET): source
gcc -o $(TARGET) source.c
このように、するのも良いだろう。2つの解決策を利用すると、生成するファイル名を変更したい場合に、編集箇所が1箇所になる。
Single Source of Truth の考え方は、どのような場面でも有効である。
source が存在しないケース
target: source
gcc -o $@
source:
echo "hello"
この場合、source が存在しないため、source のルールが毎回実行され、target のルールも毎回実行される
また、複数のmakefile で管理されているプロジェクトでは
target: source
gcc -o $@ source
source:
make -C ./source
といった記述があるかもしれない。
make -C で実行したスクリプトではちゃんと "source" という名前のファイルが生成されている場合でも、リリンクは発生する。
なぜなら、カレントディレクトリに "source" というファイルが存在しないためである。
対策としては
- source にディレクトリ名を含める
- source を生成後、moveしてカレントディレクトリに移動する
source = ./source/source
target: $(source)
gcc -o $@ $^
$(source):
make -C ./source
source = source
source_dir = ./source
target: $(source)
gcc -o $@ $^
$(source):
make -C $(source_dir)
mv $(source_dir)/$(source) .
など。
まとめ
リリンクを防ぐためには、ターゲット名と生成されるファイル名が一致しているかを確認することが重要である。
細かいところ間違ってるかもしれないが、ベースがあれば知識を繋いだり繋げ直したりすることが出来る。一回理解するのは大事。結局問題を解決できればいい。