Makefileの内容が反映されない(デフォルトルールがジャマをするケース)

次のような症状が見られる場合,makeに内蔵されているデフォルトのルールがジャマをしている可能性があります.

(P1)Makefileに書いたのと違うコマンドが実行されてしまう
(P2)Makefileでビルド方法を指定しているのに無視されてしまう
(P3)他のOSからもってきたMakefileが(P1)(P2)のような症状を起こす

この文書では,マイクロソフトnmakeを使っていて上記のような問題が生じた場合のトラブルシュートの手順を解説します.

まとめ

なんかおかしいと思ったら“nmake -p”とやってみよう.

詳細

たとえば,Windowsで(Microsoft製nmakecl.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.exthello.objを削除してから,“nmake -n”とやってみると次のように表示されるはずです (-nMakefileを解析して実行すべきコマンド群を表示するけど実際のコマンドの実行は行わないオプション). Makefile_NGを素直に読むと,“cl /EHsc /c hello.cxx”→“cl hello.obj”の順で実行されそうなのに, マイクロソフトのnmakeMakefile_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.objhello.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 makegmake)だとデフォルトルールの削除もできるのですけど, マイクロソフトの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 $<

実験用リソース

資料


はたいたかし
2007-01-27
Home > Dev > make