2012年6月27日水曜日
2012年6月19日火曜日
マクロを文字列化する
プリプロセッサでマクロを文字列化する方法ないかな〜、って以前から思ってたんですが、 u-boot のソースコード読んでいたときに発見しました!
どういうことかといいますと #x のような文字列化機能がプリプロセッサにありますね。
例えば、以下のような感じ。
---
#include <stdio.h>
#define MK_STR(x) #x
char buf[] = "baudrate=" MK_STR(19200);
int main()
{
puts(buf);
return 0;
}
---
これをコンパイル、実行すると
19200 → "19200" のように文字列化してくれています。ここまで普通です。
やりたいことは、19200 の部分をマクロで定義しておきたいのです。
---
#include <stdio.h>
#define CONFIG_BAUDRATE 19200
#define MK_STR(x) #x
char buf[] = "baudrate=" MK_STR(CONFIG_BAUDRATE);
int main()
{
puts(buf);
return 0;
}
---
う〜ん、残念。
CONFIG_BUADRATE → 19200 → "19200"
と置き換えてほしいんですが、 CONFIG_BAUDRATE をそのまま文字列化してくれました。
sprintf(buf, "%d", CONFIG_BAUDRATE)
みたいに sprintf は使いたくない。
で、今回見つけたやり方は以下の方法。
---
#include <stdio.h>
#define CONFIG_BAUDRATE 19200
#define XMK_STR(x) #x
#define MK_STR(x) XMK_STR(x)
char buf[] = "baudrate=" MK_STR(CONFIG_BAUDRATE);
int main()
{
puts(buf);
return 0;
}
---
$ ./a.out
baudrate=19200
おぉぉ〜!うまくいった。
一見無駄にも見える
というマクロ定義ですが、こんな効能があるんですね。
どういうことかといいますと #x のような文字列化機能がプリプロセッサにありますね。
例えば、以下のような感じ。
---
#include <stdio.h>
#define MK_STR(x) #x
char buf[] = "baudrate=" MK_STR(19200);
int main()
{
puts(buf);
return 0;
}
---
これをコンパイル、実行すると
$ ./a.out
baudrate=19200
baudrate=19200
19200 → "19200" のように文字列化してくれています。ここまで普通です。
やりたいことは、19200 の部分をマクロで定義しておきたいのです。
---
#define CONFIG_BAUDRATE 19200
#define MK_STR(x) #x
char buf[] = "baudrate=" MK_STR(CONFIG_BAUDRATE);
int main()
{
puts(buf);
return 0;
}
---
$ ./a.out
baudrate=CONFIG_BAUDRATE
baudrate=CONFIG_BAUDRATE
う〜ん、残念。
CONFIG_BUADRATE → 19200 → "19200"
と置き換えてほしいんですが、 CONFIG_BAUDRATE をそのまま文字列化してくれました。
sprintf(buf, "%d", CONFIG_BAUDRATE)
みたいに sprintf は使いたくない。
で、今回見つけたやり方は以下の方法。
---
#include <stdio.h>
#define CONFIG_BAUDRATE 19200
#define XMK_STR(x) #x
#define MK_STR(x) XMK_STR(x)
char buf[] = "baudrate=" MK_STR(CONFIG_BAUDRATE);
int main()
{
puts(buf);
return 0;
}
---
$ ./a.out
baudrate=19200
おぉぉ〜!うまくいった。
一見無駄にも見える
#define XMK_STR(x) #x
#define MK_STR(x) XMK_STR(x)
#define MK_STR(x) XMK_STR(x)
というマクロ定義ですが、こんな効能があるんですね。
2012年6月14日木曜日
make に無限ループ防止機能?
以下の Makefile は無限ループになるだろう、と思っているのだが、実際にやってみるとならない場合もあるみたいだ。。
$(warning restarting)
include foo.mk
all:
echo $(BAR)
foo.mk: FORCE
echo "BAR:=1" > $@
.PHONY: all FORCE
FORCE:
教科書的には make の動作は以下のようになります:
make は include 命令に出会うと、そのインクルードファイルを読み込みます。
ただし、インクルードファイルがなくても、エラーを報告した上で処理を続行します。
makefile を読み終わった時点で、インクルードファイルを更新するルールがないか探します。
もしルールが見つかると、通常のターゲットの更新として処理します。
ルールによりインクルードファイルが更新されると、make を最初からやりなおします。
この例の場合、
が見つかりました。
そこで make はまず FORCE を更新し(といってもPHONYターゲットなので何もできませんが)、続いて foo.mk を更新するために
foo.mk が更新されたので、 make を最初からやりなおします。
ですが、 foo.mk: FORCE という依存関係があるので、またまた foo.mk を更新すべきと判断します。で foo.mk を更新し、また make を最初からやりなおし、といったループを繰り返すわけです。
理屈的にはこうなはずなのですが、手元の Ubuntu 11.10 マシンでやってみたら以下のようになりました。
リスタート3回で止まった。不思議!
もう一回やってみると
$ make
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
echo 1
1
となった。
何回か同じことを繰り返すと、無限ループに入ったぞ、と判断してやめるのかな?
リスタート回数は毎回違うんですけど。。
make のバージョンは
一方、別の RHEL マシンでやってみると、無限に繰り返すようです。
make のバージョンは同じく 3.81 です。
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
これはフリーソフトウェアです. 利用許諾についてはソースを
ご覧ください.
商業性や特定の目的への適合性の如何に関わらず, 無保証です.
This program built for x86_64-redhat-linux-gnu
このあたりの挙動はディストリビューションによって違うんでしょうか??
よくわかりません。
$(warning restarting)
include foo.mk
all:
echo $(BAR)
foo.mk: FORCE
echo "BAR:=1" > $@
.PHONY: all FORCE
FORCE:
教科書的には make の動作は以下のようになります:
make は include 命令に出会うと、そのインクルードファイルを読み込みます。
ただし、インクルードファイルがなくても、エラーを報告した上で処理を続行します。
makefile を読み終わった時点で、インクルードファイルを更新するルールがないか探します。
もしルールが見つかると、通常のターゲットの更新として処理します。
ルールによりインクルードファイルが更新されると、make を最初からやりなおします。
この例の場合、
include foo.mk
がありますので、 foo.mk を構築するルールがないか探します。
foo.mk: FORCE
echo "BAR:=1" > $@
echo "BAR:=1" > $@
が見つかりました。
そこで make はまず FORCE を更新し(といってもPHONYターゲットなので何もできませんが)、続いて foo.mk を更新するために
echo "BAR:=1" > $@
を実行します。foo.mk が更新されたので、 make を最初からやりなおします。
ですが、 foo.mk: FORCE という依存関係があるので、またまた foo.mk を更新すべきと判断します。で foo.mk を更新し、また make を最初からやりなおし、といったループを繰り返すわけです。
理屈的にはこうなはずなのですが、手元の Ubuntu 11.10 マシンでやってみたら以下のようになりました。
$ make
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
echo 1
1
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
echo 1
1
リスタート3回で止まった。不思議!
もう一回やってみると
$ make
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
Makefile:1: restarting
echo "BAR:=1" > foo.mk
echo 1
1
となった。
何回か同じことを繰り返すと、無限ループに入ったぞ、と判断してやめるのかな?
リスタート回数は毎回違うんですけど。。
make のバージョンは
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
これはフリーソフトウェアです. 利用許諾についてはソースを
ご覧ください.
商業性や特定の目的への適合性の如何に関わらず, 無保証です.
This program built for x86_64-pc-linux-gnu
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
これはフリーソフトウェアです. 利用許諾についてはソースを
ご覧ください.
商業性や特定の目的への適合性の如何に関わらず, 無保証です.
This program built for x86_64-pc-linux-gnu
一方、別の RHEL マシンでやってみると、無限に繰り返すようです。
make のバージョンは同じく 3.81 です。
$ make --version
GNU Make 3.81
Copyright (C) 2006 Free Software Foundation, Inc.
これはフリーソフトウェアです. 利用許諾についてはソースを
ご覧ください.
商業性や特定の目的への適合性の如何に関わらず, 無保証です.
This program built for x86_64-redhat-linux-gnu
このあたりの挙動はディストリビューションによって違うんでしょうか??
よくわかりません。
2012年6月12日火曜日
Makefile 変数のスコープ
Linux の Makefile には以下のような記述が出てきます。
clean: rm-dirs := $(CLEAN_DIRS)
clean: rm-files := $(CLEAN_FILES)
(ターゲット): (代入文)
という記述方法ですが、これっていったいどういう機能を持つのでしょうか?
以下のような簡単な Makefile を書いて実験してみます。
foo := 1
all: bar := 1
sub: baz := 1
all: sub
all sub:
@ echo foo = $(foo) in $@
@ echo bar = $(bar) in $@
@ echo baz = $(baz) in $@
$(warning foo = $(foo) in warning)
$(warning bar = $(bar) in warning)
$(warning baz = $(baz) in warning)
$ make
を実行すると
Makefile:12: foo = 1 in warning
Makefile:13: bar = in warning
Makefile:14: baz = in warning
foo = 1 in sub
bar = 1 in sub
baz = 1 in sub
foo = 1 in all
bar = 1 in all
baz = in all
と表示されました。
$(warning ) 文からは bar と baz の値が見えてないですし、
all の構築ルールの中では baz の値が見えてないですね。
また
$ make sub
を実行すると
Makefile:12: foo = 1 in warning
Makefile:13: bar = in warning
Makefile:14: baz = in warning
foo = 1 in sub
bar = in sub
baz = 1 in sub
sub の構築ルールの中では bar の値が見えないですね。
foo := 1 がグローバル変数的な振る舞いをするのに対して、
all: bar := 1 と書くと、all を構築している間だけこの代入文が見えます。
all の構築が終わると消えます。
sub: baz := 1 も同様。
ローカル変数的に使うことができますね。
2012年6月11日月曜日
LinuxカーネルのMakefileを解析する その7
前回から間があきましたが、今回はLinux Makefile のコンパイルオプションについて解析してみた。
Makefile 中でのコンパイルオプションの与え方は Documentation/kbuild/makefiles.txt の 「3.7 Compilation flags」に記載されている。
ざっと読んでみると、
ccflags-y, asflags-y, ldflags-y
これが記載されている Makefile で有効。
(EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS も過去互換のためにサポートされているが、非推奨)
subdir-ccflags-y, subdir-asflags-y
これが記載されているMakefileとそのサブディレクトリで有効。
CFLAGS_$@, AFLAGS_$@
はファイル単位で追加したいオプションを指定する。
例) CFLAGS_foo.o := -D BAR
CPPFLAGS_$@ はないみたい。
CFLAGS_REMOVE_$@
でファイル単位で削除したいオプションを指定する。
AFLAGS_REMOVE_$@, CPPFLAGS_REMOVE_$@ はないみたい。。
このあたりのオプションがどのように実装されているか、見てみた。
コンパイルオプションはほとんど scripts/Makefile.lib の中で加工されている。
scripts/Makefile.lib は scripts/Makefile.build からインクルードされている。
まず、EXTRA_* の過去互換性のためのルールがある。
# Backward compatibility
asflags-y += $(EXTRA_AFLAGS)
ccflags-y += $(EXTRA_CFLAGS)
cppflags-y += $(EXTRA_CPPFLAGS)
ldflags-y += $(EXTRA_LDFLAGS)
また、サブディレクトリにも伝搬するように、
$(subdir-asflags-y), $(subdir-ccflags-y) は
KBUILD_SUBDIR_ASFLAGS, KBUILD_SUBDIR_CCFLAGS にいれて export している。
# flags that take effect in sub directories
export KBUILD_SUBDIR_ASFLAGS := $(KBUILD_SUBDIR_ASFLAGS) $(subdir-asflags-y)
export KBUILD_SUBDIR_CCFLAGS := $(KBUILD_SUBDIR_CCFLAGS) $(subdir-ccflags-y)
以下の部分で、_c_flags, _a_flags, _cpp_flags にまとめます。
KBUILD_CPPFLAGS, KBUILD_CFLAGS, KBUILD_AFLAGS は トップのMakefileとarch/*/Makefileで規定されていて、
make 全体に影響を与えます。
orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \
$(ccflags-y) $(CFLAGS_$(basetarget).o)
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(KBUILD_SUBDIR_ASFLAGS) \
$(asflags-y) $(AFLAGS_$(basetarget).o)
_cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(@F))
さらに、KBUILD_SRC が指定されている(つまり O=, M= が指定されている)ならば、インクルードパスの加工を行う。
KBUILD_SRC が指定されてなければ、 _*_flags がそのまま __*_flags になる。
ifeq ($(KBUILD_SRC),)
__c_flags = $(_c_flags)
__a_flags = $(_a_flags)
__cpp_flags = $(_cpp_flags)
else
# -I$(obj) locates generated .h files
# $(call addtree,-I$(obj)) locates .h files in srctree, from generated .c files
# and locates generated .h files
# FIXME: Replace both with specific CFLAGS* statements in the makefiles
__c_flags = $(call addtree,-I$(obj)) $(call flags,_c_flags)
__a_flags = $(call flags,_a_flags)
__cpp_flags = $(call flags,_cpp_flags)
endif
これから c_flags, a_flags, cpp_flags, ld_flags が作られる。
c_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(__c_flags) $(modkern_cflags) \
-D"KBUILD_STR(s)=\#s" $(basename_flags) $(modname_flags)
a_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(__a_flags) $(modkern_aflags)
cpp_flags = -Wp,-MD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(__cpp_flags)
ld_flags = $(LDFLAGS) $(ldflags-y)
-Wp,-MD,$(depfile) は依存関係自動生成に使うプリプロセッサ用にオプションですね。
ようやく、これが scripts/Makefile.build のパターンルールの中で使われます。
%.c → %.o には
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<
%.S → %.o には
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $<
%.c → %.s には
cmd_cc_s_c = $(CC) $(c_flags) -fverbose-asm -S -o $@ $<
%.c → %.i には
cmd_cc_i_c = $(CPP) $(c_flags) -o $@ $<
%.S → %.s には
cmd_as_s_S = $(CPP) $(a_flags) -o $@ $<
%.lds.S → %.lds には
cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -C -U$(ARCH) \
-D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
という風に使われるようです。
ところで、cppflags-y や EXTRA_CPPFLAGS って Makefile.lib には用意されているのですが、ほとんど使われている形跡がないですね。
-Dmacro や -Idir などはプリプロセッサオプションなので、cppflags-y に追加すべきと思うのですが、もっぱら ccflags-y に方に追加されているようです。
もう一つ気になるのは用語の非統一感です。
ccflags-y, EXTRA_CFLAGS, CFLAGS_$@, CFLAGS_REMOVE_$@
→ なんで cflags-y じゃなくて ccflags-y にしたの?
asflags-y, AFLAGS_$@
→ なんで ASFLAGS_$@ じゃなくて AFLAGS_$@ なの?
make でよく使われるのは CFLAGS, CPPFLAGS, ASFLAGS, LDFLAGS だと思うんですが。。(make --print-data-base で確認)
2012年6月9日土曜日
2012年6月8日金曜日
Gas の .align ディレクティブ
GNU Assembler の .align ディレクティブについて。
例えば、4バイトアラインするときに
.align 2 と書いている人と .align 4 と書いてる人がいて、どっちが正しいんだ?と思ったので調べてみた。
The GNU Assembler のドキュメントを参照すると、、、
The way the required alignment is specified varies from system to system.
For the a29k, hppa, m68k, m88k, w65, sparc, Xtensa, and Renesas / SuperH SH, and i386 using ELF format, the first expression is the alignment request in bytes.
For example .align 8 advances the location counter until it is a multiple of 8.
If the location counter is already a multiple of 8, no change is needed.
For other systems, including the i386 using a.out format, and the arm and strongarm, it is the number of low-order zero bits the location counter must have after advancement.
For example .align 3 advances the location counter until it a multiple of 8.
If the location counter is already a multiple of 8, no change is needed.
なんだそうだ。
例えば i386 (ELF) は
.align 2 → 2バイトアライン
.align 4 → 4バイトアライン
.align 8 → 8バイトアライン
となるのに対し、
ARMは
.align 1 → 2バイトアライン
.align 2 → 4バイトアライン
.align 3 → 8バイトアライン
となるらしい。
こりゃ気をつけな間違えるな~。
というわけで、
.balign を使えば、システムに依存せずに、
.balign 2 → 2バイトアライン
.balign 4 → 4バイトアライン
.balign 8 → 8バイトアライン
となるみたいだ。
例えば、4バイトアラインするときに
.align 2 と書いている人と .align 4 と書いてる人がいて、どっちが正しいんだ?と思ったので調べてみた。
The GNU Assembler のドキュメントを参照すると、、、
The way the required alignment is specified varies from system to system.
For the a29k, hppa, m68k, m88k, w65, sparc, Xtensa, and Renesas / SuperH SH, and i386 using ELF format, the first expression is the alignment request in bytes.
For example .align 8 advances the location counter until it is a multiple of 8.
If the location counter is already a multiple of 8, no change is needed.
For other systems, including the i386 using a.out format, and the arm and strongarm, it is the number of low-order zero bits the location counter must have after advancement.
For example .align 3 advances the location counter until it a multiple of 8.
If the location counter is already a multiple of 8, no change is needed.
なんだそうだ。
例えば i386 (ELF) は
.align 2 → 2バイトアライン
.align 4 → 4バイトアライン
.align 8 → 8バイトアライン
となるのに対し、
ARMは
.align 1 → 2バイトアライン
.align 2 → 4バイトアライン
.align 3 → 8バイトアライン
となるらしい。
こりゃ気をつけな間違えるな~。
というわけで、
.balign を使えば、システムに依存せずに、
.balign 2 → 2バイトアライン
.balign 4 → 4バイトアライン
.balign 8 → 8バイトアライン
となるみたいだ。
Linux ローダブルモジュールの Makefileで
LinuxのローダブルモジュールのMakefileでよくあるサンプル。
obj-m := foo.o
modules:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
みたいなやつ。
$(PWD) の部分は $(CURDIR) の方がいいですね。
再帰的make の中に組み込むと動かなくなってしまうので。
詳細理由は、一つ前の話題「make の -C オプションについて」で述べています。
というわけで、「Linuxデバイスドライバプログラミング」に載っているMakefileのサンプルはよくないですね。
O'REILLY の「Linuxデバイスドライバ」のサンプルの方は
PWD := $(shell pwd)
の1行が入っているのでOKです。
2012年6月7日木曜日
make の -C オプションについて
make の -C オプションについて。
man make を読むと、
(中略) このオプションは通常、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
(中略)
さらにちなみにだが、上の例で /home/foo/bar/Makefile の $(PWD) の部分を $$PWD に書き変えるとまた違う結果になる。
make の構築ルールはサブシェル上で実行されることを知っている人は多いだろう。
一方、
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
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 へと展開される。
登録:
投稿 (Atom)