leap motionをsbtで動かすときにハマったこと

(2013-08-29 追記)

leap motionを買ったのはいいけど放置してたので弄ってみようと思ったのですが、 sbtですんなり動いてくれなかったので備忘録。 sbtに起因した問題だったので、JavaでもScalaでも発生します。

セットアップ

一応leap motion自体のセットアップの方法もメモしておきます。 OSはMacOSXを想定してます。

  1. 公式サイトからSDKを落としてくる
  2. 落としたSDKを適当な場所に展開する (自分は/Library/LeapDeveloperKit/LeapSDKに配置しました`)
  3. ライブラリのパスを通す(~/.zshrcとかに書き加えてください。ちなみにsudoすると警告が出るようになりますが、OSXのバグのようです)
export DYLD_LIBRARY_PATH=/Library/LeapDeveloperKit/LeapSDK/lib:$DYLD_LIBRARY_PATH

以上で基本的なセットアップは終わり。

sbtでサンプルを動かす

Javaのサンプルは/LeapDeveloperKit/LeapSDK/samples/Sample.javaです。 sbtプロジェクトのlibにLeapJava.jarを突っ込んで、適当にbuild.sbtを書いて実行すればおk。 が、以下の問題が発生します。

java.lang.UnsatisfiedLinkErrorの発生

初回の実行では発生しないのですが、2回目以降の実行で以下のようなエラーが発生します。

Native code library failed to load.
java.lang.UnsatisfiedLinkError: Native Library /Library/LeapDeveloperKit/LeapSDK/lib/libLeapJava.dylib already loaded in another classloader
java.lang.RuntimeException: Nonzero exit code: 1
    at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last compile:run for the full output.
[error] (compile:run) Nonzero exit code: 1
[error] Total time: 2 s, completed 2013/08/26 2:59:26

以下の記事によると、sbtと実行するアプリケーションが同じJVM上に乗っていることによって発生する問題とのこと。

以下をbuild.sbtに追記して解決。

fork in run := true
標準入力が読めなくなる

通常の設定だと初回は動いていて、2回目以降はエラーが発生していたのですが、forkする設定にするとすぐに終了してしまう問題が発生します。Connectedのログが吐かれる前に終了してます。

> run
[info] Running com.github.akiomik.App
[info] Initialized
[info] Press Enter to quit...
[info] Exited

これはforkしたことにより標準入力が読み取れず、入力待ち状態を作っているSystem.in.readがすぐに終了してしまうことに起因します。

これは以下の設定をbuild.sbtに追記すれば解決します。

connectInput in run := true
broken pipeの例外が出る

(2013-08-29 追記)

解決しました。 この問題はjava 6以前に存在しているjava.lang.ProcessのI/Fの根本的な問題とのこと。 すでにjava 7用の修正は取り込まれているため、java 7で動かせば例外は出なくなりました。 詳しくは以下のissueを参照してください。

(追記ここまで)

とりえあず上の二つの設定を追記すれば動くようになるのですが、プログラムを動かした後にsbtで以下のような例外が出てしまいます。

> Exception in thread "Thread-2" java.io.IOException: Broken pipe
    at java.io.FileOutputStream.writeBytes(Native Method)
    at java.io.FileOutputStream.write(FileOutputStream.java:282)
    at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
    at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
    at sbt.BasicIO$.read$1(ProcessImpl.scala:109)
    at sbt.BasicIO$.transferFullyImpl(ProcessImpl.scala:113)
    at sbt.BasicIO$.transferFully(ProcessImpl.scala:89)
    at sbt.BasicIO$.connectToIn(ProcessImpl.scala:80)
    at sbt.BasicIO$$anonfun$input$1.apply(ProcessImpl.scala:81)
    at sbt.BasicIO$$anonfun$input$1.apply(ProcessImpl.scala:81)
    at sbt.SimpleProcessBuilder$$anonfun$run$1.apply$mcV$sp(ProcessImpl.scala:390)
    at sbt.Spawn$$anon$3.run(ProcessImpl.scala:20)

まだ解決できてないのですが、これは標準出力を繋げたことによる弊害っぽいです。難しい。

さいごに

未解決問題もあるのですが、とりあえずsbtでleap motionアプリの開発ができるようになりました。 が、同じJNIを使っているOpenNIを使った開発では発生しなかったのでちょっともやっとしてます。

あと余談なんですが、自分の環境だとtmuxでsbtを実行するとサンプルのonFrame()が呼ばれなくなってしまいました…謎です。 こちらも暇があったら調べてみます。