這是 sbt 1.x 的第三個功能版本,一個二進位檔相容的版本,著重於新功能。sbt 1.x 是在語意版本控制下發佈的,外掛程式預期會在整個 1.x 系列中運作。
sbt 1.3 的主要功能是開箱即用的 Coursier 函式庫管理、ClassLoader 分層、IO 改善和超級 Shell。結合在一起,我們希望這些功能可以改善您執行建置的使用者體驗。
.withAllowInsecureProtocol(true)
的解析器 #4997CrossVersion.Disabled
。請改用 CrossVersion.disabled
sbt/librarymanagement#316run
和 test
任務完成後關閉這些任務使用的臨時 ClassLoader。如果任務使用 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) 就開始將 Coursier 支援的 LM API 實作做為 lm#190。在秋季期間,Andrea Peruffo (@andreaTP) 進一步改進了它,lm-coursier
最終成為由 Alex 維護的 coursier/sbt-coursier 儲存庫的一部分。今年春天,Eugene (@eed3si9n) 再次檢閱了它,並做了一些變更,以便我們可以在 #4614 中替換 LM 引擎,並得到 Alex 的協助。
sbt 1.3.0 新增了「turbo」模式,可啟用可能需要在建置使用者無法正常運作時進行偵錯的實驗性或進階功能。
ThisBuild / turbo := true
最初,我們將分層 ClassLoader (ClassLoaderLayeringStrategy.AllLibraryJars
) 放在此旗標後面。
sbt 在評估 run
和 test
任務時,一律會建立雙層 ClassLoader。ClassLoader 的頂層包含 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
包含除 Java 執行階段之外的所有類別和 JAR。使用此策略的任務行為應該類似於 forking,而沒有啟動新 jvm 的額外負荷。ClassLoaderLayeringStrategy.ScalaLibrary
會建立一個雙層 ClassLoader,其中 Scala 標準函式庫會保持暖機,類似於 sbt 1.2.xClassLoaderLayeringStrategy.AllLibraryJars
會建立一個三層 ClassLoader,其中函式庫相依性(除了 Scala 標準函式庫之外)會保持暖機ClassLoaderLayeringStrategy.AllLibraryJars
應該能縮短執行和測試任務的反應時間。藉由快取函式庫 jar ClassLoader,當在同一工作階段中多次執行時,可以大幅減少執行和測試任務的啟動延遲。GC 的壓力也降低了,因為每次評估任務時都不會重新載入函式庫 jar。
注意:ClassLoaderLayeringStrategy.AllLibraryJars 會重複使用測試之間的單例物件,這要求函式庫在自身之後進行清除。
另一方面,ClassLoaderLayeringStrategy.Flat
將使某些無法與分層 ClassLoader 良好搭配的應用程式受益。一個這樣的範例是 Scala 集合使用的 Java 序列化 + 序列化 Proxy 模式。
ClassLoader 分層由 Ethan Atkins (@eatkins) 貢獻,做為 #4476
除了 ClassLoader 分層之外,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
擷取 Globs。例如,可以撰寫
val scalaSources = baseDirectory.value.toGlob / ** / "*.scala"
val javaSources = baseDirectory.value.toGlob / ** / "*.java"
val allSources = fileTreeView.value.list(Seq(scalaSources, javaSources))
而 FileTreeView
只會遍歷基底目錄一次。Globs 和 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
的結果也可以使用 gccCompile.outputFileChanges
來追蹤。
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 Tracing 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
,可以完全停用檢查器。 #4485,由 @eatkins 提供。首先,我想介紹 Ethan Atkins,他是 sbt 專案的核心社群成員,也是 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 貢獻了各種與 IO 相關的改進,讓 sbt 在他自己的時間內更具回應性。 sbt 1.3.0 反映了他的許多想法。
sbt 1 的最後一個功能版本是 2018 年 7 月的 sbt 1.2.0。 自那時起,我們已在 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。 謝謝!