1. sbt 0.13.xからの移行

sbt 0.13.xからの移行 

ケースクラス.copy(...)の移行 

多くのケースクラスは、Contrabandを使用して生成された擬似ケースクラスに置き換えられました。.copy(foo = xxx)withFoo(xxx)に移行します。m: ModuleIDがあり、現在m.copy(revision = "1.0.1")を呼び出している場合、移行方法は次のとおりです。

m.withRevision("1.0.1")

SbtPlugin 

sbt 0.13、sbt 1.0、およびsbt 1.1では、sbtプラグインを開発するためにsbtPlugin設定とスクリプト化されたプラグインが必要でした。sbt 1.2.1では、両方がSbtPluginプラグインに統合されました。

project/plugins.sbtからスクリプト化されたプラグインを削除し、次のように使用します。

lazy val root = (project in file("."))
  .enablePlugins(SbtPlugin)

sbtバージョン固有のソースディレクトリ 

sbtプラグインをクロスビルドする場合、sbtバージョン固有のソースディレクトリsrc/main/scala-sbt-0.13src/main/scala-sbt-1.0を使用する方法があります。そこでは、次のようにPluginCompatという名前のオブジェクトを定義できます。

package sbtfoo

import sbt._
import Keys._

object PluginCompat {
  type UpdateConfiguration = sbt.librarymanagement.UpdateConfiguration

  def subMissingOk(c: UpdateConfiguration, ok: Boolean): UpdateConfiguration =
    c.withMissingOk(ok)
}

これで、subMissingOk(...)関数をsbtバージョン固有の方法で実装できます。

スラッシュ構文への移行 

sbt 0.13では、キーは2つの異なる構文でスコープされていました。1つはsbtのシェル用、もう1つはコード内用です。

  • sbt 0.13シェル:<project-id>/config:intask::key
  • sbt 0.13コード:key in (<project-id>, Config, intask)

sbt 1.1.0以降、キーをスコープするための構文は、シェルとビルド定義の両方で、次のように**スラッシュ構文**に統一されました。

  • <project-id> / Config / intask / key

いくつかの例を以下に示します。

version in ThisBuild := "1.0.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    name := "hello",
    scalacOptions in Compile += "-Xlint",
    scalacOptions in (Compile, console) --= Seq("-Ywarn-unused", "-Ywarn-unused-import"),
    fork in Test := true
  )

これらは現在次のように記述されています。

ThisBuild / version := "1.0.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    name := "hello",
    Compile / scalacOptions += "-Xlint",
    Compile / console / scalacOptions --= Seq("-Ywarn-unused", "-Ywarn-unused-import"),
    Test / fork := true
  )

そして、sbtのシェルでは同じ構文になります。

sbt:hello> name
[info] hello
sbt:hello> ThisBuild / version
[info] 1.0.0-SNAPSHOT
sbt:hello> show Compile / scalacOptions
[info] * -Xlint
sbt:hello> show Compile / console / scalacOptions
[info] * -Xlint
sbt:hello> Test / fork
[info] true

既存のsbt 0.13構文をスラッシュ構文に半自動的に書き換える統一されたスラッシュ構文のための構文Scalafixルールがあります。現在、scalafix CLIの使用が必要であり、非常に正確ではありません(コードの形状のみを見る構文ルールであるため)が、ほとんどの作業は完了します。

$ scalafix --rules=https://gist.githubusercontent.com/eed3si9n/57e83f5330592d968ce49f0d5030d4d5/raw/7f576f16a90e432baa49911c9a66204c354947bb/Sbt0_13BuildSyntax.scala *.sbt project/*.scala

sbt 0.12スタイルからの移行 

sbt 0.13以前(sbt 0.9から0.12)では、ビルドでsbtの3つの側面を使用することが非常に一般的でした。

  • キー依存演算子:<<=<+=<++=
  • TaskKeyとSettingKeyのタプルエンリッチメント(applyとmap)(例:(foo, bar) map { (f, b) => ... }
  • project/Build.scalaでのBuildトレイトの使用

3年以上前のsbt 0.13のリリースでは、.value DSLが導入され、より読みやすく書きやすいコードが可能になり、事実上、最初の2つの側面が冗長になり、公式ドキュメントから削除されました。

同様に、sbt 0.13で導入されたマルチプロジェクトbuild.sbtにより、Buildトレイトは冗長になりました。さらに、現在sbt 0.13で標準となっている自動プラグイン機能により、プラグイン設定の自動ソートと自動インポート機能が有効になりましたが、Build.scalaのメンテナンスがより困難になりました。

sbt 1.0.0で削除されたため、ここではコードの移行方法を説明します。

sbt 0.12スタイルの演算子の移行 

次のような単純な式では

a <<= aTaskDef
b <+= bTaskDef
c <++= cTaskDefs

これらを同等のものに変更するだけで十分です。

a := aTaskDef.value
b += bTaskDef.value
c ++= cTaskDefs.value

タプルエンリッチメントからの移行 

上記のように、.apply.mapの2つのタプルエンリッチメントがあります。違いは、SettingKeyまたはTaskKeyの設定を定義するかどうかによって、前者には.apply、後者には.mapを使用していました。

val sett1 = settingKey[String]("SettingKey 1")
val sett2 = settingKey[String]("SettingKey 2")
val sett3 = settingKey[String]("SettingKey 3")

val task1 = taskKey[String]("TaskKey 1")
val task2 = taskKey[String]("TaskKey 2")
val task3 = taskKey[String]("TaskKey 3")
val task4 = taskKey[String]("TaskKey 4")

sett1 := "s1"
sett2 := "s2"
sett3 <<= (sett1, sett2)(_ + _)

task1 := { println("t1"); "t1" }
task2 := { println("t2"); "t2" }
task3 <<= (task1, task2) map { (t1, t2) => println(t1 + t2); t1 + t2 }
task4 <<= (sett1, sett2) map { (s1, s2) => println(s1 + s2); s1 + s2 }

(タスクを設定に基づいて定義できますが、その逆はできません)

.value DSLを使用すると、キーがSettingKeyTaskKeyかを覚える必要はありません。

sett1 := "s1"
sett2 := "s2"
sett3 := sett1.value + sett2.value

task1 := { println("t1"); "t1" }
task2 := { println("t2"); "t2" }
task3 := { println(task1.value + task2.value); task1.value + task2.value }
task4 := { println(sett1.value + sett2.value); sett1.value + sett2.value }

.dependsOn.triggeredBy、または.runBeforeを使用する場合の移行 

.dependsOnを呼び出す代わりに、

a <<= a dependsOn b

次のように定義します。

a := (a dependsOn b).value

**注記**:問題#1444のため、sbt 0.13.13以前では.triggeredBy.runBefore<<=演算子を使用する必要があります。

Taskを設定する必要がある場合の移行 

sbtのTask型を使用するsourceGeneratorsresourceGeneratorsなどのキーの場合

val sourceGenerators =
  settingKey[Seq[Task[Seq[File]]]]("List of tasks that generate sources")
val resourceGenerators =
  settingKey[Seq[Task[Seq[File]]]]("List of tasks that generate resources")

以前は次のように定義していました。

sourceGenerators in Compile <+= buildInfo

sbt 1では、次のように定義します。

Compile / sourceGenerators += buildInfo

または一般的には、

Compile / sourceGenerators += Def.task { List(file1, file2) }

InputKeyを使用した移行 

InputKeyを使用する場合

run <<= docsRunSetting

移行する際には.valueではなく.evaluatedを使用する必要があります。

run := docsRunSetting.evaluated

Buildトレイトからの移行 

次のようなBuildトレイトベースのビルドの場合

import sbt._
import Keys._
import xyz.XyzPlugin.autoImport._

object HelloBuild extends Build {
  val shared = Defaults.defaultSettings ++ xyz.XyzPlugin.projectSettings ++ Seq(
    organization := "com.example",
    version      := "0.1.0",
    scalaVersion := "2.12.18")

  lazy val hello =
    Project("Hello", file("."),
      settings = shared ++ Seq(
        xyzSkipWrite := true)
    ).aggregate(core)

  lazy val core =
    Project("hello-core", file("core"),
      settings = shared ++ Seq(
        description := "Core interfaces",
        libraryDependencies ++= scalaXml.value)
    )

  def scalaXml = Def.setting {
    scalaBinaryVersion.value match {
      case "2.10" => Nil
      case _      => ("org.scala-lang.modules" %% "scala-xml" % "1.0.6") :: Nil
    }
  }
}

build.sbtに移行できます。

val shared = Seq(
  organization := "com.example",
  version      := "0.1.0",
  scalaVersion := "2.12.18"
)

lazy val helloRoot = (project in file("."))
  .aggregate(core)
  .enablePlugins(XyzPlugin)
  .settings(
    shared,
    name := "Hello",
    xyzSkipWrite := true
  )

lazy val core = (project in file("core"))
  .enablePlugins(XyzPlugin)
  .settings(
    shared,
    name := "hello-core",
    description := "Core interfaces",
    libraryDependencies ++= scalaXml.value
  )

def scalaXml = Def.setting {
  scalaBinaryVersion.value match {
    case "2.10" => Nil
    case _      => ("org.scala-lang.modules" %% "scala-xml" % "1.0.6") :: Nil
  }
}
  1. project/Build.scalaの名前をbuild.sbtに変更します。
  2. インポート文import sbt._import Keys._、およびすべての自動インポートを削除します。
  3. すべての内部定義(sharedhelloRootなど)をobject HelloBuildの外に移動し、HelloBuildを削除します。
  4. Project(...)(project in file("x"))スタイルに変更し、そのsettings(...)メソッドを呼び出して設定を渡します。これにより、自動プラグインはプラグインの依存関係に基づいて設定シーケンスを並べ替えることができます。古い名前を保持するために、name設定を設定する必要があります。
  5. これらの設定は組み込みの自動プラグインによって既に設定されているため、sharedからDefaults.defaultSettingsを削除し、sharedからxyz.XyzPlugin.projectSettingsを削除して、代わりにenablePlugins(XyzPlugin)を呼び出します。

**注記**:Buildトレイトは非推奨ですが、project/*.scalaファイルを使用してビルドを整理したり、アドホックプラグインを定義したりすることはできます。ビルドの整理を参照してください。

Resolver.withDefaultResolversからの移行 

0.13.xでは、Maven Centralリポジトリの代わりに他のリポジトリを使用します。

externalResolvers := Resolver.withDefaultResolvers(resolvers.value, mavenCentral = false)

1.x以降、withDefaultResolverscombineDefaultResolversに名前変更されました。その一方で、パラメーターの1つであるuserResolversSeqではなくVectorに変更されました。

  • 移行にはtoVectorを使用できます。

    externalResolvers := Resolver.combineDefaultResolvers(resolvers.value.toVector, mavenCentral = false)
    
  • Vectorを直接使用することもできます。