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 -k
make -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
VPATH
VPATH
サポートを指定していません.多くの
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