2015年02月26日
WindowsでGNU makeを実行するとファイルが消えてしまう現象と対策
GNU make には、暗黙のルールの連鎖で中間ファイルを生成したら、自動的に削除するという仕様があるのですが、これと WIndows のファイルシステムの、ファイル名の大文字小文字を区別しないという仕様が組み合わさると、ファイルが削除されてしまうという現象があり、実際ハマったのでメモしておきます。
10.4 Chains of Implicit Rules
(make 3.79.1: 新堂安孝氏による日本語訳):10.4 暗黙のルールの連鎖
10.4 Chains of Implicit Rules
(make 3.79.1: 新堂安孝氏による日本語訳):10.4 暗黙のルールの連鎖
GCC には、拡張子 .S(大文字)のアセンブラソースは、プリプロセッサを通してからアセンブラに通すという仕様があります。
また、make には、何も関連ルールを Makefile に書かなければ、*.s(小文字)や *.S(大文字)から *.o を生成するという暗黙のルールがあります。
ここで、Makefile に、アセンブラソース *.s(小文字)から *.o を生成するというルール「のみ」を書いた場合に、問題が起こりました。ターゲットが例えば foo.o で、foo.s(小文字)が存在せず、foo.S(大文字)のファイルが存在すると、foo.S が削除されてしまいました。
これは、make が foo.o を生成するために、まずは明示されたルールに従い foo.s を探し、foo.s が存在しないので、改めて foo.o を生成するために foo.S を探すという挙動になるため、のようです。そして make は、foo.S から foo.o を直接生成したにも関わらず ※、なぜか foo.s を中間ファイルだと認識して削除してしまうようです。最終的に Windows では同名のファイルになるので、foo.S が削除されてしまいます。
もし同じようにファイルが消えてしまう現象に悩んだ場合は、make に -d(--debug)オプションを付けて実行し、ログに以下のような記述が無いかどうかをまずは確認してみると良いでしょう。
また、make には、何も関連ルールを Makefile に書かなければ、*.s(小文字)や *.S(大文字)から *.o を生成するという暗黙のルールがあります。
ここで、Makefile に、アセンブラソース *.s(小文字)から *.o を生成するというルール「のみ」を書いた場合に、問題が起こりました。ターゲットが例えば foo.o で、foo.s(小文字)が存在せず、foo.S(大文字)のファイルが存在すると、foo.S が削除されてしまいました。
これは、make が foo.o を生成するために、まずは明示されたルールに従い foo.s を探し、foo.s が存在しないので、改めて foo.o を生成するために foo.S を探すという挙動になるため、のようです。そして make は、foo.S から foo.o を直接生成したにも関わらず ※、なぜか foo.s を中間ファイルだと認識して削除してしまうようです。最終的に Windows では同名のファイルになるので、foo.S が削除されてしまいます。
(RCS や SCCS などは省略) Looking for a rule with intermediate file 'foo.s'. Avoiding implicit rule recursion. Trying pattern rule with stem 'foo'. Trying implicit prerequisite 'foo.S'. Found an implicit rule for 'Debug/foo.o'. Considering target file 'foo.s'. Considering target file 'foo.S'. Looking for an implicit rule for 'foo.S'. Trying pattern rule with stem 'foo.S'. Trying implicit prerequisite 'foo.S,v'. Trying pattern rule with stem 'foo.S'. Trying implicit prerequisite 's.foo.S'. No implicit rule found for 'foo.S'. Finished prerequisites of target file 'foo.S'. No need to remake target 'foo.S'. Finished prerequisites of target file 'foo.s'. Prerequisite 'foo.S' is older than target 'foo.s'. No need to remake target 'foo.s'. Finished prerequisites of target file 'Debug/foo.o'. Must remake target 'Debug/foo.o'.今回は、あまりスマートでは無いですが、とりあえず *.s と同じルールを *.S に対しても明示的に記述(コピペ)するという方法で解決しました。
もし同じようにファイルが消えてしまう現象に悩んだ場合は、make に -d(--debug)オプションを付けて実行し、ログに以下のような記述が無いかどうかをまずは確認してみると良いでしょう。
Removing intermediate files... rm foo.s※ ここらへんの挙動はログを見てもよくわかりません。拡張子 s のファイルが突然どこかから出てきてアセンブルされます。どうもこれも、Windows 上では拡張子 S も s も同じファイルだと認識される仕様の影響のような気がします。