1. 設定核心

設定核心 

此頁面簡述核心設定引擎。這對於在 sbt 之外使用它可能很有用。這對於理解 sbt 的內部運作方式也可能很有用。

此文件由兩個部分組成。第一部分展示了基於設定引擎構建的範例設定系統。第二部分評論了 sbt 的設定系統是如何基於設定引擎構建的。這可能有助於闡明核心設定引擎到底提供了什麼,以及構建類似 sbt 設定系統的東西需要什麼。

範例 

設定 

若要執行此範例,請先使用以下 build.sbt 檔案建立新專案

libraryDependencies += "org.scala-sbt" %% "collections" % sbtVersion.value

resolvers += sbtResolver.value

然後,將以下範例放入來源檔案 SettingsExample.scalaSettingsUsage.scala 中。最後,執行 sbt 並使用 console 進入 REPL。若要查看以下描述的輸出,請輸入 SettingsUsage

範例設定系統 

範例的第一部分定義了自訂設定系統。主要有三個部分

  1. 定義 Scope 類型。
  2. 定義一個將 Scope(加上 AttributeKey)轉換為 String 的函式。
  3. 定義一個委派函式,該函式定義要查找值的 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)並乘以 3
  • a5 被定義為 a5 的先前值 + 1,由於未定義 a5 的先前值,因此會委派給 a4,結果為 3+1=4。
  • b5 未明確定義,因此會委派給 b4,因此也等於 9

sbt 設定討論 

範圍 

sbt 定義了比此處顯示的更複雜的範圍,用於建置中設定的標準用法。此範圍有四個元件:專案軸、組態軸、任務軸和額外軸。每個元件可以是 Zero(無特定值)、This(目前內容)或 Select(包含特定值)。sbt 將 This_ 解析為 ZeroSelect,具體取決於內容。

例如,在專案中,This 專案軸會變成 Select,表示定義專案。所有其他為 This 的軸都會轉換為 Zero。諸如 inConfiginTask 之類的函式會將 This 轉換為特定值的 Select。例如,如果軸值為 ThisinConfig(Compile)(someSettings) 會將 *someSettings* 中所有設定的組態軸轉換為 Select(Compile)

因此,從範例和 sbt 的範圍中,您可以看到核心設定引擎對範圍的結構沒有施加太多限制。它所需要的只是一個 delegates 函式 Scope => Seq[Scope] 和一個 display 函式。您可以選擇適合您情況的範圍類型。

建構設定 

appvalueupdate 和相關方法是建構設定的核心方法。此範例顯然與 sbt 的介面非常不同,因為這些方法通常不直接使用,而是包裝在更高階的抽象中。

使用核心設定引擎時,您可以使用 HList 來存取其他設定。在 sbt 的更高階系統中,TupleNFunctionN(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.sbtBuild.scala)中定義這些設定的方法。這是透過基本剖析來針對 build.sbt 完成的,然後將產生的程式碼區塊傳遞到 compile/Eval.scala。對於所有定義,sbt 管理類別路徑和重新編譯過程以取得設定。它還提供了一種讓使用者定義專案、任務和組態委派的方法,最終由 delegates 函式使用。