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 需要 sbtPlugin 设置和脚本化插件来开发 sbt 插件。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 中,键使用两种不同的语法进行作用域限定:一种用于 sbt 的 shell,另一种用于代码中。

  • sbt 0.13 shell:<project-id>/config:intask::key
  • sbt 0.13 代码:key in (<project-id>, Config, intask)

从 sbt 1.1.0 开始,对键进行作用域限定的语法已统一为 shell 和构建定义,都使用以下 **斜杠语法**

  • <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 的 shell 中也使用相同的语法

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

有一个 用于统一斜杠语法的语法 Scalafix 规则,可以半自动地将现有的 sbt 0.13 语法重写为斜杠语法。目前,它需要使用 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 的三个方面的用法

  • 键依赖运算符:<<=<+=<++=
  • TaskKey 和 SettingKey 的元组增强(apply 和 map)(例如 (foo, bar) map { (f, b) => ... }
  • project/Build.scala 中使用 Build 特性

sbt 0.13 的发布(已经超过 3 年了!)引入了 .value DSL,它允许更易于阅读和编写代码,有效地使前两个方面变得多余,并且它们已从官方文档中删除。

类似地,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。区别在于您是为 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,您无需知道或记住您的键是 SettingKey 还是 TaskKey

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

**注意**:在 sbt 0.13.13 及更早版本中,由于问题 #1444,您需要在 .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 之后,withDefaultResolvers 被重命名为 combineDefaultResolvers。与此同时,其中一个参数 userResolvers 变成了 Vector,而不是 Seq

  • 您可以使用 toVector 来帮助迁移。

    externalResolvers := Resolver.combineDefaultResolvers(resolvers.value.toVector, mavenCentral = false)
    
  • 您也可以直接使用 Vector