1. 狀態與動作

狀態與動作 

State 是 sbt 中所有可用資訊的進入點。主要方法為

  • definedCommands: Seq[Command] 傳回所有已註冊的 Command 定義
  • remainingCommands: List[Exec] 傳回要執行的剩餘命令
  • attributes: AttributeMap 包含通用資料。

命令的動作部分執行工作並轉換 State。以下章節討論 State => State 轉換。如先前所述,命令通常也會處理剖析的值:(State, T) => State

命令相關資料 

Command 可以修改目前已註冊的命令或要執行的命令。這是透過在動作部分轉換提供給命令的(不可變)State 來完成。註冊額外 power 命令的函式可能如下所示

val powerCommands: Seq[Command] = ...

val addPower: State => State =
  (state: State) =>
    state.copy(definedCommands =
      (state.definedCommands ++ powerCommands).distinct
    )

這會取得目前的命令,附加新的命令,並捨棄重複項。或者,State 有一個方便的方法可以執行上述操作

val addPower2 = (state: State) => state ++ powerCommands

一些修改要執行的剩餘命令的函式範例

val appendCommand: State => State =
  (state: State) =>
    state.copy(remainingCommands = state.remainingCommands :+ "cleanup")

val insertCommand: State => State =
  (state: State) =>
    state.copy(remainingCommands = "next-command" +: state.remainingCommands)

第一個會新增一個命令,該命令會在所有目前指定的命令執行後執行。第二個會插入一個命令,該命令會接著執行。剩餘的命令將在插入的命令完成後執行。

若要指出命令已失敗且不應繼續執行,請傳回 state.fail

(state: State) => {
  val success: Boolean = ...
  if(success) state else state.fail
}

專案相關資料 

專案相關資訊儲存在 attributes 中。通常,命令不會直接存取此資訊,而是會使用方便的方法來擷取最有用的資訊

val state: State
val extracted: Extracted = Project.extract(state)
import extracted._

Extracted 提供

  • 存取目前的建置與專案 (currentRef)
  • 存取已初始化的專案設定資料 (structure.data)
  • 存取來自 .sbt 和 .scala 檔案的 Session Setting 和原始的永久設定(分別為 session.append 和 session.original)
  • 存取目前的 Eval 執行個體,以在建置內容中評估 Scala 運算式。

專案資料 

所有專案資料都儲存在 structure.data 中,其類型為 sbt.Settings[Scope]。通常,會以下列方式取得 T 類型資訊

val key: SettingKey[T]
val scope: Scope
val value: Option[T] = key in scope get structure.data

在此,SettingKey[T] 通常從 Keys 取得,並且與用來定義 .sbt 檔案中的設定的類型相同。 Scope 選擇要取得金鑰的作用域。可以使用 in 的便利多載來僅指定所需的作用域軸。請參閱 Structure.scala,以瞭解定義 in 和設定介面其他部分的位置。一些範例

import Keys._
val extracted: Extracted
import extracted._

// get name of current project
val nameOpt: Option[String] = (currentRef / name).get(structure.data)

// get the package options for the `Test/packageSrc` task or Nil if none are defined
val pkgOpts: Seq[PackageOption] = (currentRef / Test / packageSrc / packageOptions).get(structure.data).getOrElse(Nil)

BuildStructure 包含有關建置和專案關係的資訊。主要成員為

units: Map[URI, LoadedBuildUnit]
root: URI

URI 識別建置,而 root 識別載入的初始建置。 LoadedBuildUnit 提供有關單一建置的資訊。LoadedBuildUnit 的主要成員為

// Defines the base directory for the build
localBase: File

// maps the project ID to the Project definition
defined: Map[String, ResolvedProject]

ResolvedProject 具有與 project/Build.scala 中使用的 Project 相同的資訊,只是 ProjectReferences 會解析為 ProjectRef

類別路徑 

sbt 中的類別路徑類型為 Seq[Attributed[File]]。這允許將任意資訊標記到類別路徑項目。sbt 目前使用此功能將 Analysis 與項目建立關聯。這就是它管理多專案增量重新編譯所需資訊的方式。它也會將 ModuleID 和 Artifact 與受管理項目(透過依賴管理取得的項目)建立關聯。當您只需要底層的 Seq[File] 時,請使用 files

val attributedClasspath: Seq[Attribute[File]] = ...
val classpath: Seq[File] = attributedClasspath.files

執行任務 

命令不是從另一個任務)執行特定專案任務並取得其結果會很有用。例如,IDE 相關命令可能想要從專案取得類別路徑,或者任務可能會分析編譯的結果。相關方法為 Project.runTask,其簽名如下

def runTask[T](taskKey: ScopedKey[Task[T]], state: State,
  checkCycles: Boolean = false): Option[(State, Result[T])]

例如,

val eval: State => State = (state: State) => {

    // This selects the main 'compile' task for the current project.
    //   The value produced by 'compile' is of type inc.Analysis,
    //   which contains information about the compiled code.
    val taskKey = Compile / Keys.compile

    // Evaluate the task
    // None if the key is not defined
    // Some(Inc) if the task does not complete successfully (Inc for incomplete)
    // Some(Value(v)) with the resulting value
    val result: Option[(State, Result[inc.Analysis])] = Project.runTask(taskKey, state)
    // handle the result
    result match
    {
        case None => // Key wasn't defined.
        case Some((newState, Inc(inc))) => // error detail, inc is of type Incomplete, use Incomplete.show(inc.tpe) to get an error message
        case Some((newState, Value(v))) => // do something with v: inc.Analysis
    }
}

若要取得特定專案的測試類別路徑,請使用此金鑰

val projectRef: ProjectRef = ...
val taskKey: Task[Seq[Attributed[File]]] =
  (projectRef / Test / Keys.fullClasspath)

在任務中使用 State 

若要從任務存取目前的 State,請使用 state 任務作為輸入。例如,

myTask := ... state.value ...

在任務中更新 State 

也可以在任務中更新 sbt 狀態。若要執行此操作,任務必須傳回類型 StateTransform。狀態會在任務評估完成時轉換。StateTransform 是透過一個函式從 State => State 建構,該函式接受 State 的先前值,並產生新的狀態。例如

import complete.DefaultParsers._
val counter = AttributeKey[Int]("counter")
val setCounter = inputKey[StateTransform]("Set the value of the counter attribute")
setCounter := {
  val count = (Space ~> IntBasic).parsed
  StateTransform(_.put(counter, count))
}

建立輸入任務 setCounter,該任務會將計數器屬性設定為某個值。