Makefile の = と := と ?= (makeの基礎)
問題です!
以下のような Makefile があったとして、
それぞれの make test
の出力結果は何になるでしょう?
Q1. =
x = foo y = $(x) bar x = later .PHONY: test test: @echo $(x) @echo $(y)
Q2. :=
x := foo y := $(x) bar x := later .PHONY: test test: @echo $(x) @echo $(y)
Q3. ?=
x ?= foo y ?= $(x) bar x ?= later .PHONY: test test: @echo $(x) @echo $(y)
ヒント!
ドキュメントです!
GNU make - How to Use Variables
答え
というわけで、上のドキュメントがわかりにくかったので
自分なりにまとめたかったための、この記事でした。
= (equal) , := (colon equal) , ?= (question mark equal) の挙動はすべて異なります。
特に = がよくあるプログラミング言語とは異なる挙動を示すので注意です。
A1. =
x = foo y = $(x) bar x = later .PHONY: test test: @echo $(x) @echo $(y)
答えは
later later bar
です。注意すべきは、y の中身が「later bar」になっていることですね。
make において = は recursive expansion (再帰的な展開)がなされます。
y = $(x) bar
と定義したとき、 x の値が更新されるたびに y の値は更新されるのです。
なので、 x = later
と再定義されたことで最初の x = foo
はもはや関係なく、 y の値は「later bar」となっているのです。
A2. :=
x := foo y := $(x) bar x := later .PHONY: test test: @echo $(x) @echo $(y)
これの出力は
later foo bar
です。一番わかりやすいのではないでしょうか。
ドキュメント上でも
Simply expanded variables generally make complicated makefile programming more predictable because they work like variables in most programming languages.
意訳すると『このシンプルな変数の展開は、多くのプログラミング言語での変数と同様の挙動をするので、複雑な Makefile をわかりやすくできます』といったことが書かれています。
Makefile での変数定義で :=
が使われることの多い理由がよくわかりました。
なお、もし1行目がない場合、つまり
y := $(x) bar x := later
となっていた場合、未定義の x は空文字として処理されます。ですので y の値は「bar」の文字列になります。
A3. ?=
x ?= foo y ?= $(x) bar x ?= later .PHONY: test test: @echo $(x) @echo $(y)
最後のこちらの答えは
foo foo bar
です。x が「foo」のままなのがポイントですね!
?=
は変数がまだ未定義のときのみ代入をおこなう演算子です。
最近のプログラミング言語ではこのような機能の演算子を実装されているものが多いですね。
なお、make のドキュメント上では conditional variable assignment operator (条件付き代入演算子)と呼ぶそうです。
おわりに
make は昔からあるツールながら、ほとんどの環境にビルドインされているぶん、
Docker や AWS コマンドなど長いコマンドを呼び出すことの多い昨今においても、
ビルドツール以外の使われ方として好まれて使われます。
一方で、私のような若輩者にとって Makefile の書き方は正直わからず、
今回の変数定義の話になりました。 :=
がよく使われる理由がわかってスッキリです。
冒頭のクイズ、周囲のプログラマー仲間に出題してみると面白いかもしれませんよ!
Vue.js、完全に理解した(い)!【プログラミング実況・はじめての Vue.js編 #2】
最近はこんなのもやってます!