Tasks & Settings
Task and Settings are the core building blocks of our build, which is a graph representing the dependencies between tasks and settings. Both, tasks and settings, return values. This allows use to depend on the actual return value of a task or setting and not on the possible side-effects of a task/setting.
We will use the empty build.sbt
we created in the previous step and insert all the following snippets there.
In this section we will configure our build with some basic information.
Operators
Assign settings or tasks
SBT defines a single operator with which you can configure any setting or task. Because a setting or task can depend on other settings or tasks a simple assignment operator wouldn't be sufficient.
taskOrSettingKey := ???
The :=
is SBT's assignment operator. We will use it throughout this section to customize our build.
Get a setting or task value
If a task or setting needs the value of another task or setting, we need to access it's value. We do this by calling .value
on the setting or task. We can assign values from one setting or task to another.
taskA := taskB.value
Settings
A setting is a static value that is configured once and does not change. Because of this constraint a setting can only depend on other settings, never on a task.
SBT defines a lot of settings by default that every build needs. First we will set the name
of our project, the version
and an organization
.
name := "Hello World"
scalaVersion := "2.12.2"
We start sbt and see the effect of our changes
$ sbt
To display the value of a setting or task we use the show
command. show
takes the setting or task key as the first argument.
> show name
[info] Hello World
We can get a list of the most important settings with the settings
command.
> settings
Exercise
Beginner
Configure the organization
setting to com.example and the version
_to 1.0-SNAPSHOT_.
Advanced
Configure the organization
setting to com.example. The version
setting that
- if the environment variable _BUILD_NUMBER is set, the version is
1.BUILD_NUMBER
- otherwise it's
1.0-SNAPSHOT
Hint: Write simple scala code. Use the
sys.env
property map to access environment variables.
Tasks
A task defines a dynamically generated output. Tasks define how source files are compiled, resources generated or packages bundled. A task can depend on settings or other tasks to perform its action.
We start with compiling our project sources
> compile
[success] Total time: 1 s, completed 01.05.2017 18:36:13
When we use the show
command, sbt displays the output of compile
> show compile
[info] Analysis: 1 Scala source, 3 classes, 2 binary dependencies
[success] Total time: 0 s, completed 01.05.2017 18:52:51
Change an existing task
Before we define custom tasks, we learn how to change existing ones. The easiest change is to override a task with itself.
compile := compile.value
We define the compile task with the value of the existing compile task. If the task output types don't match, SBT won't compile.
Extend a task
Now we do a bit more. We override the packageBin
task that creates a binary artifact and log the size of the created jar file.
packageBin := {
// depend on the current packageBin file
val jarFile = packageBin.value
// sbt logging accessible via the streams task
val log = streams.value.log
log.info(s"jar file size: ${jarFile.length} byte")
// return the jar file
jarFile
}
We depend by accessing the.value
of a task on two tasks in our custom implementation. The original packageBin
definition and the streams
tasks. This dependency is reflected in our build ( which is a dependency graph for settings and tasks ). So both tasks are executed before our new packageBin
tasks is evaluated.
Create a dependency between two tasks
You can add a dependency explicitly if the task itself doesn't depend on the other task. This is useful if you have side effects that can't be managed otherwise. Imagine we have an external build step like yarn install
and yarn test
. We want to make sure that install is called before test. Fore simplicity we assume these two tasks are already defined as yarnTest
and yarnInstall
.
yarnTest := (yarnTest dependsOn yarnInstall).value
A task can dependOn
another task. This doesn't change the output of the initial yarnTest
task and only creates a dependency. To assign the output of the old yarnTest
we access it with .value and assign it to the new yarnTest
definition.
Define a new task
This is covered in depth in the AutoPlugins section. The API is
// inlined in a sbt file
lazy val newTask = taskKey[String]("a task returning a string")
// in an AutoPlugin
val newTask = TaskKey[String]("new-task", "a task returning a string")
Exercise
Beginner
Find the task that runs the main method in your project.
Advanced
Find the task that runs the main method in your project. Create a second main class and try to rerun.
Find another task that takes the main class you want to execute.