2012年8月1日水曜日

make の ifdef はわかりにくい

シェルにおいて
$ FOO=
とすると、シェル変数 FOO は空ではあるが、定義はされている。
変数 FOO を未定義にするには
$ unset FOO
としなければならない。
つまり、シェルスクリプトで、「変数が空」であることと、「変数が未定義」であることは明確に違う。

一方、make においてはこのあたりが紛らわしいというか、非常にわかりにくいと思うので、整理しておきたい。



make には ifdef という構文があるが、これの挙動を見てみる。

(実験1)
以下の内容の Makefile を用意する。


FOO=

all:
ifdef
@echo FOO is defined.
else
@echo FOO is NOT defined.
endif




実行すると以下のようになる。

$ make
FOO is NOT defined.



FOO=
のように、空文字を代入した場合、 ifdefFOO を未定義だと判定した。

なるほど〜、makeにおいては
空の変数は未定義の扱いになる。
そして、make には unset がないから、変数を未定義に戻すには、空文字を代入すればいいんだ、と理解したくなるかもしれない。
だが、これは違う。


(実行2)
次の内容の Makefile を用意する。


FOO=


FOO ?= 1
BAR ?= 1


all:
@echo FOO = $(FOO)
@echo BAR = $(BAR)

この実行結果は以下のようになる。
$ make

FOO =
BAR = 1


?= というのは、変数が未定義の時のみ、右辺の値を代入してくれる。
実験結果を見ると、
?=BAR は未定義と判定したが、FOO は定義済みと判定したことになる。


(実験3)
以下の内容の Makefile を用意する。

FOO=

all:
@echo $(filter FOO, $(.VARIABLES))

実行結果は
$ make
FOO

となる。
.VARIABLES という makeの変数には、使用中の変数が全部入っている。
大量の変数が入っているのだが、その中に FOO も含まれていることがわかる。
つまり、 .VARIABLES には中身が空の変数も含めて、定義済みの変数がリストされている。


(実験1)〜(実験3)からわかることは、
makeにおいても、「変数が空」であることと「変数が未定義」であることは区別している。
(ただ、シェルと違って、いったん定義された変数を未定義に戻すことはできない。)
?= は変数が「未定義」であれば右辺値が代入される。
注意しておきたいことは、(実験1)からわかるように、 ifdef は「定義されているか」を判定するのではなく、「空でないかどうか」を判定するものだ。
これは、ひじょーーーーーに紛らわしい。
この ifdef の仕様は失敗じゃないの?と思う。

つまり
ifdef FOO
ifndef ($(FOO),)
と同じなのだ。

だから、 ifdef 自体、別になくても構わないし、私は使わない。
ifndef ($(FOO),)
を使うほうが、誤解がないから。



0 件のコメント:

コメントを投稿