1. 命令

命令 

什么是“命令”? 

“命令”看起来很像任务:它是一个可以在 sbt 控制台中执行的命名操作。

但是,命令的实现以整个构建状态(由 State 表示)作为参数,并计算一个新的 State。这意味着命令可以查看或修改其他 sbt 设置,例如。通常情况下,您会在需要执行常规任务无法完成的操作时使用命令。

简介 

命令主要包含三个方面:

  1. 用户用来调用命令的语法,包括:

    • 语法的制表符补全
    • 将输入转换为适当数据结构的解析器
  2. 使用解析后的数据结构执行的操作。此操作会转换构建 State
  3. 为用户提供的帮助

在 sbt 中,语法部分(包括制表符补全)使用解析器组合器指定。如果您熟悉 Scala 标准库中的解析器组合器,它们非常相似。操作部分是一个函数 (State, T) => State,其中 T 是解析器产生的数据结构。有关如何使用解析器组合器的信息,请参见 解析输入 页面。

State 提供对构建状态的访问,例如所有注册的 Command、要执行的剩余命令以及所有与项目相关的信息。有关 State 的详细信息,请参见 状态和操作

最后,可能会提供基本的帮助信息,供 help 命令用来显示命令帮助。

定义命令 

命令将函数 State => Parser[T] 与操作 (State, T) => State 结合在一起。使用 State => Parser[T] 而不是 Parser[T] 的原因是,当前的 State 通常用于构建解析器。例如,当前加载的项目(由 State 提供)决定了 project 命令的有效补全。以下部分展示了通用和特定情况的示例。

有关构造命令的源 API 详细信息,请参见 Command.scala

通用命令 

通用命令构造如下所示:

val action: (State, T) => State = ...
val parser: State => Parser[T] = ...
val command: Command = Command("name")(parser)(action)

无参数命令 

有一个便捷方法用于构造不接受任何参数的命令。

val action: State => State = ...
val command: Command = Command.command("name")(action)

单参数命令 

有一个便捷方法用于构造接受单个参数(具有任意内容)的命令。

// accepts the state and the single argument
val action: (State, String) => State = ...
val command: Command = Command.single("name")(action)

多参数命令 

有一个便捷方法用于构造接受用空格分隔的多个参数的命令。

val action: (State, Seq[String]) => State = ...

// <arg> is the suggestion printed for tab completion on an argument
val command: Command = Command.args("name", "<arg>")(action)

完整示例 

以下示例是一个示例构建,它向项目添加命令。要试用它:

  1. 创建 build.sbtproject/CommandExample.scala
  2. 在项目上运行 sbt。
  3. 试用 hellohelloAllfailIfTruecolor 和 printState 命令。
  4. 使用制表符补全并将以下代码作为指导。

以下是 build.sbt

import CommandExample._

ThisBuild / organization := "com.example"
ThisBuild / scalaVersion := "2.12.18"
ThisBuild / version      := "0.1.0-SNAPSHOT"

lazy val root = (project in file("."))
  .settings(
    commands ++= Seq(hello, helloAll, failIfTrue, changeColor, printState)
  )

以下是 project/CommandExample.scala

import sbt._
import Keys._

// imports standard command parsing functionality
import complete.DefaultParsers._

object CommandExample {
  // A simple, no-argument command that prints "Hi",
  //  leaving the current state unchanged.
  def hello = Command.command("hello") { state =>
    println("Hi!")
    state
  }

  // A simple, multiple-argument command that prints "Hi" followed by the arguments.
  //   Again, it leaves the current state unchanged.
  def helloAll = Command.args("helloAll", "<name>") { (state, args) =>
    println("Hi " + args.mkString(" "))
    state
  }

  // A command that demonstrates failing or succeeding based on the input
  def failIfTrue = Command.single("failIfTrue") {
    case (state, "true") => state.fail
    case (state, _) => state
  }

  // Demonstration of a custom parser.
  // The command changes the foreground or background terminal color
  //  according to the input.
  lazy val change = Space ~> (reset | setColor)
  lazy val reset = token("reset" ^^^ "\033[0m")
  lazy val color = token( Space ~> ("blue" ^^^ "4" | "green" ^^^ "2") )
  lazy val select = token( "fg" ^^^ "3" | "bg" ^^^ "4" )
  lazy val setColor = (select ~ color) map { case (g, c) => "\033[" + g + c + "m" }

  def changeColor = Command("color")(_ => change) { (state, ansicode) =>
    print(ansicode)
    state
  }

  // A command that demonstrates getting information out of State.
  def printState = Command.command("printState") { state =>
    import state._
    println(definedCommands.size + " registered commands")
    println("commands to run: " + show(remainingCommands))
    println()

    println("original arguments: " + show(configuration.arguments))
    println("base directory: " + configuration.baseDirectory)
    println()

    println("sbt version: " + configuration.provider.id.version)
    println("Scala version (for sbt): " + configuration.provider.scalaProvider.version)
    println()

    val extracted = Project.extract(state)
    import extracted._
    println("Current build: " + currentRef.build)
    println("Current project: " + currentRef.project)
    println("Original setting count: " + session.original.size)
    println("Session setting count: " + session.append.size)

    state
  }

  def show[T](s: Seq[T]) =
    s.map("'" + _ + "'").mkString("[", ", ", "]")
}