此頁面簡述核心設定引擎。這對於在 sbt 之外使用它可能很有用。這對於理解 sbt 的內部運作方式也可能很有用。
此文件由兩個部分組成。第一部分展示了基於設定引擎構建的範例設定系統。第二部分評論了 sbt 的設定系統是如何基於設定引擎構建的。這可能有助於闡明核心設定引擎到底提供了什麼,以及構建類似 sbt 設定系統的東西需要什麼。
若要執行此範例,請先使用以下 build.sbt 檔案建立新專案
libraryDependencies += "org.scala-sbt" %% "collections" % sbtVersion.value
resolvers += sbtResolver.value
然後,將以下範例放入來源檔案 SettingsExample.scala
和 SettingsUsage.scala
中。最後,執行 sbt 並使用 console
進入 REPL。若要查看以下描述的輸出,請輸入 SettingsUsage
。
範例的第一部分定義了自訂設定系統。主要有三個部分
Scope
類型。Scope
(加上 AttributeKey
)轉換為 String
的函式。Scope
序列。還有第四個,但其用法在這個時候可能特定於 sbt。該範例對這部分使用了微不足道的實作。
SettingsExample.scala
:
import sbt._
/** Define our settings system */
// A basic scope indexed by an integer.
final case class Scope(index: Int)
// Extend the Init trait.
// (It is done this way because the Scope type parameter is used everywhere in Init.
// Lots of type constructors would become binary, which as you may know requires lots of type lambdas
// when you want a type function with only one parameter.
// That would be a general pain.)
object SettingsExample extends Init[Scope]
{
// Provides a way of showing a Scope+AttributeKey[_]
val showFullKey: Show[ScopedKey[_]] = new Show[ScopedKey[_]] {
def apply(key: ScopedKey[_]) = key.scope.index + "/" + key.key.label
}
// A sample delegation function that delegates to a Scope with a lower index.
val delegates: Scope => Seq[Scope] = { case s @ Scope(index) =>
s +: (if(index <= 0) Nil else delegates(Scope(index-1)) )
}
// Not using this feature in this example.
val scopeLocal: ScopeLocal = _ => Nil
// These three functions + a scope (here, Scope) are sufficient for defining our settings system.
}
此部分展示了如何使用我們剛定義的系統。最終結果是一個 Settings[Scope]
值。此類型基本上是映射 Scope -> AttributeKey[T] -> Option[T]
。請參閱 設定 API 文件 以取得詳細資訊。
SettingsUsage.scala
:
/** Usage Example **/
import sbt._
import SettingsExample._
import Types._
object SettingsUsage {
// Define some keys
val a = AttributeKey[Int]("a")
val b = AttributeKey[Int]("b")
// Scope these keys
val a3 = ScopedKey(Scope(3), a)
val a4 = ScopedKey(Scope(4), a)
val a5 = ScopedKey(Scope(5), a)
val b4 = ScopedKey(Scope(4), b)
// Define some settings
val mySettings: Seq[Setting[_]] = Seq(
setting( a3, value( 3 ) ),
setting( b4, map(a4)(_ * 3)),
update(a5)(_ + 1)
)
// "compiles" and applies the settings.
// This can be split into multiple steps to access intermediate results if desired.
// The 'inspect' command operates on the output of 'compile', for example.
val applied: Settings[Scope] = make(mySettings)(delegates, scopeLocal, showFullKey)
// Show results.
for(i <- 0 to 5; k <- Seq(a, b)) {
println( k.label + i + " = " + applied.get( Scope(i), k) )
}
}
執行時會產生以下輸出
a0 = None
b0 = None
a1 = None
b1 = None
a2 = None
b2 = None
a3 = Some(3)
b3 = None
a4 = Some(3)
b4 = Some(9)
a5 = Some(4)
b5 = Some(9)
None
結果,我們從未定義該值,也沒有可委派的值。a3
,我們明確將其定義為 3。a4
未定義,因此根據我們的 delegates
函式委派給 a3
。b4
取得 a4
的值(它委派給 a3
,所以是 3)並乘以 3a5
被定義為 a5
的先前值 + 1,由於未定義 a5
的先前值,因此會委派給 a4
,結果為 3+1=4。b5
未明確定義,因此會委派給 b4
,因此也等於 9sbt 定義了比此處顯示的更複雜的範圍,用於建置中設定的標準用法。此範圍有四個元件:專案軸、組態軸、任務軸和額外軸。每個元件可以是 Zero(無特定值)、This(目前內容)或 Select(包含特定值)。sbt 將 This_
解析為 Zero 或 Select,具體取決於內容。
例如,在專案中,This 專案軸會變成 Select,表示定義專案。所有其他為 This 的軸都會轉換為 Zero。諸如 inConfig
和 inTask
之類的函式會將 This
轉換為特定值的 Select。例如,如果軸值為 This,inConfig(Compile)(someSettings)
會將 *someSettings* 中所有設定的組態軸轉換為 Select(Compile)
。
因此,從範例和 sbt 的範圍中,您可以看到核心設定引擎對範圍的結構沒有施加太多限制。它所需要的只是一個 delegates
函式 Scope => Seq[Scope]
和一個 display
函式。您可以選擇適合您情況的範圍類型。
app
、value
、update
和相關方法是建構設定的核心方法。此範例顯然與 sbt 的介面非常不同,因為這些方法通常不直接使用,而是包裝在更高階的抽象中。
使用核心設定引擎時,您可以使用 HList
來存取其他設定。在 sbt 的更高階系統中,TupleN
和 FunctionN
(N = 1-9,除了實際未使用 Tuple1
之外)都有針對 HList
的包裝器。處理任意元數時,盡可能在高層級製作這些包裝器非常有用。這是因為一旦定義了包裝器,就必須為每個 N 重複程式碼。透過在頂層製作包裝器,這只需要一個層級的重複。
此外,sbt 將其任務引擎一致地整合到設定系統中。底層設定引擎沒有任務的概念。這就是為什麼 sbt 使用 SettingKey
類型和 TaskKey
類型的原因。底層 TaskKey[T]
上的方法基本上會轉換為對底層 SettingKey[Task[T]]
進行操作(它們都包裝底層的 AttributeKey
)。
例如,對於 SettingKey *a*,a := 3
將大致轉換為 setting(a, value(3))
。對於 TaskKey *a*,它將大致轉換為 setting(a, value( task { 3 } ) )
。請參閱 main/Structure.scala 以取得詳細資訊。
sbt 還提供了一種在檔案(build.sbt
和 Build.scala
)中定義這些設定的方法。這是透過基本剖析來針對 build.sbt
完成的,然後將產生的程式碼區塊傳遞到 compile/Eval.scala
。對於所有定義,sbt 管理類別路徑和重新編譯過程以取得設定。它還提供了一種讓使用者定義專案、任務和組態委派的方法,最終由 delegates
函式使用。