Makefile
の内容が反映されない(デフォルトルールがジャマをするケース)
次のような症状が見られる場合,make
に内蔵されているデフォルトのルールがジャマをしている可能性があります.
(P1) | Makefile に書いたのと違うコマンドが実行されてしまう |
(P2) | Makefile でビルド方法を指定しているのに無視されてしまう |
(P3) | 他のOSからもってきたMakefile が(P1)(P2)のような症状を起こす |
この文書では,マイクロソフトnmake
を使っていて上記のような問題が生じた場合のトラブルシュートの手順を解説します.
なんかおかしいと思ったら“nmake -p
”とやってみよう.
たとえば,Windowsで(Microsoft製nmake
とcl.exe
を使って)
C++プログラムhello.cxx
をビルドする例を考えます.
Makefile_NG |
---|
0001 .SUFFIXES: 0002 .SUFFIXES: .obj .cxx 0003 0004 all: hello.exe 0005 0006 .obj.exe: 0007 $(CC) $(CFLAGS) $< 0008 0009 .cxx.obj: 0010 $(CC) $(CFLAGS) /EHsc /c $< |
ビルドの手順は,2段階でhello.cxx
をコンパイルしてhello.obj
をつくり(0006〜0007行),
その後hello.obj
をリンクしてhello.exe
をつくる(0009〜0010行)ようにしています.
この例ではソースがひとつしかないのでhello.cxx
から直接hello.exe
をつくってもいいのですけど,
一般的には複数のソースコードを分割コンパイルしてオブジェクトファイルをつくっておいて,
最後にリンクして実行可能ファイルをつくることになるので,わざと2段階にわけることにしました(この2段にわける部分がトリックになっています).
また,普通にオプションなしで“cl hello.cxx
”とコンパイルすると例外処理関係の警告がドバーっとでてうるさいので
(マイクロソフトの勧めにしたがって)/EHsc
オプションを指定しています.
擬似ターゲット.SUFFIXES
も変更して
(0001行目でデフォルトの検索優先順位を削除,0002行で.obj
→.cxx
の順を登録),
普通に考えればこのMakefile_NG
で問題ないように思えます
(というか,実際このメイクファイルで問題なく動作する環境もあるはずです).
しかし,実際にやってみると/EHsc
を指定したはずなのに効果なく,件の警告がドバーっとでてしまいます.
hello.exe
はできていますが,Makefile
にかいたのとは違う方法でビルドされていますので,これではダメです
(このシンプルな例では結果オーライですけど,一般的にはMakefile
に指定したコンパイル時オプションなどが ちゃんと適用されていないとダメです).
いったん,hello.ext
とhello.obj
を削除してから,“nmake -n
”とやってみると次のように表示されるはずです
(-n
はMakefile
を解析して実行すべきコマンド群を表示するけど実際のコマンドの実行は行わないオプション).
Makefile_NG
を素直に読むと,“cl /EHsc /c hello.cxx
”→“cl hello.obj
”の順で実行されそうなのに,
マイクロソフトのnmake
はMakefile_NG
のどこにも書いてない“cl hello.cxx
”というコマンドを選んでいます.
> del hello.exe hello.obj > make -n -f Makefile_NG Microsoft(R) Program Maintenance Utility Version 8.00.50727.42 Copyright (C) Microsoft Corporation. All rights reserved. cl hello.cxx
どうしてこうなってしまったかは,“nmake -n -p
”とするとわかります.
[-p
オプションは,nmake
がビルド手順を推論するときに利用した各種情報を表示するオプションです.
make
/Makefile
関係のトラブルはたいていこの-p
オプションを使えば原因がわかるので,覚えておくと便利です.]
nmake -n -p -f Makefile_NG
を実行すると何やらドバーっと表示されると思いますが,要約すると次の1007〜1008行目の部分(最後のほうに表示される部分)が問題です.
1001 .obj.exe: 1002 $(CC) $(CFLAGS) $< 1003 1004 .cxx.obj: 1005 $(CC) $(CFLAGS) /EHsc /c $< 1006 1007 .cxx.exe: 1008 $(CXX) $(CXXFLAGS) $< 1009 1010 .SUFFIXES: .obj .cxx 1011 1012 all: 1013 -n 1014 hello.exe
この“.cxx.exe
”というルールはMakefile_NG
には書いてありません.
このルールはマイクロソフト製nmake
が内部的にもっているデフォルトのルールでメイクファイルに書かなくても適用されてしまうのです.
その結果,nmake
は,
(I01) | hello.exe をつくりたい→hello.obj ,hello.cxx の順に検索 |
(I02) | hello.obj はないのでパス |
(I03) | hello.cxx はある→hello.cxx からhello.exe をつくることに決定 |
(I04) | .cxx.exe ルールを適用→cl hello.cxx を実行 |
のように,期待したのとは違うコマンドを実行することになってしまうのです.
[ここで,nmake -f Makefile_NG
を実行後,再ビルドしようと思って
もう1回nmake -a -f Makefile_NG
を実行すると初回とは違うコマンドが実行されることに注意してください.
というのは,2回目はカレントディレクトリにhello.obj
が存在しているので,(I02)のフェーズでhello.obj
が見つかり,
1回目とは違う推論パスが選ばれるからです.]
ここまでわかってしまえば回避方法は簡単で?,デフォルトルールの“.cxx.exe
”を迂回するように(直接.cxx
→.exe
とならないように),
.cxx
→.obj
→.exe
の順につくるんですよ〜と明示してあげます.
[GNU make
(gmake
)だとデフォルトルールの削除もできるのですけど,
マイクロソフトのnmake
ではデフォルトルールを削除する方法がないので迂回するしかありません.]
具体的には次のように,ひとつルールを追加します.
これで期待通りに動作するようになります.
Makefile_OK |
---|
0001 .SUFFIXES: 0002 .SUFFIXES: .obj .cxx 0003 0004 all: hello.exe A001 A002 hello.exe: hello.obj 0005 0006 .obj.exe: 0007 $(CC) $(CFLAGS) $< 0008 0009 .cxx.obj: 0010 $(CC) $(CFLAGS) /EHsc /c $< |
Makefile
&ソース パック expack.zip
hello.cxx
Makefile_NG
Makefile_OK
Makefile
Makefile