Play Framework 2.1系と2.3系に依存するライブラリのクロスビルド


目次

ソーシャルゲーム2部 佐々木です。最近はなかなかコーディングの時間が取れず、ストレスフルな日々を送っています。今回は、半年くらい前に行った Play Framework に関する話題です。

背景

経緯から説明すると、Play Framework 2.1系であるサービス(以後、サービスA)を開発・運用していました。 そのサービスのシステムを流用して新たにサービス(以後、サービスB)を立ち上げることになり、その機会に Play Framework 2.3系に移行することにしました。 ただ、大きなシステム変更があるわけではなく、サービスAとサービスBでは特に大きな機能改修はありませんでした。 そこで、サービスAのシステムの中で再利用性の高い機能をライブラリ化して、サービスA/Bそれぞれがそのライブラリに依存するような形にすれば保守管理しやすいと考えました。

サービスA(Play Framework 2.1)<──┐
                              │
                             ライブラリ <── Play Framework
                              │
サービスB(Play Framework 2.3)<──┘

クロスビルドできない

いざ、Build.scala でクロスビルドの設定をしてみると、コンパイルエラーになってしまいました。

crossScalaVersions := Seq("2.11.1", "2.10.4"),
libraryDependencies <++= scalaVersion(_ match {
  case "2.10.4" => Seq(
    "play" %% "play" % "2.1.5" % "provided"
  )
  case _ => Seq(
    "com.typesafe.play" %% "play" % "2.3.9" % "provided",
    "com.typesafe.play" %% "play-ws" % "2.3.9" % "provided"
  )
})

ここで問題になったのが、Play Framework の互換性でした。 Play Framework のリリースノートや移行ガイドを見るとわかりますが、Play Framework 2.2系から play.api.mvc.Result 関連のクラスがdeprecatedになり、Play Framework 2.3系では完全に取り除かれました。それにより、単純に依存するライブラリのバージョンを変えるようなクロスビルドの設定だけでは対応できませんでした。

ソースコードのパスを切り替える

そこで、Play Framework 2.1系とPlay Framework 2.3系でソースコードのパスを別々に設定することにしました。

  sourceDirectories in Compile <+= (scalaVersion, baseDirectory) { (sv, baseDir) =>
    sv match {
      case "2.10.4" => baseDir / "src" / "main" / "play21" / "scala"
      case _ => baseDir / "src" / "main" / "play23" / "scala"
    }
  }

この設定は、Play Framework 2.1系(Scala 2.10.4)でビルドする時には、src/main/scalasrc/main/play21/scalaをコンパイル対象とし、Play Framework 2.3系(not Scala 2.10.4)でビルドする時には、 src/main/scalasrc/main/play23/scalaをコンパイル対象とするものです。

src
  └main
    └scala ─ Play Framework 2.1系/2.3系のどちらでも使うソースコード
    └play21
      └scala ─ Play Framework 2.1系でのみ使うソースコード
    └play23
      └scala ─ Play Framework 2.3系でのみ使うソースコード

あとは src/main/play--/scalaにそれぞれのplay.api.mvc.Result関連のクラスを定義することによって、当初の目的を達成することができました。

まとめ

WEBフレームワークやライブラリを使う側としては、バージョンアップや互換性にどう向き合うかは、悩ましいところでもあり面白いところでもあると思います。今回の件では、ソースコードのパスを切り替えるという発想に至るまでは紆余曲折あり、互換性を無視したバージョンアップをした Play Framework を憎んだこともありました(笑)が、今となってはこの方法以外無いのではと思っています。別の方法があればぜひぜひ教えていただきたいです!