sbt利用上のIvyに関するハマりポイント

sbtはライブラリの依存関係の解決にIvyを使用しています。Ivyはローカルに「キャッシュ」と「リポジトリ」という似て非なるものを持っており、これがsbtを利用するうえでのハマりポイントの一つとなっています。
ちなみにMavenの場合、ローカルリポジトリがローカルキャッシュの役割も兼ねています。Mavenに慣れているとIvyはちょっと面食らう挙動をすることがあります。普通にライブラリを使っているだけであれば意識する必要はないのですが、自分でライブラリやsbtプラグインなどを作成して動作確認のためにローカルリポジトリにpublish-localしたりする場合にはIvyのローカルキャッシュとローカルリポジトリについて正しく理解しておく必要があります。

参照する順番とSNAPSHOTライブラリ

Ivyはまずはじめにローカルキャッシュを見に行き、存在しない場合ローカルリポジトリ→リモートリポジトリの順にライブラリの取得にトライします。一度ローカルにキャッシュができてしまうとリポジトリ上のファイルを上書きしても再取得は行われません。ここまではMavenとだいたい同じような感じですが、sbtの少し古いバージョンの場合、SNAPSHOTライブラリについても一度リポジトリから取得したらその後は更新のチェックは行いません。毎回更新をチェックしたいライブラリの依存関係には以下のようにchanging()を追加しておく必要があります。

libraryDependencies += "org.specs2" %% "specs2" % "1.7-SNAPSHOT" % "test" changing()

ちなみにsbt 0.12.0以降ではバージョン番号がSNAPSHOTで終わっている場合は自動的に更新をチェックしてくれるようですが、Play2の最新の安定版である2.0.4はまだsbt 0.11.3なので上記の対応が必要になります。
また、それ以外のライブラリの場合、再取得するにはローカルキャッシュを物理的に削除する必要があります。

ローカルリポジトリのライブラリのキャッシュ

Ivyはローカルリポジトリに格納されているライブラリに対してはjarファイルを絶対パスで参照するメタファイルのみキャッシュします。上記ではライブラリは一度キャッシュされたら更新チェックは行われないと書きましたが、ローカルリポジトリのjarファイルについてはjarファイルそのものがキャッシュされているわけではなく、参照がキャッシュされているだけなのでpublish-localするだけで変更が反映されるということになります。
また、ローカルリポジトリのキャッシュは絶対パスでの参照となるためポータビリティがありません。以前書いたようなオフライン環境でビルドするためにキャッシュを別のマシンにコピーして使用する、というような場合はローカルリポジトリのライブラリのキャッシュがローカルキャッシュに含まれないよう気を付ける必要があります。

ローカルリポジトリのライブラリを削除した場合

ライブラリやsbtプラグインを作成してローカルリポジトリにpublish-localしてテストするようなケースでハマりがちなのがこれです。
ローカルリポジトリからライブラリを消す場合、ローカルキャッシュも削除する必要があります。Ivyはたとえリモートリポジトリにpublishされているライブラリでも、ローカルリポジトリへのキャッシュが残っている場合は(ローカルリポジトリにjarが見つからなくても)リモートリポジトリは見に行ってくれません。そのためキャッシュを消すまで依存関係を解決することができなくなってしまいます。
開発中にローカルリポジトリにpublish-localする場合と公開リポジトリにpublishする場合できちんとバージョン番号を変えている場合は問題にならないと思いますが、既存のライブラリをちょっと直して動作確認したいというような場合はソースをちょこっと修正してpublish-localすることも多いと思うので注意が必要です。

Play2を使う場合

もう一つややこしいのがPlay2を使ってWebアプリケーションの開発を行う場合です。Play2はsbtを使用してビルドを行っていますが、Ivyがデフォルトで使用するUSER_HOME/.ivyではなくPLAY_HOME/repositoryをIvyのホームディレクトリとして使用します。つまり、自前のライブラリやsbtプラグインなどをpublish-localしてもPlay2アプリからは参照できないということなります。
この問題を解決するには、昨日のエントリで書いたようにPlay2のBuild.scalaのresolversにIvy標準のローカルリポジトリを追加するのが手っ取り早いと思います。
…とまあ、こんな感じでsbt(というかIvy)にはハマりポイントが盛り沢山です。そもそもなんでローカルキャッシュとローカルリポジトリが別に必要なんですかねぇ。Mavenみたいにひとつだったらこんな紛らわしいこともないと思うのですが…。