1. 與組態系統互動

與組態系統互動 

sbt 的核心是新的組態系統,旨在實現廣泛的自訂。此頁面的目標是解釋組態系統背後的通用模型,以及如何使用它。入門指南(請參閱.sbt 檔案)描述如何定義設定;此頁面描述如何與它們互動,以及如何在命令列中探索它們。

選取命令、任務和設定 

設定或任務的完整限定參考看起來像這樣

{<build-uri>}<project-id>/config:intask::key

這個「作用域金鑰」參考由 lastinspect 等命令使用,以及在選取要執行的任務時使用。通常剖析器只需要 key;其餘的可選部分會選取作用域。這些可選部分會個別稱為作用域軸。在上述描述中,{<build-uri>}<project-id>/ 會指定專案軸,config: 是組態軸,而 intask 是任務特定軸。未指定的元件會被視為目前的專案(專案軸)或自動偵測(組態和任務軸)。星號 (*) 用於明確參考 Global 內容,例如 */*:key

選取組態 

在未指定組態的情況下(也就是省略 config: 部分時),如果金鑰定義在 Global 中,則會選取該組態。否則,會選取定義金鑰的第一個組態,其中順序由專案定義的 configurations 成員決定。預設情況下,此順序為 compile, test, ...

例如,在 /home/user/sample/ 中的建置中,於專案 root 中執行時,下列項目相等

> compile
> Compile/compile
> root/compile
> root/Compile/compile
> {file:/home/user/sample/}root/Compile/compile

另一個範例是,run 本身指的是 Compile/run,因為沒有全域 run 任務,且搜尋的第一個組態 compile 定義了 run。因此,若要參考 Test 組態的 run 任務,則必須指定組態軸,例如 Test/run。其他一些需要明確 Test/ 軸的範例如下

> Test/consoleQuick
> Test/console
> Test/doc
> Test/package

任務特定設定 

某些設定是針對每個任務定義的。當在相同組態(例如 compiletest)中有數個相關任務時,會使用此功能,例如 packagepackageSrcpackageDoc。對於封裝任務,其設定是要封裝的檔案、要使用的選項,以及要產生的輸出檔案。每個封裝任務應該能夠針對這些設定有不同的值。

這會使用任務軸來完成,該軸會選取要套用設定的任務。例如,下列項目會列印不同封裝任務的輸出 jar。

> package::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1.jar

> packageSrc::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1-src.jar

> packageDoc::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1-doc.jar

> test:package::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/root_2.8.1-0.1-test.jar

請注意,單個冒號 : 後面會跟著組態軸,而雙冒號 :: 後面會跟著任務軸。

探索設定與任務 

本節討論 inspect 命令,此命令對於探索設定之間的關聯性很有用。例如,可以使用它來判斷應該修改哪個設定才能影響另一個設定。

值與「提供者」 

inspect 提供的第一條資訊是任務的類型,或是設定的值和類型。輸出中的下列區段會標記為「提供者」。這會顯示設定定義的實際作用域。例如,

> inspect libraryDependencies
[info] Setting: scala.collection.Seq[sbt.ModuleID] = List(org.scalaz:scalaz-core:6.0-SNAPSHOT, org.scala-tools.testing:scalacheck:1.8:test)
[info] Provided by:
[info]  {file:/home/user/sample/}root/*:libraryDependencies
...

這表示 libraryDependencies 已在全域組態 (*:) 的目前專案 ({file:/home/user/sample/}root) 上定義。對於像 update 這樣的任務,輸出看起來像這樣

> inspect update
[info] Task: sbt.UpdateReport
[info] Provided by:
[info]  {file:/home/user/sample/}root/*:update
...

相關設定 

inspect 輸出的「相關」區段會列出金鑰的所有定義。例如,

> inspect compile
...
[info] Related:
[info]  test:compile

這表示除了要求的 Compile/compile 任務之外,還有一個 Test/compile 任務。

相依性 

正向相依性會顯示用於定義設定(或任務)的其他設定(或任務)。反向相依性則反向運作,顯示什麼使用給定的設定。inspect 會根據要求的相依性或實際相依性提供此資訊。要求的相依性是設定直接指定的相依性。實際設定是這些相依性解析成的設定。下列章節將更詳細地說明此區別。

要求的相依性 

舉例來說,我們將查看 console

> inspect console
...
[info] Dependencies:
[info]  Compile / console / initialCommands
[info]  Compile / console / streams
[info]  Compile / console / compilers
[info]  Compile / console / cleanupCommands
[info]  Compile / console / taskTemporaryDirectory
[info]  Compile / console / scalaInstance
[info]  Compile / console / scalacOptions
[info]  Compile / console / fullClasspath

...

這會顯示 console 任務的輸入。我們可以看到它從 Compile / console / fullClasspathCompile / console / scalacOptions 取得其類別路徑和選項。因此,inspect 命令提供的資訊可協助尋找要變更的正確設定。金鑰(如 consolefullClasspath)的慣例是,Scala 識別碼是駝峰式大小寫,而字串表示法是小寫並以破折號分隔。組態的 Scala 識別碼為大寫,以區分 compiletest 等任務。例如,我們可以從先前的範例推斷出如何在 Scala 直譯器啟動時新增要執行的程式碼

> set Compile / console / initialCommands := "import mypackage._"
> console
...
import mypackage._
...

inspect 顯示 console 使用設定 Compile / console / initialCommands。將 initialCommands 字串轉譯為 Scala 識別碼,可得出 initialCommandscompile 表示這是主要原始碼。console / 表示設定是 console 特有的。因此,我們可以在 console 任務上設定初始命令,而不會影響 consoleQuick 任務(例如)。

實際相依性 

inspect actual <scoped-key> 會顯示使用的實際相依性。這很有用,因為委派表示相依性可能來自要求的作用域以外的作用域。使用 inspect actual,我們可以確切看到哪個作用域正在為設定提供值。結合 inspect actual 和簡單的 inspect,我們可以查看會影響設定的作用域範圍。返回「要求的相依性」中的範例,

> inspect actual console
...
[info] Dependencies:
[info]  Compile / console / streams
[info]  Global / taskTemporaryDirectory
[info]  scalaInstance
[info]  Compile / scalacOptions
[info]  Global / initialCommands
[info]  Global / cleanupCommands
[info]  Compile / fullClasspath
[info]  console / compilers
...

對於 initialCommands,我們看到它來自全域作用域 (Global)。將此與 inspect console 中的相關輸出合併

Compile / console / initialCommands

我們知道我們可以將 initialCommands 設定為與全域作用域一樣通用,與目前專案的 console 任務作用域一樣特定,或介於兩者之間的任何設定。這表示例如,我們可以為整個專案設定 initialCommands,並且會影響 console

> set initialCommands := "import mypackage._"
...

我們可能想要在這裡設定它的原因是,其他主控台任務現在將使用此值。我們可以查看 inspect actual 的反向相依性輸出,以了解哪些任務使用我們的新設定

> inspect actual initialCommands
...
[info] Reverse dependencies:
[info]  Compile / console
[info]  Test / console
[info]  consoleProject
[info]  Test / consoleQuick
[info]  Compile / consoleQuick
...

我們現在知道,在整個專案上設定 initialCommands 會影響該專案中所有組態的所有主控台任務。如果我們不希望初始命令應用於 consoleProject,因為它沒有我們專案的類別路徑可用,我們可以使用更具體的任務軸

> set console / initialCommands := "import mypackage._"
> set consoleQuick / initialCommands := "import mypackage._"`

或組態軸。

> set Compile/ initialCommands := "import mypackage._"
> set Test / initialCommands := "import mypackage._"

下一部分描述「委派」區段,其中顯示了範圍的委派鏈。

委派 

一個設定具有一個鍵和一個範圍。如果範圍 A 沒有定義該鍵的值,則範圍 A 中對某個鍵的請求可能會委派給另一個範圍。委派鏈是明確定義的,並顯示在 inspect 命令的「委派」區段中。「委派」區段顯示當請求的鍵沒有定義值時,搜尋範圍的順序。

舉例來說,再次考慮 console 的初始命令。

> inspect console/initialCommands
...
[info] Delegates:
[info]  console / initialCommands
[info]  initialCommands
[info]  ThisBuild / console / initialCommands
[info]  ThisBuild / initialCommands
[info]  Zero / console / initialCommands
[info]  Global / initialCommands
...

這表示如果沒有針對 console/initialCommands 的特定值,則會依序列出「委派」下的範圍,直到找到定義的值。