缓存任务

本页介绍缓存任务的细节。有关一般说明,请参阅缓存

自动缓存

val someKey = taskKey[String]("something")

someKey := name.value + version.value + "!"

在 sbt 2.x 中,任务结果将根据 nameversion 两个设置自动缓存。首次运行任务时将在现场执行,第二次起将使用磁盘缓存:

sbt:demo> show someKey
[info] demo0.1.0-SNAPSHOT!
[success] elapsed time: 0 s, cache 0%, 1 onsite task
sbt:demo> show someKey
[info] demo0.1.0-SNAPSHOT!
[success] elapsed time: 0 s, cache 100%, 1 disk cache hit

缓存与序列化同样困难

要参与自动缓存,输入键(如 nameversion)必须为 sjsonnew.HashWriter typeclass 提供 given 实例,返回类型必须为 sjsonnew.JsonFormat 提供 given 实例。

[error] -- Error: /Users/xxx/caching/project/FooPlugin.scala:17:4 -
[error]  17 |    foo := {
[error]     |    ^
[error]     |given evidence sjsonnew.JsonFormat[sbt.HashedVirtualFileRef] is not found; opt out of caching by annotating the key with @transient, or as foo := Def.uncached(...), or provide a given value
[error]     |
[error]  18 |      val b = baseDirectory.value

部分内置类型(如 HashedVirtualFileRef)的编解码器可通过 CacheImplicits.given 提供:

import CacheImplicits.given

....

  override lazy val projectSettings: Seq[Setting[?]] = Seq(
    foo := {
      val b = baseDirectory.value
      val conv = fileConverter.value
      conv.toVirtualFile((b / "build.sbt").toPath)
    },
    bar := foo.value,
  )

Contraband 可用于生成 sjson-new 编解码器。

效果跟踪

文件创建的效果

要缓存文件创建的效果(而不仅是返回文件名),需使用 Def.declareOutput(vf) 跟踪文件创建的效果。

someKey := {
  val conv = fileConverter.value
  val out: java.nio.file.Path = createFile(...)
  val vf: xsbti.VirtualFile = conv.toVirtualFile(out)
  Def.declareOutput(vf)
  vf: xsbti.HashedVirtualFileRef
}

系统属性的影响

在缓存任务中捕获系统属性或环境变量会破坏缓存稳定性。考虑以下缓存任务:

build.sbt bad example

lazy val someInt = taskKey[Int]("")

// BAD
someInt := {
  sys.props("release").toInt + 1
}

首先假设我们使用 -Drelease=0 运行:

$ sbt --server -Drelease=0
....
sbt:caching> show someInt
[info] 1
[success] elapsed time: 0 s, cache 0%, 1 onsite task

然而,即使我们使用 -Drelease=1 运行,数值也不会改变:

$ sbt --server -Drelease=1
....
sbt:caching> show someInt
[info] 1
[success] elapsed time: 0 s, cache 100%, 1 disk cache hit

这是因为在使用缓存键时,someInt 只考察任务定义的形态以及输入设置与任务。要正确缓存 someInt,请将系统属性包装为设置,如下所示:

build.sbt

lazy val someInt = taskKey[Int]("")
lazy val releaseVer = settingKey[Int]("")

releaseVer := sys.props("release").toInt
someInt := releaseVer.value + 1

这将在每次加载构建时重新求值系统属性:

$ sbt --server -Drelease=1
sbt:caching> show someInt
[info] 2
[success] elapsed time: 0 s, cache 0%, 1 onsite tas

选择退出缓存

按任务键退出

若您希望某些任务键退出缓存,可按以下方式设置缓存级别:

@transient
val someKey = taskKey[String]("something")

@cacheLevel(include = Array.empty)
val someKey = taskKey[String]("something")

按任务退出

要单独退出缓存,请按以下方式使用 Def.uncached(...)

val someKey = taskKey[String]("something")

someKey := Def.uncached {
  name.value + somethingUncachable.value + "!"
}

构建级退出

要选择退出默认的自定义任务缓存,请在 project/plugins.sbt 中添加以下内容:

Compile / scalacOptions += "-Xmacro-settings:sbt:no-default-task-cache"

Note

此设置仅适用于构建中引入的自定义任务。sbt 或插件提供的任何缓存任务仍将保持缓存。

远程缓存

sbt 2.x 实现了与 Bazel 兼容的 gRPC 接口,可与多种开源和商业后端配合使用。详见 远程缓存设置