Magnolia Tech

いつもコードのことばかり考えている人のために。

Scalaにおけるライブラリの衝突

github.com

このPR、単純な依存ライブラリのアップデートのはずがScala 2.11だけテストが失敗している。

エラーメッセージを見ると、サーバ側がstatus code500を返して、テストが失敗していることが分かる。

[info] [info] MyScalatraServletTests:
[info] [info] - GET / on MyScalatraServlet should return status 200 *** FAILED ***
[info] [info]   500 did not equal 200 (MyScalatraServletTests.scala:11)
[info] [info] Run completed in 2 seconds, 18 milliseconds.

しかし、テスト結果からはアプリケーション内で何が失敗して500になっているのか分からないので、同じコードをデバッガで実行しながら確認することにした。そうすると下記の例外が発生していることを突き止めた。

Failure(java.lang.AbstractMethodError: Receiver class org.scalatra.SinatraPathPatternParser does not define or inherit an implementation of the resolved method 'abstract scala.util.DynamicVariable scala$util$parsing$combinator$Parsers$$lastNoSuccessVar()' of interface scala.util.parsing.combinator.Parsers.)

scala-parser-combinatorsのlastNoSuccessVarというメソッドが無くなっているそうだ。確かに、以下のコミットで削除されている。

github.com

このメソッドが削除されたのはscala-parser-combinatorsの1.1.2というバージョンからだ。そしてこの変更によりScala 2.11での非互換が発生していることも既に分かっている。

github.com

そのため、scala-parser-combinatorsを使うときは、ターゲットとするScalaのバージョンに合わせて以下のように、バージョンを切り替える方法が取られる(面倒くさい…)。

  private val parserCombinatorVersion = Def.setting(
    CrossVersion.partialVersion(scalaVersion.value) match {
      case Some((2, 11)) =>
        // https://github.com/scala/scala-parser-combinators/issues/197
        "1.1.1"
      case _ =>
        "1.1.2"
    }
  )

確かにこうやって互換性の有るバージョンが指定されているはずなのに…何故か非互換のある1.1.2が使われているのと同じエラーが出ている。

ほかにscala-parser-combinatorsを使っているライブラリが無いか調べてみると、テンプレートエンジンであるTwirlが使っていることが分かった。

github.com

しかも一律1.1.2を指定している。こっちがクラスパスに存在するためだ。

github.com

しかし、Twirlはscala-parser-combinatorsの中でも非互換が発生するパッケージ(scala.util.parsing.combinator)は使用しておらず(scala.util.parsing.inputのみ利用)、Scala 2.11をサポートしているにも関わらず非互換によるエラーが発生しないのだ。


原因は分かった…あとは解決策

Twirl側に読み込むライブラリを分岐させるロジックを入れる方法も有るが、それほど前向きな話でもない…そもそもScalatra.g8は新規のプロジェクトを作るときのテンプレートライブラリだ。これからScalatraの新規プロジェクトが2.11で始められる可能性は低い(アップデートは有ると思っている)。

というわけで、ここはScalatra.g8からScala 2.11のテストを落とす、というのが妥当である(サポートしない)、という結論に至った。

ScalatraのTwirlサポートは後から入った機能なので、Scala 2.11ベースで動かしている人たちは、今すぐ問題になることは無いと思われるのでScalatra本体のScala 2.11サポートを今すぐ落とす必要は無いけど、やはり古いバージョンはどこかで切る必要が出てくると思う。

このようにアドバイスも頂いた。


ライブラリの非互換性の問題が分かりづらい形で出てくるときも有るね、という話でした。