忘れがち、OS Xの標準のファイルシステムは大文字小文字を区別しない
ちょっと前にMacPortsのチケットで問い合わせがきてた(#45257 (ruby20: warnings from library files that differ only by case of filename) – MacPorts)ものについて、慣れてるとすぐあたりがつくんだけど、気付かないとだいぶイミフなのでメモ残しとこう。
これはMacPorts固有の現象でもないので、10.9 Mevericksに添付のrubyで試してみよう。
% irb -w --simple-prompt >> RUBY_VERSION => "2.0.0" >> require 'Digest' => true >> Digest::MD5.digest("!") /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/digest.rb:4: warning: method redefined; discarding old const_missing /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/Digest.rb:4: warning: previous definition of const_missing was here /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/digest.rb:28: warning: method redefined; discarding old file /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/Digest.rb:28: warning: previous definition of file was here :
定数が上書きされてるよ!という警告がでてきてしまう。これはなぜ?という話。もちろん実際にはファイルは小文字の名前の"digest.rb"しかない。
requireされたファイルを見てみると、確かに"digest.rb"と"Digest.rb"のふたつの名前が読み込まれている。これは実際には同じファイルだ。
>> $LOADED_FEATURES.grep(/digest/i) => ["/System/Library/.../lib/ruby/2.0.0/universal-darwin13/digest.bundle", "/System/Library/Frameworks/.../lib/ruby/2.0.0/Digest.rb", # *D*igest "/System/Library/Frameworks/.../lib/ruby/2.0.0/digest.rb", # *d*igest "/System/Library/.../lib/ruby/2.0.0/universal-darwin13/digest/md5.bundle"]
原因はOS Xの標準のファイルシステム、HFS+がファイル名の大文字小文字を区別しないことだ。
1. require "Digest" → $LOAD_PATHから"Digest.(rb|bundle)"を探すと(詳細はるりまのKernel#require参照)、ファイルシステムは大文字小文字を区別しないので「"Digest.rb"があるよ」と答える → digest.rbが読み込まれ、"Digest.rb"として$LOADED_FEATURESに記録される。
2. Digest::MD5したとき、const_missingの処理の中で`require "digest"`される。"digest.rb"はまだ$LOADED_FEATURESの中にはないので、同じ"digest.rb"が読み込まれる。
という流れみたい。今回は警告メッセージ出すようにしてたから気付けたけれど、ファイル名の大文字小文字がちがってても動いてしまうので、そのまま他の環境に持っていくと動かない。ということもあるので注意。
Mac用のアプリでもこのへんわりとぐだぐだで、ファイルシステムを大文字小文字区別するHFSXに変えると動かないものがあるというのは時々聞く話。でも個人的にはそろそろ標準をHFSXにしてほしいなあと思う。