sbt-projectmatrix を用いた並列クロスビルド

去年 sbt のクロスビルドを改善するために、sbt-projectmatrix という実験的プラグインを書いた。0.2.0 をリリースしたのでここで紹介する。

複数の Scala バージョンに対するビルド

sbt-projectmatrix をビルドに追加後、以下のようにして 2つの Scala バージョンを使ったマトリックスをセットアップする。

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.8"
ThisBuild / version      := "0.1.0-SNAPSHOT"
 
lazy val core = (projectMatrix in file("core"))
  .settings(
    name := "core"
  )
  .jvmPlatform(scalaVersions = Seq("2.12.8", "2.11.12"))

これは coreJVM2_11coreJVM2_12 というサブプロジェクトを作る。
++ スタイルのステートフルなクロスビルドと違って、これは並列にビルドする。

2つのマトリックス

1つ以上のマトリックスがあると面白くなる。

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.8"
ThisBuild / version      := "0.1.0-SNAPSHOT"
 
// uncomment if you want root
// lazy val root = (project in file("."))
//   .aggregate(core.projectRefs ++ app.projectRefs: _*)
//   .settings(
//   )
 
lazy val core = (projectMatrix in file("core"))
  .settings(
    name := "core"
  )
  .jvmPlatform(scalaVersions = Seq("2.12.8", "2.11.12"))
 
lazy val app = (projectMatrix in file("app"))
  .dependsOn(core)
  .settings(
    name := "app"
  )
  .jvmPlatform(scalaVersions = Seq("2.12.8"))

この例では core は Scala 2.11 と 2.12 に対してビルドするが、app はそのうちの 1つのみ対応している。

Scala.js サポート

Tatsuno さん (@exoego) のお蔭で sbt-projectmatrix 0.2.0 から Scala.js にも対応するようになった。
この機能を使うには、sbt-scalajs も事前にセットアップする必要がある。

lazy val core = (projectMatrix in file("core"))
  .settings(
    name := "core"
  )
  .jsPlatform(scalaVersions = Seq("2.12.8", "2.11.12"))

これは、coreJS2_11coreJS2_12 を作成する。

並列クロスライブラリビルド

列を使って、並列クロスライブラリビルドを行うことも可能だ。
例えば、Config 1.2 と Config 1.3 向けのビルドを作りたいとする。

ThisBuild / organization := "com.example"
ThisBuild / version := "0.1.0-SNAPSHOT"
 
lazy val core = (projectMatrix in file("core"))
  .settings(
    name := "core"
  )
  .crossLibrary(
    scalaVersions = Seq("2.12.8", "2.11.12"),
    suffix = "Config1.2",
    settings = Seq(
      libraryDependencies += "com.typesafe" % "config" % "1.2.1"
    )
  )
  .crossLibrary(
    scalaVersions = Seq("2.12.8"),
    suffix = "Config1.3",
    settings = Seq(
      libraryDependencies += "com.typesafe" % "config" % "1.3.3"
    )
  )

これは coreConfig1_22_11coreConfig1_22_12coreConfig1_32_12 という 3つのサブプロジェクトを作って、それぞれ core_config1.3_2.12core_config1.2_2.11core_config1.2_2.12 というアーティファクトを生成する。

生成されたサブプロジェクトの参照

サブプロジェクトを build.sbt 内で参照したい場合は、以下のようにする。

lazy val core12 = core.crossLib("Config1.2")("2.12.8")

上記では core12Project 型を返す。

クレジット

  • 前述のとおり、Scala.js サポートは Tatsuno さん (@exoego) にコントリしていただいた。
  • サブプロジェクトを使ったクロスビルドというアイディアは Scala.js プラグインにおける Tobias Schlatter 氏の仕事で開拓され、これは sbt-crossproject へと発展した。しかし、これはプラットフォーム (JVM, JS, Native) のクロスビルドに限定されている。
  • 2015年に Paul Draper 氏が書いた sbt-cross は複数の Scala バージョンのクロスビルドを行う。