命令

本页详细介绍命令。一般说明请参阅命令基础

描述

命令是 sbt 的系统级构建块,常用于捕获用户交互。在命令层面,对子项目和并行处理的支持很少,因为这些由 act 命令实现。

插件作者应优先尝试用设置、任务和输入任务解决问题。若干值得注意的例外包括:

  • 扩展 sbt 自身的用户体验
  • 提供顺序处理,例如 release 命令

简介

命令主要有三个方面:

  1. 用户调用命令时使用的语法,包括:
    • 语法的 Tab 补全
    • 将输入转换为适当数据结构的解析器
  2. 使用解析后的数据结构执行的操作。该操作会转换构建 State
  3. 向用户提供的帮助

在 sbt 中,语法部分(包括 Tab 补全)由解析器组合子指定。若您熟悉 Scala 标准库中的解析器组合子,二者非常相似。解析器组合子的用法请参阅 Tab 补全解析器

State 提供对构建状态的访问,例如所有已注册命令、待执行命令以及所有与项目相关的信息。

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

定义命令

命令将函数 State => Parser[T] 与操作 (State, T) => State 组合。使用 State => Parser[T] 而非单纯的 Parser[T],是因为通常需要用当前 State 构建解析器。例如,当前已加载的项目(由 State 提供)决定 project 命令的有效补全。一般与具体情况的示例见以下各节。

构建命令的源码 API 详情请参阅 Command.scala

通用命令

一般命令的构造如下:

val action: (State, A) => State = ...
val parser: State => Parser[A] = ...
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. 试用 hellohelloAllfailIfTruecolorprintState 命令。
  4. 可结合 Tab 补全与下方代码进行尝试。

以下为 build.sbt

build.sbt

import CommandExample.*

scalaVersion := "3.8.2"
LocalRootProject / commands ++= Seq(hello, helloAll, failIfTrue, changeColor, printState)

以下为 project/CommandExample.scala

project/CommandExample.scala

import sbt.*
import Keys.*
import scala.Console

// 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"): s0 =>
    Console.out.println("Hi!")
    s0

  // 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>"): (s0, args) =>
    println("Hi " + args.mkString(" "))
    s0

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

  // 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" ^^^ Console.RESET)
  lazy val color = token( Space ~> ("blue" ^^^ "4" | "green" ^^^ "2") )
  lazy val select = token( "fg" ^^^ "3" | "bg" ^^^ "4" )
  lazy val setColor = (select ~ color).map: (g, c) =>
    s"\u001B[${g}${c}m"

  def changeColor = Command("color")(_ => change): (s0, ansicode) =>
    Console.out.print(ansicode)
    Console.out.println("Hi")
    s0

  // A command that demonstrates getting information out of State.
  def printState = Command.command("printState"): s0 =>
    import s0.*
    println(s"definedCommands.size registered commands")
    println(s"commands to run: ${show(remainingCommands)}")
    println()

    println(s"original arguments: ${show(configuration.arguments.toSeq)}")
    println(s"base directory: ${configuration.baseDirectory}")
    println()

    println(s"sbt version: ${configuration.provider.id.version}")
    println(s"Scala version (for sbt): ${configuration.provider.scalaProvider.version}")
    println()

    val extracted = Project.extract(s0)
    import extracted.*
    println(s"Current build: ${currentRef.build}")
    println(s"Current project: ${currentRef.project}")
    println(s"Original setting count: ${session.original.size}")
    println(s"Session setting count: ${session.append.size}")
    s0

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

end CommandExample