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.cxxMakefile_NGMakefile_OKMakefile Makefile