本頁說明任務和設定系統的動機。您應該已經知道如何使用任務和設定,這些內容在入門指南和任務頁面中說明。
任務系統的一個重要方面是結合建置中的兩個常見相關步驟
較早版本的 sbt 使用以下方法單獨設定這些步驟
為了了解為何結合它們是有利的,請將這種情況與 Scala 中延遲變數初始化的情況進行比較。此 Scala 程式碼是一種不好的方式來公開其初始化被延遲的值
// Define a variable that will be initialized at some point
// We don't want to do it right away, because it might be expensive
var foo: Foo = _
// Define a function to initialize the variable
def makeFoo(): Unit = ... initialize foo ...
典型用法如下
makeFoo()
doSomething(foo)
這個範例在其糟糕程度上有些誇張,但我認為它與我們的兩步驟任務定義幾乎相同。這很糟糕的特別原因包括
makeFoo()
。foo
可能會被其他程式碼變更。例如,可能會存在 def makeFoo2()。第一點類似於宣告任務依賴,第二點類似於兩個任務修改相同的狀態(專案變數或檔案),而第三點是不同步的共用狀態的結果。
在 Scala 中,我們具有內建的功能來輕鬆解決此問題:lazy val
。
lazy val foo: Foo = ... initialize foo ...
例如使用方式
doSomething(foo)
在這裡,lazy val
為我們提供執行緒安全、保證存取前初始化,以及不可變性,全部在一個 DRY 建構中。sbt 中的任務系統對任務執行與 lazy val
對我們糟糕的範例所做的事情相同(而且更多,但我們在此不做深入探討)。
任務定義必須宣告其輸入和其輸出的類型。sbt 將確保輸入任務已執行,然後將其結果提供給實作任務的函式,該函式將產生其自己的結果。其他任務可以使用此結果,並確保任務已執行(一次),並且在此過程中是執行緒安全且類型安全的。
任務定義的一般形式如下
myTask := {
val a: A = aTask.value
val b: B = bTask.value
... do something with a, b and generate a result ...
}
(這僅旨在討論任務背後的概念,因此請參閱sbt 任務頁面以了解使用詳情。)在這裡,假設 aTask
產生類型為 A
的結果,而 bTask
產生類型為 B
的結果。
舉例來說,請考慮產生包含專案二進位 jar、來源 jar 和文件 jar 的 zip 檔案。首先,判斷哪些任務會產生 jar。在此情況下,輸入任務是主要 Compile
範圍中的 packageBin
、packageSrc
和 packageDoc
。每個這些任務的結果是它們產生的 jar 的 File。我們的 zip 檔案任務是透過對應這些封裝任務並在其輸出中包含 zip 檔案來定義的。作為良好實踐,然後我們傳回此 zip 的 File,以便其他任務可以對應 zip 任務。
zip := {
val bin: File = (Compile / packageBin).value
val src: File = (Compile / packageSrc).value
val doc: File = (Compile / packageDoc).value
val out: File = zipPath.value
val inputs: Seq[(File,String)] = Seq(bin, src, doc) x Path.flat
IO.zip(inputs, out)
out
}
val inputs
行定義輸入檔案如何對應到 zip 中的路徑。如需詳細資訊,請參閱對應檔案。並非必須明確宣告類型,但為了清楚起見而包含。
zipPath
輸入將是一個自訂任務,用於定義 zip 檔案的位置。例如
zipPath := target.value / "out.zip"