make自身には非常に多くの制限があるので苦労します,ここではわず かですが紹介します.とにかく,シェルによってコマンドが実行されるので,そ の弱い部分の全てが継承されていくということを覚えておいてください ....
$< $ cat Makefile
_am_include = #
_am_quote =
all:; @echo this is test
$ make
Make: Must be a separator on rules line 2. Stop.
$ cat Makefile2
am_include = #
am_quote =
all:; @echo this is test
$ make -f Makefile2
this is test
FOO = one \
BAR = two
test:
: FOO is "$(FOO)"
: BAR is "$(BAR)"
FOOはone BAR = twoと等価です.それ以外のmakeでは,
バックスラッシュは直後の行だけを含みます.
#ではじまり,
エスケープされていない改行まで続きます.
% cat Makefile
# A = foo \
bar \
baz
all:
@echo ok
% make # GNU make
ok
しかし現実では,これは常にそうではありません.実装によっては,#
から行末までを廃棄し,後置されるバックスラッシュを無視するものもあります.
% pmake # BSD make
"Makefile", line 3: Need an operator
Fatal errors encountered -- cannot continue
このため,複数行の定義をコメントアウトしたい場合,最初の行だけでなく,そ
れぞれの行に# を前置してください.
# A = foo \
# bar \
# baz
make macro=valueとサブ呼び出しのmakefoo=barのような定義は,Makefileの
fooの定義に優先します.(GNU makeのような)
makeの実装によっては,この優先はサブ呼び出しのmake
に伝搬します.古い実装によっては,それ以下のmakeに代入を渡さな
いものもあります.
% cat Makefile
foo = foo
one:
@echo $(foo)
$(MAKE) two
two:
@echo $(foo)
% make foo=bar # GNU make 3.79.1
bar
make two
make[1]: Entering directory `/home/adl'
bar
make[1]: Leaving directory `/home/adl'
% pmake foo=bar # BSD make
bar
pmake two
foo
サブ呼び出しのmakeにfoo=barの優先を伝搬したい場合,移植
性を持たせる方法が無いわけではありません.その一つは,すべての環境変数を
Makefileマクロ定義に優先させる-eオプションを使用し,
fooを環境変数として定義する方法です.
% env foo=bar make -e
-eオプションは,自動的にサブ呼び出しのmakeに伝搬し,環
境変数はmakeの呼び出し間で継承されるので,fooマクロはサ
ブ呼び出しのmakeで期待したように優先されます.
この構文(foo=bar make -e)は,Makefileの外で使用するときだ
け,例えば,スクリプトやコマンドラインのときだけ移植性があります.
makeルール内で実行するとき,GNU make 3.80とそれ以前
のバージョンは,それ以下でのmakeに-eオプションを伝搬さ
せることを忘れています.
更に-eを使用することで,Makefileで通常定義されるその他のマ
クロが環境変数に含まれている場合,予期しない副作用があるかもしれません.
(以下のmake -eとSHELLの注意も参照してください.)
サブ呼び出しのmakeに優先物を伝搬させるもう一つの方法は, Makefileに手動で行なうことです.
foo = foo
one:
@echo $(foo)
$(MAKE) foo=$(foo) two
two:
@echo $(foo)
そうする場合,ユーザが優先したいと思われるすべてのマクロを予測する必要が
あります.
SHELLマクロ$(SHELL)マクロを使用
します.これはmakeで提供される組み込みマクロですが,
Makefileやコマンドライン引数で変更することが可能です.
すべてのmakeが,このSHELLマクロを定義するわけではありま
せん.例えば,OSF/Tru64 makeがそうです.この実装では,常に
/bin/shを使用します.そのため,Makefileで常にSHELL
を定義するのは良い考えです.Autoconfを使用している場合,以下のようにして
ください.
SHELL = @SHELL@
POSIX準拠のmakeでは,make -eが使用されている
場合でも,環境変数から$(SHELL)の値を入手してはなりません(そうでない場合,
SHELL=/bin/tcshの状況でルールによって何が起こるのか考えてみてくだ
さい).
しかし,すべてのmakeがこのような例外を実装しているわけではあり
ません.例えば,OSF/Tru64 makeはSHELLを使用しないので,
保護していなくても不思議ではありません.
% cat Makefile
SHELL = /bin/sh
FOO = foo
all:
@echo $(SHELL)
@echo $(FOO)
% env SHELL=/bin/tcsh FOO=bar make -e # OSF1 V4.0 Make
/bin/tcsh
bar
% env SHELL=/bin/tcsh FOO=bar gmake -e # GNU make
/bin/sh
bar
タブで始まるものは,タブの直後に#が続いていても,すべて現在のルー
ルのコマンドとして扱うmakeもあります.Tru64 Unix V5.1の
makeはその一つです.以下のMakefileで,シェルで#
fooを実行します.
all:
# foo
obj/ディレクトリが存在する場合,BSD makeは Makefileを読み込む前に,そのなかに入ります.このため,現在のディ レクトリのMakefileは読み込まれません.
% cat Makefile
all:
echo Hello
% cat obj/Makefile
all:
echo World
% make # GNU make
echo Hello
Hello
% pmake # BSD make
echo World
World
make -kmake -kの終了ステータスに依存しないようにしてください.終了ステー
タスがエラーかどうかを反映する実装もあります.それ以外の実装では,常に成
功します.
% cat Makefile
all:
false
% make -k; echo exit status: $? # GNU make
false
make: *** [all] Error 1
exit status: 2
% pmake -k; echo exit status: $? # BSD make
false
*** Error code 1 (continuing)
exit status: 0
VPATHVPATHサポートを指定していません.多くの
makeはVPATHサポートの形式がありますが,その実装は,
make間で一貫していません.
VPATH機能を必要としている人々への最高の提案は,makeの実
装を選択しそれに固執するようにと言うことかもしれません.Makefile
の結果は常に移植性があるとは限らないので,移植性の高いmakeを選
択するのが良いでしょう(ヒント,ヒント).
VPATHの実装の既知の問題には以下のものがあります.
VPATHと二重のコロンのルールVPATHへの代入で,Sunのmakeは最初の二重コロンのルールの
組だけを実行します.(このコメントは,1994年からで,現在は無くなっていま
す.SunOS 4では移植性があります.これが再生成された場合,それを説明する
テストケースを送ってください.)
$<がサポートされていない$<を使用するのは移植性があり
ません.必要条件のファイルは,ルール内で明示的な名前にすべきです.
VPATHの検索で必要条件を見つけたい場合,手動でコード全体を書く必要
があります.例えば,以下のようなパターンを使用します.
VPATH = ../src
foo.o: foo.c
cc -c `test -f foo.c || echo ../src/`foo.c -o foo.o
VPATHで必要条件を探し,出現するたび
に適切なルールにを再書き込みするmakeの実装もあります.
例えば,以下を考えます.
VPATH = ../src
foo.o: foo.c
cc -c foo.c -o foo.o
foo.cが../srcで見つかった場合,cc -c ../src/foo.c -o
foo.oを実行します.素晴らしいと思います.
しかし,それ以外のmakeの実装では,これに依存することは不可能で,
VPATHを手動で検索する必要があります.
VPATH = ../src
foo.o: foo.c
cc -c `test -f foo.c || echo ../src/`foo.c -o foo.o
しかし"必要条件の再書き込み"はこれに適用されます.そのため, ../srcにfoo.cがある場合,SunOSのmakeは以下を実行 します.
cc -c `test -f ../src/foo.c || echo ../src/`foo.c -o foo.o
以下を生成します.
cc -c foo.c -o foo.o
そしてこのために失敗します.あぁ.
回避策の一つは,ルールのなかにfoo.cをそのまま書いていないことを確 かめることです.例えば,以下の三つのルールは安全です.
VPATH = ../src
foo.o: foo.c
cc -c `test -f ./foo.c || echo ../src/`foo.c -o foo.o
foo2.o: foo2.c
cc -c `test -f 'foo2.c' || echo ../src/`foo2.c -o foo2.o
foo3.o: foo3.c
cc -c `test -f "foo3.c" || echo ../src/`foo3.c -o foo3.o
必要条件がマクロ内にあるとき,事態はより悪くなります.
VPATH = ../src
HEADERS = foo.h foo2.h foo3.h
install-HEADERS: $(HEADERS)
for i in $(HEADERS); do \
$(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
$(DESTDIR)$(includedir)/$$i; \
done
上記のinstall-HEADERSルールは,for i in $(HEADERS);は
for i in foo.h foo2.h foo3.h;に展開され,foo.hと
foo2.hはそのまま単語となり,このためサブジェクトはVPATH に
調整されるので,SunOSでは信頼できません.
三つのファイルが../srcにある場合,このルールは以下のように実行さ れます.
for i in ../src/foo.h ../src/foo2.h foo3.h; do \
install -m 644 `test -f $i || echo ../src/`$i \
/usr/local/include/$i; \
done
最初の二つのinstallの呼び出しは失敗します.例えば,
foo.hをインストールする事を考えます.
install -m 644 `test -f ../src/foo.h || echo ../src/`../src/foo.h \
/usr/local/include/../src/foo.h;
以下を生成します.
install -m 644 ../src/foo.h /usr/local/include/../src/foo.h;
手動のVPATHの検索には問題が無いことに注意してください.しかし,こ
のコマンドは,間違ったディレクトリにfoo.hをインストールします.
ここまで,いくつかのMakefileでfoo.cに対して行なってきた,
$(HEADERS)をどうにかして引用符で囲むことは役に立ちません.
install-HEADERS: $(HEADERS)
headers='$(HEADERS)'; for i in $$headers; do \
$(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
$(DESTDIR)$(includedir)/$$i; \
done
実際,headers='$(HEADERS)'はheaders='foo.h foo2.h foo3.h'
に展開され,foo2.hはそのまま単語になります.(一方,
headers='$(HEADERS)'; for i in $$headers;の慣用句は,for i
in;で構文エラーになるシェルもあるので,$(HEADERS)が空の場合は良
い考えです.)
回避方法の一つは,不要な../src/の接頭辞を手動で削除する事です.
VPATH = ../src
HEADERS = foo.h foo2.h foo3.h
install-HEADERS: $(HEADERS)
headers='$(HEADERS)'; for i in $$headers; do \
i=`expr "$$i" : '../src/\(.*\)'`;
$(INSTALL) -m 644 `test -f $$i || echo ../src/`$$i \
$(DESTDIR)$(includedir)/$$i; \
done
Automakeも同様なことを行ないます.
VPATHのサブディレクトリにある場合,Tru64 make
はそれを現在のディレクトリに作成します.
% mkdir -p foo/bar build
% cd build
% cat >Makefile <<END
VPATH = ..
all: foo/bar
END
% make
mkdir foo
mkdir foo/bar
ルールは,前に存在している手動のVPATH検索を使用するので,これは予
想外の結果になるはずです.
VPATH = ..
all : foo/bar
command `test -d foo/bar || echo ../`foo/bar
上記のcommandは,現在のディレクトリに作成された,空の
foo/barディレクトリで実行されます.
VPATHで見つかったファイルを使用す
べきとき決定するアルゴリズムは幾分複雑です.See How Directory Searches are Performed.
ターゲットのリビルドが必要な場合,GNU makeは,このター
ゲットをVPATHで検索している間に見つかったファイル名を廃棄し,
Makefileで与えられたファイル名を使用して,ローカルなファイルをビ
ルドします.ターゲットをリビルドする必要が無い場合は,GNU
makeはVPATHで検索している間に見つかったファイル名を使用
します.
NetBSD makeのような,その他のmakeの実装は,より簡単
に記述できます.VPATHで検索している間に見つかったファイル名は,ター
ゲットがリビルドを必要としているかどうかにかかわらず使用されます.このた
め<新しいファイルはローカルに作成されますが,VPATHに位置する既存
のファイルは更新されます.
しかし,OpenBSDとFreeBSDのmakeは,明示的なルールを持つ依存性を
探すためにVPATHを実行しません.これは非常にイライラします.
VPATHで,autoconfパッケージのビルドを試みるとき(例えば,
mkdir build && cd build && ../configure),GNU
make makeはbuildディレクトリですべてのローカ
ルにビルドしますが,BSD makeはローカルな新しいファイ
ルをビルドし,ソースディレクトリの既存のファイルを更新する事を意味します.
% cat Makefile
VPATH = ..
all: foo.x bar.x
foo.x bar.x: newer.x
@echo Building $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
Building foo.x
Building bar.x
% pmake # NetBSD make
Building foo.x
Building ../bar.x
% fmake # FreeBSD make, OpenBSD make
Building foo.x
Building bar.x
% tmake # Tru64 make
Building foo.x
Building bar.x
% touch ../bar.x
% make # GNU make
Building foo.x
% pmake # NetBSD make
Building foo.x
% fmake # FreeBSD make, OpenBSD make
Building foo.x
Building bar.x
% tmake # Tru64 make
Building foo.x
Building bar.x
NetBSDのmakeが../bar.xをVPATHのある場所で更新し, FreeBSD,OpenBSD,そしてTru64のmakeは,../bar.xが最新の ときでも,常にbar.xを更新することに注意して下さい.
言及する価値のあるもう一つの点は,GNU makeが一度
VPATHのファイル名を無視する事に決めると(例えば,上記の例の
../bar.xを無視する),ターゲットが他のルールの必要条件になったとき,
それを無視し続けます.
以下の例では,bar.x: newer.xのルールを実行している間に
bar.x のVPATHの結果を無視するので,.x.yのルールを実
行する前に,GNU makeはVPATHのbar.xを探
さない事を示しています.
% cat Makefile
VPATH = ..
all: bar.y
bar.x: newer.x
@echo Building $@
.SUFFIXES: .x .y
.x.y:
cp $< $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
Building bar.x
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
make: *** [bar.y] Error 1
% pmake # NetBSD make
Building ../bar.x
cp ../bar.x bar.y
% rm bar.y
% fmake # FreeBSD make, OpenBSD make
echo Building bar.x
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
*** Error code 1
% tmake # Tru64 make
Building bar.x
cp: bar.x: No such file or directory
*** Exit 1
bar.x: newer.xルールからコマンドを削除した場合,GNU
makeでは手品のように動作し始める事に注意してください.それは,
bar.xが更新されていない事を知っているので,VPATH
(../bar.x)の結果がうまく使用できるという結果を廃棄しません.Tru64
でも動作しますが,FreeBSDとOpenBSDではまだそうではありません.
% cat Makefile
VPATH = ..
all: bar.y
bar.x: newer.x
.SUFFIXES: .x .y
.x.y:
cp $< $@
% touch ../bar.x
% touch ../newer.x
% make # GNU make
cp ../bar.x bar.y
% rm bar.y
% pmake # NetBSD make
cp ../bar.x bar.y
% rm bar.y
% fmake # FreeBSD make, OpenBSD make
cp bar.x bar.y
cp: cannot stat `bar.x': No such file or directory
*** Error code 1
% tmake # True64 make
cp ../bar.x bar.y
すべてのmakeの実装がVPATHによるダーゲットの検索に依存し
ないようにお願いするのが,唯一の解決方法だと思います.言い替えると,
VPATHはビルドされていないソースへの予約にすべきです.
分離された依存性(Separated dependencies)は,ルールを定義すること無 く,ターゲットの必要条件のリストを単純に参照します.通常,一方ではルール を,もう一方で依存性をリストアップすることが可能です.
Solarisのmakeは,単一のサフィックスルールで定義されたターゲッ トに対する,分離された依存性をサポートしていません.
$ cat Makefile
.SUFFIXES: .in
foo: foo.in
.in:
cp $< $ $ touch foo.in
$ make
$ ls
Makefile foo.in
一方GNU Makeはサポートしています.
$ gmake
cp foo.in foo
$ ls
Makefile foo foo.in
それは`foo: foo.in'の依存性無しで動作することに注意してください.
$ cat Makefile
.SUFFIXES: .in
.in:
cp $< $ $ make foo
cp foo.in foo
そして,それは二重のサフィックスの継承ルールで動作することにも注意してく ださい.
$ cat Makefile
foo.out: foo.in
.SUFFIXES: .in .out
.in.out:
cp $< $ $ make
cp foo.in foo.out
結果として,そのような状況では,ターゲットルールを書く必要があります.
`cp -p'と `touch -r'のようなコマンドでは,通常はファイルのタイ ムスタンプを完全な分解能でコピーしません(see Limitations of Usual Tools).このため,以下のようなルールは気を付けるべきです.
dest: src
cp -p src dest
それは,タイムスタンプの切り詰め後では,srcよりもdestが古 くなり,このためmakeは次回も不必要な仕事を再び行なうはずです. この問題を回避するため,タイムスタンプファイルを使用することが可能です. 例えば以下のようにします.
dest-stamp: src
cp -p src dest
date >dest-stamp