1. コマンド

コマンド 

「コマンド」とは何か? 

「コマンド」はタスクに似ています。sbtコンソールから実行できる名前付き操作です。

ただし、コマンドの実装はビルドの全体の状態(Stateで表される)をパラメータとして受け取り、新しいStateを計算します。つまり、コマンドは他のsbt設定を参照したり変更したりできます。通常、通常のタスクでは不可能なことを行う必要がある場合に、コマンドを使用します。

はじめに 

コマンドには3つの主要な側面があります。

  1. コマンド呼び出しにユーザーが使用する構文、これには以下が含まれます。

    • 構文のためのタブ補完
    • 入力を適切なデータ構造に変換するパーサー
  2. 解析されたデータ構造を使用して実行するアクション。このアクションはビルドのStateを変換します。
  3. ユーザーに提供されるヘルプ

sbtでは、タブ補完を含む構文の部分はパーサーコンバイナーで指定されます。Scala標準ライブラリのパーサーコンバイナーに精通している場合は、これらは非常に似ています。アクションの部分は、(State, T) => Stateという関数です。ここで、Tはパーサーによって生成されたデータ構造です。パーサーコンバイナーの使い方については、入力の解析ページを参照してください。

Stateは、登録されているすべてのCommand、実行する残りのコマンド、およびすべてのプロジェクト関連情報など、ビルドの状態へのアクセスを提供します。Stateの詳細については、状態とアクションを参照してください。

最後に、helpコマンドがコマンドのヘルプを表示するために使用する基本的なヘルプ情報が提供される場合があります。

コマンドの定義 

コマンドは、関数State => Parser[T]とアクション(State, T) => Stateを組み合わせたものです。単なるParser[T]ではなくState => 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("[", ", ", "]")
}