这是 sbt 1.x 的第三个功能版本,一个二进制兼容版本,专注于新功能。sbt 1.x 在 **语义版本控制** 下发布,插件预计在整个 1.x 系列中都能正常工作。
sbt 1.3 的主要功能包括开箱即用的 Coursier 库管理、类加载器分层、IO 改进和超级 shell。我们希望这些功能组合在一起能改善用户运行构建的体验。
.withAllowInsecureProtocol(true)
的解析器 #4997CrossVersion.Disabled
。请改用 CrossVersion.disabled
sbt/librarymanagement#316run
和 test
任务完成后关闭了这些任务使用的临时类加载器。如果任务使用 ShutdownHooks 或如果任务创建的任何线程在任务完成后继续运行,这可能会导致下游崩溃。要禁用此行为,请设置 Compile / run / fork := true
或使用 -Dsbt.classloader.close=false
运行 sbt。sbt 1.3.0 采用 Coursier 进行库管理。Coursier 是一个类似于 Ivy 的依赖解析器,由 Alexandre Archambault (@alexarchambault) 用 Scala 重写,旨在成为一个更快的替代方案。
注意:在某些情况下,Coursier 的解析方式可能与 Ivy 不同(例如,远程 -SNAPSHOT
会被缓存 24 小时)。如果您希望回到 Apache Ivy 进行库管理,请在您的 build.sbt
中添加以下内容
ThisBuild / useCoursier := false
许多人参与了将 Coursier 引入 sbt 的工作。2018 年初,Leonard Ehrenfried (@leonardehrenfried) 作为 lm#190 开始了基于 Coursier 的 LM API 实现。在秋季,Andrea Peruffo (@andreaTP) 对其进行了进一步改进,lm-coursier
最终成为由 Alex 维持的 coursier/sbt-coursier 存储库的一部分。今年春天,Eugene (@eed3si9n) 重新审视了它,进行了一些修改,以便我们能够在 #4614 中替换 LM 引擎,并在 Alex 的帮助下完成。
sbt 1.3.0 添加了“turbo”模式,该模式启用实验性或高级功能,这些功能可能需要构建用户进行一些调试,以便在出现故障时进行处理。
ThisBuild / turbo := true
最初,我们将分层类加载器 (ClassLoaderLayeringStrategy.AllLibraryJars
) 置于此标志之后。
sbt 一直在评估 run
和 test
任务时创建两层类加载器。类加载器的顶层包含 scala 库 jar,以便 scala 包中的类可以跨多个任务评估重复使用。sbt 1.3.0 引入了 实验性 classLoaderLayeringStrategy
功能,该功能进一步完善了这一概念。
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
// default
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary
// enabled with turbo
Compile / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.Flat
// default
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.ScalaLibrary
// enabled with turbo
Test / classLoaderLayeringStrategy := ClassLoaderLayeringStrategy.AllLibraryJars
ClassLoaderLayeringStrategy.Flat
包含所有类和 JAR,但 Java 运行时除外。使用此策略的任务的行为应该类似于分叉,而无需启动新 jvm 的开销。ClassLoaderLayeringStrategy.ScalaLibrary
创建一个两层类加载器,其中 Scala 标准库保持热状态,类似于 sbt 1.2.xClassLoaderLayeringStrategy.AllLibraryJars
创建一个三层类加载器,其中库依赖项(除了 Scala 标准库)保持热状态ClassLoaderLayeringStrategy.AllLibraryJars
应该有利于 run 和 test 任务的响应时间。通过缓存库 jar 类加载器,当 run 和 test 任务在同一会话中多次运行时,它们的启动延迟可以显著减少。GC 压力也会降低,因为库 jar 不会在每次任务评估时重新加载。
注意:ClassLoaderLayeringStrategy.AllLibraryJars 在测试之间重用单例对象,这要求库自行清理。
另一方面,ClassLoaderLayeringStrategy.Flat
将有利于某些不能很好地与分层类加载器一起工作的应用程序。其中一个示例是 Scala 集合使用的 Java 序列化 + 序列化代理模式。
类加载器分层由 Ethan Atkins (@eatkins) 在 #4476 中贡献
除了类加载器分层之外,sbt 1.3.0 还包含许多性能增强功能,包括
截至撰写本文时,sbt 1.3.0 对 5000 个源文件的编辑-编译-测试循环比使用 sbt 0.13、Gradle 和我们测试的其他构建工具的三个源文件的编辑-编译-测试循环要快(有关详细信息,请参见 构建性能)。这些更改由 Ethan Atkins (@eatkins) 贡献。
sbt 1.3.0 引入了一种新的类型,Glob
,它描述了路径搜索查询。例如,项目目录中的所有 scala 源代码都可以用 Glob(baseDirectory.value, RecursiveGlob / "*.scala")
或 baseDirectory.value.toGlob / ** / "*.scala"
来描述,其中 **
是 RecursiveGlob
的别名。Glob 基于 PathFinders,但它们可以进行组合,没有 IO 开销。可以使用 FileTreeView
检索 Glob。例如,可以编写
val scalaSources = baseDirectory.value.toGlob / ** / "*.scala"
val javaSources = baseDirectory.value.toGlob / ** / "*.java"
val allSources = fileTreeView.value.list(Seq(scalaSources, javaSources))
FileTreeView
仅遍历一次基本目录。Glob 和 FileTreeView 由 Ethan Atkins (@eatkins) 在 io#178、io#216、io#226 中添加。
sbt 1.3.0 引入了一个新的文件监控实现。它使用增强的 API 来跟踪使用操作系统事件的文件更改事件。它添加了一个新的解析器,该解析器提取了将监控源文件的特定任务,并在检测到更改时重新运行。仅监控正在运行的任务的源依赖项。例如,在运行 ~compile
时,对测试源文件的更改不会触发新的构建。在文件事件之间,现在还有选择返回到 shell、重新运行上一个命令或退出 sbt 的选项。这些更改由 Ethan Atkins (@eatkins) 在 io#178、#216、#226、#4512、#4627 中实现。
sbt 1.3.0 会自动监视构建定义源代码,并在未重新加载的情况下执行任务时显示警告。这可以配置为自动重新加载,如下所示
Global / onChangedBuildSource := ReloadOnSourceChanges
此功能由 Ethan Atkins (@eatkins) 在 #4664 中贡献。
sbt 1.3.0 提供了基于文件实现自定义增量任务的支持。给定一个返回 java.nio.file.Path
、Seq[java.nio.file.Path]
、File
或 Seq[File]
的自定义任务,您可以定义一些辅助任务来使其更具增量性。
import java.nio.file._
import scala.sys.process._
val gccCompile = taskKey[Seq[Path]]("compile C code using gcc")
val gccHeaders = taskKey[Seq[Path]]("header files")
val gccInclude = settingKey[Path]("include directory")
val gccLink = taskKey[Path]("link C code using gcc")
gccCompile / sourceDirectory := sourceDirectory.value
gccCompile / fileInputs += (gccCompile / sourceDirectory).value.toGlob / ** / "*.c"
gccInclude := (gccCompile / sourceDirectory).value.toPath / "include"
gccHeaders / fileInputs += gccInclude.value.toGlob / "*.h"
gccCompile / target := baseDirectory.value / "out"
gccCompile := {
val objectDir = Files.createDirectories((gccCompile / target).value.toPath / "objects")
def objectFile(path: Path): Path =
target.value.toPath / path.getFileName.toString.replaceAll(".c$", ".o")
Files.createDirectories(target.value.toPath)
val headerChanges = gccHeaders.inputFileChanges.hasChanges
val changes = gccCompile.inputFileChanges
changes.deleted.foreach(sf => Files.deleteIfExists(objectFile(sf)))
val sourceFileChanges = changes.created ++ changes.modified
val needRecompile = (sourceFileChanges ++ (if (headerChanges) changes.unmodified else Nil)).toSet
val logger = streams.value.log
gccCompile.inputFiles.map { sf =>
val of = objectFile(sf)
if (!Files.exists(of) || needRecompile(sf)) {
logger.info(s"Compiling $sf")
s"gcc -I${gccInclude.value} -c $sf -o $of".!!
}
of
}
}
在這種設定下,gccCompile.inputFiles
將會返回所有輸入的 c
源代碼文件的序列,gccCompile.inputFileChanges
返回一個數據結構,包含自上次執行 gccCompile
後創建、刪除、修改和未修改的文件,而 gccHeaders.changedInputFiles
返回自上次執行 gccCompile
後已更改的頭文件。將這些任務結合起來,可以根據上次 gccCompile
完成後的文件系統更改,增量地僅重建需要重建的源文件。
在另一個任務,例如 gccLink
中,也可以使用 gccCompile.outputFileChanges
追蹤 gccCompile
的結果。
gccLink := {
val library = (gccCompile / target).value.toPath / "libmylib.dylib"
val objectFiles = gccCompile.outputFiles
val logger = streams.value.log
if (!Files.exists(library) || gccCompile.outputFileChanges.hasChanges) {
logger.info(s"Rebuilding $library")
s"gcc -dynamiclib -o $library ${objectFiles mkString " "}".!!
}
library
}
任務的輸入將會被 ~ 命令自動監控,該命令具有新的上下文感知解析器。對於生成文件輸出的任何任務,也會實現自定義清理任務。清理任務會在項目和配置範圍內累加。例如,Test / clean 將清理 Test 配置中聲明的 Test 配置中任務生成的所有文件,但不會清理 Compile 配置中生成的那些文件。
此功能由 Ethan Atkins (@eatkins) 在 #4627 中貢獻。
在 ANSI 相容終端中運行時,sbt 1.3.0 將顯示當前正在運行的任務。這讓開發人員能夠了解哪些任務正在並行處理,以及構建在哪裡花費時間。為了向 Gradle 的“Rich Console”和 Buck 的“Super Console”致敬,我們稱之為“超級 shell”。
要退出,請在構建中添加以下內容
ThisBuild / useSuperShell := false
或使用 --supershell=false
(或 -Dsbt.supershell=false
) 運行 sbt。此功能由 Eugene Yokota (@eed3si9n) 添加,作為 #4396/util#196。
要以可視方式查看任務分解,請使用 --traces
(或 -Dsbt.traces=true
) 運行 sbt。這將生成 build.traces
文件,可以使用 Chrome 追蹤 chrome://tracing/
查看該文件。此功能由 Jason Zaugg (@retronym) 貢獻。
要在屏幕上輸出任務計時,請使用 --timings
(或 -Dsbt.task.timings=true -Dsbt.task.timings.on.shutdown=true
) 運行 sbt。
sbt 1.3.0 使生成 SemanticDB 更容易。要啟用在整個構建中生成 SemanticDB
ThisBuild / semanticdbEnabled := true
ThisBuild / semanticdbVersion := "4.1.9"
ThisBuild / semanticdbIncludeInJar := false
sbt 1.3.0 添加了一個新的 print
命令,類似於 show
,但直接輸出到標準輸出。
# sbt -no-colors --error "print akka-cluster/scalaVersion"
2.12.8
這由 David Knapp (@Falmarri) 貢獻,作為 #4341
可以使用 +=
追加 Function1
。
Global / onLoad += { s =>
doSomething()
s
}
這由 Dale Wijnand (@dwijnand) 貢獻,作為 #4521。
sbt 1.3.0 是 sbt 的第一个版本,在 JDK11 上进行了广泛的测试。Travis CI 上的所有集成测试都在 AdoptOpenJDK 的 JDK 11 上运行,这些测试由 @eed3si9n 更新,作为 #4389/zinc#639/[zinc640]。
rt.jar
失效导致的无端重建 #4679,由 @eatkins 贡献。-Dsbt.global.base
属性中将 ~
扩展到用户主目录。 #4367,由 @kai-chi 贡献。def sequential[A](tasks: Seq[Initialize[Task[A]]]): Initialize[Task[A]]
。 #4369,由 @3tty0n 贡献。"sbt/completion"
命令以完成 sbt 命令。 #4397,由 @andreaTP 贡献。import sbt.dsl.LinterLevel.Ignore
来完全禁用 linter。 #4485,由 @eatkins 贡献。首先,我想介绍一下 Ethan Atkins,他是 sbt 项目的核心社区成员,也是 Close Watch 的作者,Close Watch 使用原生代码在 macOS 上提供监视服务。通常我不会公开提交数量,但以下是 sbt 1.3.0 的前 10 名。
541 Ethan Atkins
369 Eugene Yokota (eed3si9n)
42 Jorge Vicente Cantero (jvican)
35 Łukasz Wawrzyk
34 Dale Wijnand
24 Andrea Peruffo
16 Kenji Yoshida (xuwei-k)
13 Guillaume Martres
7 Arnout Engelen
7 Jason Zaugg
作为社区成员,Ethan 在业余时间为 sbt 贡献了各种 IO 相关的改进,以提高 sbt 的响应速度。sbt 1.3.0 反映了他的许多想法。
sbt 1 的最后一个特性版本是 sbt 1.2.0,发布于 2018 年 7 月。从那时起,我们在 sbt 1.2.x 下发布了八个补丁版本,用于修复错误,但大多数特性增强都合并到了 develop
分支。在这些月中,45 位贡献者参与了 sbt 1.3.0 和 Zinc:Ethan Atkins、Eugene Yokota (eed3si9n)、Jorge Vicente Cantero (jvican)、Łukasz Wawrzyk、Dale Wijnand、Andrea Peruffo、Kenji Yoshida (xuwei-k)、Guillaume Martres、Arnout Engelen、Jason Zaugg、Krzysztof Romanowski、Antonio Cunei、Mirco Dotta、OlegYch、Alex Dupre、Nepomuk Seiler、0lejk4、Alexandre Archambault、Eric Peters、Kazuhiro Sera、Philippus、Som Snytt、Syed Akber Jafri、Thomas Droxler、Veera Venky、bigwheel、Akhtyam Sakaev、Alexey Vakhrenev、Eugene Platonov、Helena Edelson、Ignasi Marimon-Clos、Julien Sirocchi、Justin Kaeser、Kajetan Maliszewski、Leonard Ehrenfried、Mikołaj Jakubowski、Nafer Sanabria、Stefan Wachter、Yasuhiro Tatsuno、Yusuke Izawa、falmarri、ilya、kai-chi、tanishiking、Ólafur Páll Geirsson。感谢你们!