2012-09-15 00:08:35 +02:00
===========
Input Tasks
===========
Input Tasks parse user input and produce a task to run.
:doc: `/Detailed-Topics/Parsing-Input/` describes how to use the parser
combinators that define the input syntax and tab completion. This page
describes how to hook those parser combinators into the input task
system.
Input Keys
==========
2013-07-29 13:27:17 +02:00
A key for an input task is of type `InputKey` and represents the input
task like a `SettingKey` represents a setting or a `TaskKey`
2012-09-15 00:08:35 +02:00
represents a task. Define a new input task key using the
2013-07-29 13:27:17 +02:00
`inputKey.apply` factory method:
2012-09-15 00:08:35 +02:00
::
2013-03-08 20:23:31 +01:00
// goes in project/Build.scala or in build.sbt
2012-12-02 09:17:20 +01:00
val demo = inputKey[Unit]("A demo input task.")
2012-09-15 00:08:35 +02:00
2012-11-19 02:29:01 +01:00
The definition of an input task is similar to that of a normal task, but it can
2013-10-16 22:21:34 +02:00
also use the result of a :doc: `Parser </Detailed-Topics/Parsing-Input>` applied to
2013-07-29 13:27:17 +02:00
user input. Just as the special `value` method gets the value of a
setting or task, the special `parsed` method gets the result of a `Parser` .
2012-11-19 02:29:01 +01:00
2012-09-15 00:08:35 +02:00
Basic Input Task Definition
===========================
The simplest input task accepts a space-delimited sequence of arguments.
2012-11-19 02:29:01 +01:00
It does not provide useful tab completion and parsing is basic. The built-in
2013-07-29 13:27:17 +02:00
parser for space-delimited arguments is constructed via the `spaceDelimited`
2012-11-19 02:29:01 +01:00
method, which accepts as its only argument the label to present to the user
during tab completion.
For example, the following task prints the current Scala version and then echoes
the arguments passed to it on their own line.
2012-09-15 00:08:35 +02:00
::
2012-11-19 02:29:01 +01:00
demo := {
// get the result of parsing
val args: Seq[String] = spaceDelimited("<arg>").parsed
// Here, we also use the value of the `scalaVersion` setting
println("The current Scala version is " + scalaVersion.value)
println("The arguments to demo were:")
args foreach println
2012-09-15 00:08:35 +02:00
}
Input Task using Parsers
========================
2013-07-29 13:27:17 +02:00
The Parser provided by the `spaceDelimited` method does not provide
2013-03-08 20:23:31 +01:00
any flexibility in defining the input syntax. Using a custom parser
2013-07-29 13:27:17 +02:00
is just a matter of defining your own `Parser` as described on the
2012-11-19 02:29:01 +01:00
:doc: `/Detailed-Topics/Parsing-Input` page.
2012-09-15 00:08:35 +02:00
Constructing the Parser
-----------------------
2013-07-29 13:27:17 +02:00
The first step is to construct the actual `Parser` by defining a value
2012-11-19 02:29:01 +01:00
of one of the following types:
2013-07-29 13:27:17 +02:00
* `Parser[I]` : a basic parser that does not use any settings
* `Initialize[Parser[I]]` : a parser whose definition depends on one or more settings
* `Initialize[State => Parser[I]]` : a parser that is defined using both settings and the current :doc: `state <Build-State>`
2012-11-19 02:29:01 +01:00
2013-07-29 13:27:17 +02:00
We already saw an example of the first case with `spaceDelimited` , which doesn't use any settings in its definition.
As an example of the third case, the following defines a contrived `Parser` that uses the
2012-11-19 02:29:01 +01:00
project's Scala and sbt version settings as well as the state. To use these settings, we
2013-07-29 13:27:17 +02:00
need to wrap the Parser construction in `Def.setting` and get the setting values with the
special `value` method:
2012-09-15 00:08:35 +02:00
::
import complete.DefaultParsers._
val parser: Initialize[State => Parser[(String,String)]] =
2012-11-19 02:29:01 +01:00
Def.setting {
2012-09-15 00:08:35 +02:00
(state: State) =>
2012-11-19 02:29:01 +01:00
( token("scala" <~ Space) ~ token(scalaVersion.value) ) |
( token("sbt" <~ Space) ~ token(sbtVersion.value) ) |
2012-09-15 00:08:35 +02:00
( token("commands" <~ Space) ~
token(state.remainingCommands.size.toString) )
2012-11-19 02:29:01 +01:00
}
2012-09-15 00:08:35 +02:00
2013-07-29 13:27:17 +02:00
This Parser definition will produce a value of type `(String,String)` .
2012-11-19 02:29:01 +01:00
The input syntax defined isn't very flexible; it is just a demonstration. It
2012-09-15 00:08:35 +02:00
will produce one of the following values for a successful parse
2013-07-29 13:27:17 +02:00
(assuming the current Scala version is |scalaRelease|, the current sbt version is
|release|, and there are 3 commands left to run):
2012-09-15 00:08:35 +02:00
2013-07-29 13:27:17 +02:00
.. parsed-literal ::
2012-09-15 00:08:35 +02:00
2013-07-29 13:27:17 +02:00
("scala", "|scalaRelease|")
("sbt", "|release|")
2012-09-15 00:08:35 +02:00
("commands", "3")
2012-11-19 02:29:01 +01:00
Again, we were able to access the current Scala and sbt version for the project because
they are settings. Tasks cannot be used to define the parser.
2012-09-15 00:08:35 +02:00
Constructing the Task
---------------------
Next, we construct the actual task to execute from the result of the
2013-07-29 13:27:17 +02:00
`Parser` . For this, we define a task as usual, but we can access the
result of parsing via the special `parsed` method on `Parser` .
2012-09-15 00:08:35 +02:00
The following contrived example uses the previous example's output (of
2013-07-29 13:27:17 +02:00
type `(String,String)` ) and the result of the `package` task to
2012-09-15 00:08:35 +02:00
print some information to the screen.
::
2012-11-19 02:29:01 +01:00
demo := {
val (tpe, value) = parser.parsed
2012-09-15 00:08:35 +02:00
println("Type: " + tpe)
println("Value: " + value)
2012-11-19 02:29:01 +01:00
println("Packaged: " + packageBin.value.getAbsolutePath)
2012-09-15 00:08:35 +02:00
}
2013-03-08 20:23:31 +01:00
The InputTask type
==================
2013-07-29 13:27:17 +02:00
It helps to look at the `InputTask` type to understand more advanced usage of input tasks.
2013-03-08 20:23:31 +01:00
The core input task type is:
::
class InputTask[T](val parser: State => Parser[Task[T]])
2013-07-29 13:27:17 +02:00
Normally, an input task is assigned to a setting and you work with `Initialize[InputTask[T]]` .
2013-03-08 20:23:31 +01:00
Breaking this down,
2013-07-29 13:27:17 +02:00
1. You can use other settings (via `Initialize` ) to construct an input task.
2. You can use the current `State` to construct the parser.
2013-03-08 20:23:31 +01:00
3. The parser accepts user input and provides tab completion.
4. The parser produces the task to run.
2013-07-29 13:27:17 +02:00
So, you can use settings or `State` to construct the parser that defines an input task's command line syntax.
2013-03-08 20:23:31 +01:00
This was described in the previous section.
2013-07-29 13:27:17 +02:00
You can then use settings, `State` , or user input to construct the task to run.
2013-03-08 20:23:31 +01:00
This is implicit in the input task syntax.
Using other input tasks
=======================
The types involved in an input task are composable, so it is possible to reuse input tasks.
2013-07-29 13:27:17 +02:00
The `.parsed` and `.evaluated` methods are defined on InputTasks to make this more convenient in common situations:
2013-03-08 20:23:31 +01:00
2013-07-29 13:27:17 +02:00
* Call `.parsed` on an `InputTask[T]` or `Initialize[InputTask[T]]` to get the `Task[T]` created after parsing the command line
* Call `.evaluated` on an `InputTask[T]` or `Initialize[InputTask[T]]` to get the value of type `T` from evaluating that task
2013-03-08 20:23:31 +01:00
2013-07-29 13:27:17 +02:00
In both situations, the underlying `Parser` is sequenced with other parsers in the input task definition.
In the case of `.evaluated` , the generated task is evaluated.
2013-03-08 20:23:31 +01:00
2013-07-29 13:27:17 +02:00
The following example applies the `run` input task, a literal separator parser `--` , and `run` again.
2013-03-08 20:23:31 +01:00
The parsers are sequenced in order of syntactic appearance,
2013-07-29 13:27:17 +02:00
so that the arguments before `--` are passed to the first `run` and the ones after are passed to the second.
2013-03-08 20:23:31 +01:00
::
val run2 = inputKey[Unit](
2013-05-08 18:56:50 +02:00
"Runs the main class twice with different argument lists separated by --")
val separator: Parser[String] = "--"
2013-03-08 20:23:31 +01:00
run2 := {
2013-03-08 20:23:31 +01:00
val one = (run in Compile).evaluated
2013-03-08 20:23:31 +01:00
val sep = separator.parsed
2013-03-08 20:23:31 +01:00
val two = (run in Compile).evaluated
2013-03-08 20:23:31 +01:00
}
For a main class Demo that echoes its arguments, this looks like:
::
$ sbt
> run2 a b -- c d
[info] Running Demo c d
[info] Running Demo a b
c
d
a
b
Preapplying input
=================
2013-07-29 13:27:17 +02:00
Because `InputTasks` are built from `Parsers` , it is possible to generate a new `InputTask` by applying some input programmatically.
2013-10-18 22:49:34 +02:00
(It is also possible to generate a `Task` , which is covered in the next section.)
2013-07-29 13:27:17 +02:00
Two convenience methods are provided on `InputTask[T]` and `Initialize[InputTask[T]]` that accept the String to apply.
2013-03-08 20:23:31 +01:00
2013-07-29 13:27:17 +02:00
* `partialInput` applies the input and allows further input, such as from the command line
* `fullInput` applies the input and terminates parsing, so that further input is not accepted
2013-03-08 20:23:31 +01:00
In each case, the input is applied to the input task's parser.
Because input tasks handle all input after the task name, they usually require initial whitespace to be provided in the input.
Consider the example in the previous section.
We can modify it so that we:
2013-07-29 13:27:17 +02:00
* Explicitly specify all of the arguments to the first `run` . We use `name` and `version` to show that settings can be used to define and modify parsers.
* Define the initial arguments passed to the second `run` , but allow further input on the command line.
2013-03-08 20:23:31 +01:00
2013-07-29 13:27:17 +02:00
NOTE: the current implementation of `:=` doesn't actually support applying input derived from settings yet.
2013-03-08 20:23:31 +01:00
::
2013-10-18 22:49:34 +02:00
lazy val run2 = inputKey[Unit]("Runs the main class twice: " +
2013-03-08 20:23:31 +01:00
"once with the project name and version as arguments"
"and once with command line arguments preceded by hard coded values.")
// The argument string for the first run task is ' <name> <version>'
2013-10-18 22:49:34 +02:00
lazy val firstInput: Initialize[String] =
2013-03-08 20:23:31 +01:00
Def.setting(s" ${name.value} ${version.value}")
// Make the first arguments to the second run task ' red blue'
2013-10-18 22:49:34 +02:00
lazy val secondInput: String = " red blue"
2013-03-08 20:23:31 +01:00
run2 := {
2013-03-08 20:23:31 +01:00
val one = (run in Compile).fullInput(firstInput.value).evaluated
val two = (run in Compile).partialInput(secondInput).evaluated
2013-03-08 20:23:31 +01:00
}
For a main class Demo that echoes its arguments, this looks like:
::
$ sbt
> run2 green
[info] Running Demo demo 1.0
[info] Running Demo red blue green
demo
1.0
red
blue
green
2013-10-18 22:49:34 +02:00
Get a Task from an InputTask
============================
The previous section showed how to derive a new `InputTask` by applying input.
In this section, applying input produces a `Task` .
The `toTask` method on `Initialize[InputTask[T]]` accepts the `String` input to apply and produces a task that can be used normally.
For example, the following defines a plain task `runFixed` that can be used by other tasks or run directly without providing any input, ::
lazy val runFixed = taskKey[Unit]("A task that hard codes the values to `run` ")
runFixed := {
val _ = (run in Compile).toTask(" blue green").value
println("Done!")
}
For a main class Demo that echoes its arguments, running `runFixed` looks like:
::
$ sbt
> runFixed
[info] Running Demo blue green
blue
green
Done!
Each call to `toTask` generates a new task, but each task is configured the same as the original `InputTask` (in this case, `run` ) but with different input applied.
For example, ::
lazy val runFixed2 = taskKey[Unit]("A task that hard codes the values to `run` ")
fork in run := true
runFixed2 := {
val x = (run in Compile).toTask(" blue green").value
val y = (run in Compile).toTask(" red orange").value
println("Done!")
}
The different `toTask` calls define different tasks that each run the project's main class in a new jvm.
That is, the `fork` setting configures both, each has the same classpath, and each run the same main class.
However, each task passes different arguments to the main class.
For a main class Demo that echoes its arguments, the output of running `runFixed2` might look like:
::
$ sbt
> runFixed2
[info] Running Demo blue green
[info] Running Demo red orange
blue
green
red
orange
Done!