sbt 0.12.x から導入された binary version について

Play 2.1-RC1 を使おうとしたらいろいろハマったのでまとめました。

scala 2.9系, sbt 0.11系 までの依存管理

scala の異なるバージョンのScala用にビルドされたライブラリは使えないことがあります。
例えば、Scala2.8と2.9ではバイナリ互換性がないので、2.8向けにコンパイルされたライブラリは、2.9のプロダクトで使用することはできません。
使用すると、クラスが見つかりません、メソッドが見つかりません、など、まあなんらかのエラーで死にます。

この問題があるため、ScalaではライブラリのバージョンをScalaのバージョン込みで管理する必要があります。
artifactID の後ろに Scala のバージョンをくっつけるのがスタンダードです。

libraryDependencies += "com.example" % "utility_2.9.1" % "1.0"

いちいち、バージョンを書くのもめんどうです。 % の変わりに %% を使うと、バージョンは sbt が自動的に補ってくれます。

libraryDependencies += "com.example" %% "utility" % "1.0"

Scalaのバージョンが異なっていても、バイナリ互換性があれば、問題ありません。
例えば、Scala 2.9.2 を使用しているプロダクトで、2.9.1 用にビルドされたライブラリを使用することは可能です。

scalaVersion := "2.9.2"  

libraryDependencies += "com.example" % "utility_2.9.1" % "1.0"

このような管理が scala 2.9、 sbt 0.11系まで行われて来ました。

scala2.10, sbt0.12系 での依存管理

ここに、scala 2.10、sbt 0.12系からは新たな概念「バイナリバージョン」が加わります。これからscala 2.10, Play 2.1 対応をするという人は知っておいたほうが良いでしょう。多分ハマリます。

ポイントは、

  • 同じバイナリバージョンならばバイナリ互換性がある。
  • バイナリバージョンは . 。例えば、2.10.0 の場合は 2.10となる。
  • (ってことは、2.10.x の間は全部バイナリ互換になるってことですね!)
  • 2.9以前についてはフルバージョンをそのままバイナリバージョンとする。例えば、2.9.1のバイナリバージョンは2.9.1 となる。

です。

まあ簡単に言ってしまえば、
Scala2.10 の間は artifactID は utility_2.10 までつければ OK。今までのは今までどおり。
ということです。

問題その1 ライブラリ作者と使用者でsbtのバージョンが違う場合

これだけなら話は非常に簡単です。今までよりちょっとだけ楽ですね、よかったですねとなるのですが、そうはいかないんですね。残念です。なぜなら

  • みんながみんな sbt 0.12系を使用しているとは限らない

からです。

sbt0.11系を使っているライブラリ作者にとってはバイナリバージョンなんて関係ありません。普通にpublishすればartifactID は utility_2.10.0 のように、フルバージョンがお尻につきます。
このライブラリをsbt0.12系のユーザーが使おうとして、

libraryDependencies += "com.example" %% "utility" % "1.0"

と、書くと、artifactID は utility_2.10 と解釈されてしまいます。
これはめんどうです。。。みんなが気をつけましょうってことですね。イケてない....
こういう場合、ライブラリ使用者は次のように記述し、sbt0.11以前と同じようにフルバージョン=バイナリバージョンとsbtに教えてあげます。

libraryDependencies += "com.example" %% "utility" % "1.0" cross CrossVersion.full

その他細かい設定はsbtのドキュメントを見て下さい。

"Cross-building ― sbt Documentation" http://www.scala-sbt.org/release/docs/Detailed-Topics/Cross-Build.html

問題その2 RC版はfinalとバイナリ非互換

また、全ての人が 0.12系を使っていたとしても、期間限定でまずいことがあります。現在ScalaはRC期間ですが、Milestone版やRC版は安定版とはバイナリ互換性がないのです。
なのに現状sbtがそこの面倒を見てくれないので、気をつけないと2.10.0-finalとはバイナリ互換性がないのにバイナリバージョン=2.10 としてpublishされてしまいます。RC期間は次のように、scalaBinaryVersion を自分で指定しなければなりません。

scalaBinaryVersion := "2.10.0-RC2"

はい、なんだかめんどくさいですね。

まとめ

  • ライブラリ使用者は cross CrossVersion.xxx という書き方を知っておきましょう。
  • ライブラリ作成者は scalaBinaryVersion という設定について知っておきましょう。
  • 2.10.0 が出たらなるべく sbt 0.12.0 を使うようにしましょう。0.11.xと0.12.xを使っている人がまじると混乱しそうです。
  • sbtのドキュメントはたまに読みなおそう。


というところでしょうか。
sbt がもうちょっと気を利かせてくれると嬉しいんですが...
せめてRC版のバイナリバージョンは自動的にフルバージョンにしますとか、してくれないんでしょうかねえ。