1. 状态和操作

状态和操作 

State 是 sbt 中所有可用信息的入口点。关键方法是

  • definedCommands: Seq[Command] 返回所有已注册的 Command 定义
  • remainingCommands: List[Exec] 返回要运行的剩余命令
  • attributes: AttributeMap 包含通用数据。

命令的操作部分执行工作并转换 State。以下部分讨论 State => State 转换。如前所述,命令通常也会处理解析后的值:(State, T) => State

与命令相关的数据 

Command 可以修改当前注册的命令或要执行的命令。这在操作部分通过转换提供给命令的(不可变)State 来完成。注册额外功能命令的函数可能如下所示

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)
  • 访问会话 Settings 以及来自 .sbt 和 .scala 文件的原始永久设置(分别为 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 有一些便捷的重载,可用于仅指定所需的范围轴。有关 in 和设置接口的其他部分的定义位置,请参阅 Structure.scala。一些示例

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 已解析为 ProjectRefs。

类路径 

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,该任务将计数器属性设置为某个值。