2012年6月7日木曜日

make の -C オプションについて

make の -C オプションについて。

man make を読むと、

-C dir
makefile を読み込むなどの動作の前に、ディレクトリ dir に移動する。
(中略) このオプションは通常、make を再帰的に呼び出すときに使われる。

と書いてあります。

ほとんどの場面で 「make -C dir」は「cd dir; make」と同じと考えていいと思うが、
わずかに挙動が違う部分があることに最近気づいた。

実験のため、次のような /home/foo/Makefile, /home/foo/bar/Makefile
という2つの Makefile を用意。

/home/foo/Makefileの内容:

all:
    @ echo CURDIR = $(CURDIR)
    @ echo PWD = $(PWD)
    @ cd bar; $(MAKE)


/home/foo/bar/Makefileの内容:

all:
    @ echo CURDIR = $(CURDIR)
    @ echo PWD = $(PWD)


/home/foo にて make を実行してみると以下のように表示された。


CURDIR = /home/foo
PWD = /home/foo
make[1]: ディレクトリ `/home/foo/bar' に入ります
CURDIR = /home/foo/bar
PWD = /home/foo/bar
make[1]: ディレクトリ `/home/foo/bar' から出ます


さて、 cd bar; $(MAKE) の部分を $(MAKE) -C bar と書き変えるとどうだろう?


/home/foo/Makefileの内容:

all:
    @ echo CURDIR = $(CURDIR)
    @ echo PWD = $(PWD)
    @ $(MAKE) -c bar ← 変更する


/home/foo/bar/Makefileの内容:

all:
    @ echo CURDIR = $(CURDIR)
    @ echo PWD = $(PWD)




実行結果は以下のようになった。

CURDIR = /home/foo
PWD = /home/foo
make[1]: ディレクトリ `/home/foo/bar' に入ります
CURDIR = /home/foo/bar
PWD = /home/foo ← 注目!!!
make[1]: ディレクトリ `/home/foo/bar' から出ます


/home/foo/bar/Makefile における $(PWD) の値が変わった。

からくりはこうだ。

CURDIR は make が提供する変数で、現在実行している make プロセスのカレントディレクトリを返す。
一方、PWD は環境変数であって、make 変数ではない。

cd bar; make」で再帰的に make を呼び出した場合、
cd bar; の時点で PWD/home/foo/bar に更新される。

一方、「make -C bar」で再帰的に make を呼び出した場合、
環境変数 PWD/home/foo を指したまま、 /home/foo/bar/Makefile に引き継がれる。


このことから、make を実行しているカレントディレクトリを指す用途には、常に $(CURDIR) もしくは $(shell pwd) を使うべきだとわかる。
$(PWD) は使うべきでない。
上の例で明らかなように、上位階層の Makefile がどのように再帰的に呼び出しているかによって  $(PWD) が返す値が変わってくるので、必ずしも期待通りに動かないからだ。

ちなみに、変数がどこからやってきているかは

$ make --print-data-base
と打ってみればわかる。

やってみると以下のように表示された。

# makefile 変数
CURDIR := /home/masahiro

(中略)

# 環境変数
PWD = /home/masahiro


さらにちなみにだが、上の例で /home/foo/bar/Makefile$(PWD) の部分を $$PWD に書き変えるとまた違う結果になる。

make の構築ルールはサブシェル上で実行されることを知っている人は多いだろう。

echo PWD = $(PWD)
については、$(PWD) を make が展開した後にサブシェルに渡しているので、サブシェルは
echo PWD = /home/foo
を実行している。

一方、
echo PWD = $$PWD
については、make が $$$ に変換してサブシェルに渡すので、サブシェルは
echo PWD = $PWD
を実行することになる。$PWD はサブシェルによって /home/foo/bar へと展開される。

0 件のコメント:

コメントを投稿