Windowsのショートネーム変換の~sがバグってるのでGroovyServ0.9の起動に失敗する場合がある

事象

ふもさんが、WindowsXP上のJava1.7.0でGroovyServを使おうとしてハマッたという報告を受け、我々取材班オレは急遽調査を開始した*1

fumokmm: v0.9 on WindowsXP with jdk1.7.0 だと groovyclient が起動しないです。 JAVA_HOMEのパスがおかしいって言われます。 #groovyserv http://h.hatena.ne.jp/fumokmm/225868361023164635 #hatena819
http://twitter.com/#!/fumokmm/status/108039184668819456

しかも、わざわざ英語でMLに投げていただいたりして、どうもどうも。

https://groups.google.com/d/topic/groovyserv/_QSfpjQG_Vg/discussion

nemo_kazさんも英語で絡んでくれたりして、なんかインターナショナルですね!(日本人同士ですけど)


さて、エラーメッセージなんですが、こんな↓のでした。

ERROR: JAVA_HOME is set to an invalid directory: c:\PROGRA~1\Java\JDK17~1.0.7.0 
Please set the JAVA_HOME variable in your environment 
to match the location of your Java installation. 

このメッセージ、GroovyServのソースにgrepしてもヒットしません。というか、Gradleラッパスクリプトのgradlew, gradlew.batに見つかったんですけど、これはビルドファイルですからね、groovyclient実行時にこれが使われるわけがない。

じゃあ、Groovyかな、と手っ取り早くアクセスできたMac上のHomebrewでインストールしたGroovy配下でgrepしてみたんですけど、該当するファイルが見つからず。

えー、じゃあGradleなん?なんでなん?と若干パニックになったんですけども、それが昨日。


今日、改めてgroovyのソースコード配下で調べてみたら、ありましたよ。
このエラーメッセージ、startGroovy.batが出してました。
startGroovy(sh)とはメッセージが違うとかなにそれひどい。

とまあ、エラーメッセージの所在が分かれば、結局事象としては、

JAVA_HOME=c:\Program Files\java\jdk1.7.0

と指定してるのに、ショートネーム変換によって、

c:\PROGRA~1\Java\JDK17~1.0.7.0

になってて、お前ホントは

c:\PROGRA~1\Java\JDK17~1.0

じゃねーの?*2 末尾の".7.0"ってナニよ、という話。
当然そんなディレクトリはないのでstartGroovy.batがコケてた、という。

解析

さて、私の環境で

> set JAVA_HOME=C:\Program Files\java\jdk1.7.0
> groovyclient.exe -v

をやってみました。

とりあえず、

C:\Program Files\java\jdk1.7.0

ファイルは存在しない状態でやってみると、

ERROR: JAVA_HOME is set to an invalid directory: C:\PROGRA~1\Java\jdk1.7.0
Please set the JAVA_HOME variable in your environment
to match the location of your Java installation.

となり、ショートネーム自体になりません。アレ、そういうもんなの?

今度は

C:\Program Files\java\jdk1.7.0

ディレクトリを作成してから試してみると...

ERROR: JAVA_HOME is set to an invalid directory: C:\PROGRA~1\Java\JDK17~1.0.7.0
Please set the JAVA_HOME variable in your environment
to match the location of your Java installation.

再現した!!!! 何コレ。

うーん。というか、jdk1.7.0の"7.0"が拡張子っぽく扱われてるとか?

というわけで、ググッてみたら、これっぽい気がする...

本来は、「\LONGLO~1\ABBE64~1.TXT」 のように表示されるべき部分が、「\LONGLO~1\ABBE64~1.TXTB.txt」 のように後ろにごみ(LFNの残骸)が付いてしまう。 こういうバグがある以上、特定の環境で一時的に使うバッチならともかく、汎用ツールを作る時は ~s 修飾子を安易に使えないと言うことだ。
http://otnx.jp/CMD/?%A5%D0%A5%B0#geb5e062

でも、実体も存在する、拡張子っぽくない(ピリオドを含まない)名前のディレクトリで、かつそれ自体も長い名前だと、

> set JAVA_HOME=C:\Program Files\java\hogefoobar_longlongname
> groovyclient.exe -v
ERROR: No java.exe found at: C:\PROGRA~1\Java\HOGEFO~1\bin\java.exe
Please set the JAVA_HOME variable in your environment
to match the location of your Java installation.

のように、HOGEFO~1とショートネーム変換は成功したりしてる。

よくわからん。ひどすぎる。
Windowsェ....

とりあえずの回避策

とりあえず、ふもさんが

fumokmm: @nobeans メーリングリストに慣れない英語で追加情報を投稿しておきましま。
なんだかショートネームのアルゴリズムっぽいですね…。パスjdk7の後ろに無理やり_01とか付けたら動きました。_xxありきのアルゴリズムなんですかね?よろしくお願いします。
http://twitter.com/#!/fumokmm/status/108363229012570112

と書かれたように、末尾に違う文字を付けて拡張子と誤解されないようにすると、ショートネームの誤変換が回避できるようです。

今後の展開

うーん。ショートネームを使うこと自体が問題な気がしてきたなぁ。
空白を含むパスでもうまく動くように良かれと思って変換してたのだけど。

JAVA_HOMEに関しては正直GroovyServ自体では使ってなくて、そのままGroovyにスルーしてしまって良いので、ショートネーム変換をあきらめるというのはアリなのだれど、groovyコマンドパス(ローカルのGROOVY_BIN変数)については、startコマンドに食わせる必要があるので(startはダブルクォートをつけるとNGという超う○こ仕様)、空白を含むパスは厳禁なのだ。なのでそこにはショートネーム変換が必要(もしくはstartコマンドを使わない常駐的起動方法 or 他)。

空白含むパスにおくなよ、という制約を付ければいいのかもしれないけど、Windowsだと標準のProgram Filesからしてアレだからなぁ。

ああ、困った困った。

誰か、素敵なアイデア(できればパッチ付きで)をください。

*1:全然"急遽"じゃないですけど

*2:この変換自体よくわからなんけども