SBT Reference
SBT Reference
Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Install . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Getting Started . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Features of sbt . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
Also . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1
sbt by example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
Create a minimum sbt build . . . . . . . . . . . . . . . . . . . . . 27
Start sbt shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Exit sbt shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Compile a project . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Recompile on code change . . . . . . . . . . . . . . . . . . . . . . 28
Create a source file . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Run a previous command . . . . . . . . . . . . . . . . . . . . . . 29
Getting help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Run your app . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Set ThisBuild / scalaVersion from sbt shell . . . . . . . . . . . . 30
Save the session to build.sbt . . . . . . . . . . . . . . . . . . . . . 30
Name your project . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Reload the build . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Add ScalaTest to libraryDependencies . . . . . . . . . . . . . . . 31
Run tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Run incremental tests continuously . . . . . . . . . . . . . . . . . 31
Write a test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Make the test pass . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Add a library dependency . . . . . . . . . . . . . . . . . . . . . . 33
Use Scala REPL . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Make a subproject . . . . . . . . . . . . . . . . . . . . . . . . . . 34
List all subprojects . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Compile the subproject . . . . . . . . . . . . . . . . . . . . . . . 34
Add ScalaTest to the subproject . . . . . . . . . . . . . . . . . . 35
Broadcast commands . . . . . . . . . . . . . . . . . . . . . . . . . 35
Make hello depend on helloCore . . . . . . . . . . . . . . . . . . . 36
Parse JSON using Play JSON . . . . . . . . . . . . . . . . . . . . 36
Add sbt-native-packager plugin . . . . . . . . . . . . . . . . . . . 38
Create a .zip distribution . . . . . . . . . . . . . . . . . . . . . . 39
Dockerize your app . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2
Set the version . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Switch scalaVersion temporarily . . . . . . . . . . . . . . . . . . . 40
Inspect the dist task . . . . . . . . . . . . . . . . . . . . . . . . . 41
Batch mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
sbt new command . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Directory structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Base directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Source code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
sbt build definition files . . . . . . . . . . . . . . . . . . . . . . . 43
Build support files . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Build products . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Configuring version control . . . . . . . . . . . . . . . . . . . . . 43
Running . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
sbt shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Batch mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Continuous build and test . . . . . . . . . . . . . . . . . . . . . . 45
Common commands . . . . . . . . . . . . . . . . . . . . . . . . . 45
Tab completion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
History Commands . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Build definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Specifying the sbt version . . . . . . . . . . . . . . . . . . . . . . 47
What is a build definition? . . . . . . . . . . . . . . . . . . . . . 47
How build.sbt defines settings . . . . . . . . . . . . . . . . . . . . 48
Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Defining tasks and settings . . . . . . . . . . . . . . . . . . . . . 50
Keys in sbt shell . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Imports in build.sbt . . . . . . . . . . . . . . . . . . . . . . . . . 51
Bare .sbt build definition . . . . . . . . . . . . . . . . . . . . . . 51
Adding library dependencies . . . . . . . . . . . . . . . . . . . . . 51
Multi-project builds . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3
Multiple subprojects . . . . . . . . . . . . . . . . . . . . . . . . . 52
Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Inter-project dependencies . . . . . . . . . . . . . . . . . . . . . . 55
Default root project . . . . . . . . . . . . . . . . . . . . . . . . . 56
Navigating projects interactively . . . . . . . . . . . . . . . . . . 57
Common code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Task graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
Declaring dependency to other tasks . . . . . . . . . . . . . . . . 58
Inlining .value calls . . . . . . . . . . . . . . . . . . . . . . . . . . 60
What’s the point of the build.sbt DSL? . . . . . . . . . . . . . . 64
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
The whole story about keys . . . . . . . . . . . . . . . . . . . . . 66
Scope axes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
Referring to scopes in a build definition . . . . . . . . . . . . . . 69
Referring to scoped keys from the sbt shell . . . . . . . . . . . . 70
Examples of scoped key notation . . . . . . . . . . . . . . . . . . 71
Inspecting scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
When to specify a scope . . . . . . . . . . . . . . . . . . . . . . . 72
Build-level settings . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Scope delegation . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Appending values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Appending to previous values: += and ++= . . . . . . . . . . . . . 74
Appending with dependencies: += and ++= . . . . . . . . . . . . . 75
Scope delegation (.value lookup) . . . . . . . . . . . . . . . . . . . . . 76
Scope delegation rules . . . . . . . . . . . . . . . . . . . . . . . . 76
Rule 1: Scope axis precedence . . . . . . . . . . . . . . . . . . . . 77
Rule 2: The task axis delegation . . . . . . . . . . . . . . . . . . 77
Rule 3: The configuration axis search path . . . . . . . . . . . . . 78
Rule 4: The subproject axis search path . . . . . . . . . . . . . . 79
4
Inspect command lists the delegates . . . . . . . . . . . . . . . . 80
.value lookup vs dynamic dispatch . . . . . . . . . . . . . . . . . 81
Library dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Unmanaged dependencies . . . . . . . . . . . . . . . . . . . . . . 85
Managed Dependencies . . . . . . . . . . . . . . . . . . . . . . . 85
Using plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
What is a plugin? . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Declaring a plugin . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Enabling and disabling auto plugins . . . . . . . . . . . . . . . . 89
Global plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Available Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Custom settings and tasks . . . . . . . . . . . . . . . . . . . . . . . . . 91
Defining a key . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
Implementing a task . . . . . . . . . . . . . . . . . . . . . . . . . 92
Execution semantics of tasks . . . . . . . . . . . . . . . . . . . . 93
Turn them into plugins . . . . . . . . . . . . . . . . . . . . . . . . 97
Organizing the build . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
sbt is recursive . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Tracking dependencies in one place . . . . . . . . . . . . . . . . . 99
When to use .scala files . . . . . . . . . . . . . . . . . . . . . . 99
Defining auto plugins . . . . . . . . . . . . . . . . . . . . . . . . . 100
Getting Started summary . . . . . . . . . . . . . . . . . . . . . . . . . 100
sbt: The Core Concepts . . . . . . . . . . . . . . . . . . . . . . . 100
Advanced Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
General Information . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Community Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
sbt Organization . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Community Ivy Repository . . . . . . . . . . . . . . . . . . . . . 113
Cross building plugins from sbt 0.13 . . . . . . . . . . . . . . . . 113
Plugins available for sbt 1.0 (including RC-x) . . . . . . . . . . . 113
5
Community Repository Policy . . . . . . . . . . . . . . . . . . . . . . . 118
Bintray For Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Create an Open Source Distribution account on Bintray . . . . . 119
Create a repository for your sbt plugins . . . . . . . . . . . . . . 119
Add the sbt-bintray plugin to your build. . . . . . . . . . . . . . 119
Make a release . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Linking your package to the sbt organization . . . . . . . . . . . 120
Linking your package to the sbt organization (sbt org admins) . 121
Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Setup Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
Do not put sbt-launch.jar on your classpath. . . . . . . . . . . 121
Terminal encoding . . . . . . . . . . . . . . . . . . . . . . . . . . 121
JVM heap, permgen, and stack sizes . . . . . . . . . . . . . . . . 122
Boot directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
HTTP/HTTPS/FTP Proxy . . . . . . . . . . . . . . . . . . . . . 122
Using Sonatype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Sonatype setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
sbt setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Optional steps . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
Contributing to sbt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Migrating from sbt 0.13.x . . . . . . . . . . . . . . . . . . . . . . . . . 129
Migrating case class .copy(...) . . . . . . . . . . . . . . . . . . 129
SbtPlugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
sbt version specific source directory . . . . . . . . . . . . . . . . . 130
Migrating to slash syntax . . . . . . . . . . . . . . . . . . . . . . 130
Migrating from sbt 0.12 style . . . . . . . . . . . . . . . . . . . . 131
Migrating from the Build trait . . . . . . . . . . . . . . . . . . . 134
Migrating from Resolver.withDefaultResolvers . . . . . . . . . . . 136
sbt 1.2.x releases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6
sbt 1.2.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
sbt 1.2.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
sbt 1.1.x releases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
sbt 1.1.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
sbt 1.1.5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
sbt 1.1.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
Contributors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
sbt 1.1.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
sbt 1.1.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
sbt 1.1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
sbt 1.0.x releases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
sbt 1.0.4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
sbt 1.0.3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
sbt 1.0.2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
sbt 1.0.1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
sbt 1.0.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
Detailed Topics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
Using sbt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Command Line Reference . . . . . . . . . . . . . . . . . . . . . . . . . 168
Notes on the command line . . . . . . . . . . . . . . . . . . . . . 168
Project-level tasks . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Configuration-level tasks . . . . . . . . . . . . . . . . . . . . . . . 168
General commands . . . . . . . . . . . . . . . . . . . . . . . . . . 170
Commands for managing the build definition . . . . . . . . . . . 171
Command Line Options . . . . . . . . . . . . . . . . . . . . . . . 171
Console Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
Accessing settings . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Evaluating tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
Cross-building . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
7
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
Publishing Conventions . . . . . . . . . . . . . . . . . . . . . . . 175
Using Cross-Built Libraries . . . . . . . . . . . . . . . . . . . . . 175
Cross-Building a Project . . . . . . . . . . . . . . . . . . . . . . . 176
Interacting with the Configuration System . . . . . . . . . . . . . . . . 178
Selecting commands, tasks, and settings . . . . . . . . . . . . . . 178
Discovering Settings and Tasks . . . . . . . . . . . . . . . . . . . 179
Triggered Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
Compile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
Running Multiple Commands . . . . . . . . . . . . . . . . . . . . 184
Scripts, REPL, and Dependencies . . . . . . . . . . . . . . . . . . . . . 184
Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
sbt Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
Language Server Protocol 3.0 . . . . . . . . . . . . . . . . . . . . 188
Server modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
Server discovery and authentication . . . . . . . . . . . . . . . . 189
Initialize request . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
textDocument/publishDiagnostics event . . . . . . . . . . . . 191
textDocument/didSave event . . . . . . . . . . . . . . . . . . . . 192
sbt/exec request . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
sbt/setting request . . . . . . . . . . . . . . . . . . . . . . . . . 193
sbt/completion request . . . . . . . . . . . . . . . . . . . . . . . 193
sbt/cancelRequest . . . . . . . . . . . . . . . . . . . . . . . . . 194
Understanding Incremental Recompilation . . . . . . . . . . . . . . . . 195
sbt heuristics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
How to take advantage of sbt heuristics . . . . . . . . . . . . . . 196
Implementation of incremental recompilation . . . . . . . . . . . . . . 197
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
Interaction with the Scala compiler . . . . . . . . . . . . . . . . . 199
8
Name hashing algorithm . . . . . . . . . . . . . . . . . . . . . . . 200
What is included in the interface of a Scala class . . . . . . . . . 207
Bytecode Enhancers . . . . . . . . . . . . . . . . . . . . . . . . . 210
Further references . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
Classpaths, sources, and resources . . . . . . . . . . . . . . . . . . . . 212
Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
Compiler Plugin Support . . . . . . . . . . . . . . . . . . . . . . . . . 214
Continuations Plugin Example . . . . . . . . . . . . . . . . . . . 215
Version-specific Compiler Plugin Example . . . . . . . . . . . . . 215
Configuring Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
Automatically managed Scala . . . . . . . . . . . . . . . . . . . . 215
Using Scala from a local directory . . . . . . . . . . . . . . . . . 217
sbt’s Scala version . . . . . . . . . . . . . . . . . . . . . . . . . . 218
Forking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Enable forking . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
Change working directory . . . . . . . . . . . . . . . . . . . . . . 220
Forked JVM options . . . . . . . . . . . . . . . . . . . . . . . . . 220
Java Home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
Configuring output . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Configuring Input . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Direct Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
Global Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
Basic global configuration file . . . . . . . . . . . . . . . . . . . . 222
Global Settings using a Global Plugin . . . . . . . . . . . . . . . 222
Java Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
Known issues in mixed mode compilation . . . . . . . . . . . . . 224
Ignoring the Scala source directories . . . . . . . . . . . . . . . . 225
Mapping Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
Relative to a directory . . . . . . . . . . . . . . . . . . . . . . . . 225
9
Rebase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Flatten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
Alternatives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Local Scala . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
Macro Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
Defining the Project Relationships . . . . . . . . . . . . . . . . . 228
Common Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 230
Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
Constructing a File . . . . . . . . . . . . . . . . . . . . . . . . . . 232
Path Finders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
File Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Parallel Execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Task ordering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
Practical constraints . . . . . . . . . . . . . . . . . . . . . . . . . 236
Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
External Processes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
Running Project Code . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
sbt’s Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
Additional test configurations . . . . . . . . . . . . . . . . . . . . 249
JUnit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
Dependency Management . . . . . . . . . . . . . . . . . . . . . . . . . 254
10
Artifacts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
Selecting default artifacts . . . . . . . . . . . . . . . . . . . . . . 254
Modifying default artifacts . . . . . . . . . . . . . . . . . . . . . . 255
Defining custom artifacts . . . . . . . . . . . . . . . . . . . . . . 256
Publishing .war files . . . . . . . . . . . . . . . . . . . . . . . . . 257
Using dependencies with artifacts . . . . . . . . . . . . . . . . . . 257
Dependency Management Flow . . . . . . . . . . . . . . . . . . . . . . 258
Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
Caching and Configuration . . . . . . . . . . . . . . . . . . . . . 258
General troubleshooting steps . . . . . . . . . . . . . . . . . . . . 259
Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Library Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
Manual Dependency Management . . . . . . . . . . . . . . . . . 260
Automatic Dependency Management . . . . . . . . . . . . . . . . 261
Proxy Repositories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
sbt Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
~/.sbt/repositories . . . . . . . . . . . . . . . . . . . . . . . . 274
Using credentials for the proxy repository . . . . . . . . . . . . . 274
Proxying Ivy Repositories . . . . . . . . . . . . . . . . . . . . . . 275
Publishing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
Define the repository . . . . . . . . . . . . . . . . . . . . . . . . . 277
Credentials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Cross-publishing . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Published artifacts . . . . . . . . . . . . . . . . . . . . . . . . . . 278
Modifying the generated POM . . . . . . . . . . . . . . . . . . . 279
Publishing Locally . . . . . . . . . . . . . . . . . . . . . . . . . . 279
Skipping Publishing . . . . . . . . . . . . . . . . . . . . . . . . . 280
Resolvers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
11
Maven . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Predefined . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
Custom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
Update Report . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
Filtering a Report and Getting Artifacts . . . . . . . . . . . . . . 284
Cached Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Dependency as a graph . . . . . . . . . . . . . . . . . . . . . . . 288
Cached Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . 288
Caveats and known issues . . . . . . . . . . . . . . . . . . . . . . 289
Consolidated resolution . . . . . . . . . . . . . . . . . . . . . . . 290
Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
Tasks and Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Defining a Task . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
Getting values from multiple scopes . . . . . . . . . . . . . . . . 296
Advanced Task Operations . . . . . . . . . . . . . . . . . . . . . 299
Dynamic Computations with Def.taskDyn . . . . . . . . . . . . 300
Using Def.sequential . . . . . . . . . . . . . . . . . . . . . . . . . 301
Handling Failure . . . . . . . . . . . . . . . . . . . . . . . . . . . 302
Input Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Input Keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 306
Basic Input Task Definition . . . . . . . . . . . . . . . . . . . . . 306
Input Task using Parsers . . . . . . . . . . . . . . . . . . . . . . . 307
The InputTask type . . . . . . . . . . . . . . . . . . . . . . . . . 308
Using other input tasks . . . . . . . . . . . . . . . . . . . . . . . 309
Preapplying input . . . . . . . . . . . . . . . . . . . . . . . . . . 310
Get a Task from an InputTask . . . . . . . . . . . . . . . . . . . 311
Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
12
What is a “command”? . . . . . . . . . . . . . . . . . . . . . . . 312
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
Defining a Command . . . . . . . . . . . . . . . . . . . . . . . . . 313
Full Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
Parsing and tab completion . . . . . . . . . . . . . . . . . . . . . . . . 316
Basic parsers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
Built-in parsers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Combining parsers . . . . . . . . . . . . . . . . . . . . . . . . . . 317
Transforming results . . . . . . . . . . . . . . . . . . . . . . . . . 318
Controlling tab completion . . . . . . . . . . . . . . . . . . . . . 319
Dependent parsers . . . . . . . . . . . . . . . . . . . . . . . . . . 319
State and actions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
Command-related data . . . . . . . . . . . . . . . . . . . . . . . . 320
Project-related data . . . . . . . . . . . . . . . . . . . . . . . . . 321
Project data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
Classpaths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Running tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
Using State in a task . . . . . . . . . . . . . . . . . . . . . . . . . 324
Tasks/Settings: Motivation . . . . . . . . . . . . . . . . . . . . . . . . 324
Application . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
Plugins and Best Practices . . . . . . . . . . . . . . . . . . . . . . . . 326
General Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
project/ vs. ~/.sbt/ . . . . . . . . . . . . . . . . . . . . . . . . 326
Local settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
.sbtrc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
Generated files . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
Don’t hard code . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
Don’t “mutate” files . . . . . . . . . . . . . . . . . . . . . . . . . 328
Use absolute paths . . . . . . . . . . . . . . . . . . . . . . . . . . 328
Parser combinators . . . . . . . . . . . . . . . . . . . . . . . . . . 329
Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
13
Using an auto plugin . . . . . . . . . . . . . . . . . . . . . . . . . 330
By Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
Plugin dependencies . . . . . . . . . . . . . . . . . . . . . . . . . 331
Creating an auto plugin . . . . . . . . . . . . . . . . . . . . . . . 332
Using a library in a build definition example . . . . . . . . . . . . 338
Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Plugins Best Practices . . . . . . . . . . . . . . . . . . . . . . . . . . . 340
Key naming convention: Use prefix . . . . . . . . . . . . . . . . . 341
Artifact naming convention . . . . . . . . . . . . . . . . . . . . . 342
(optional) Plugin naming convention . . . . . . . . . . . . . . . . 342
Don’t use default package . . . . . . . . . . . . . . . . . . . . . . 342
Get your plugins known . . . . . . . . . . . . . . . . . . . . . . . 342
Reuse existing keys . . . . . . . . . . . . . . . . . . . . . . . . . . 342
Use settings and tasks. Avoid commands. . . . . . . . . . . . . . 343
Provide core feature in a plain old Scala object . . . . . . . . . . 343
Configuration advices . . . . . . . . . . . . . . . . . . . . . . . . 343
Scoping advices . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
Setting up Travis CI with sbt . . . . . . . . . . . . . . . . . . . . . . . 348
Set project/build.properties . . . . . . . . . . . . . . . . . . 348
Read the Travis manual . . . . . . . . . . . . . . . . . . . . . . . 348
Basic setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Plugin build setup . . . . . . . . . . . . . . . . . . . . . . . . . . 349
Custom JVM options . . . . . . . . . . . . . . . . . . . . . . . . . 350
Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
Build matrix . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
Notification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
Dealing with flaky network or tests . . . . . . . . . . . . . . . . . 353
More things . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Sample setting . . . . . . . . . . . . . . . . . . . . . . . . . . . . 354
Testing sbt plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
scripted test framework . . . . . . . . . . . . . . . . . . . . . . . 355
14
step 1: snapshot . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
step 2: SbtPlugin . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
step 3: src/sbt-test . . . . . . . . . . . . . . . . . . . . . . . . . . 356
step 4: write a script . . . . . . . . . . . . . . . . . . . . . . . . . 357
step 5: run the script . . . . . . . . . . . . . . . . . . . . . . . . . 358
step 6: custom assertion . . . . . . . . . . . . . . . . . . . . . . . 358
step 7: testing the test . . . . . . . . . . . . . . . . . . . . . . . . 359
step 8: get inspired . . . . . . . . . . . . . . . . . . . . . . . . . . 360
sbt new and Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
Trying new command . . . . . . . . . . . . . . . . . . . . . . . . 360
Giter8 support . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361
How to extend sbt new . . . . . . . . . . . . . . . . . . . . . . . . 362
Cross building plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
How to… . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Classpaths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Include a new type of managed artifact on the classpath, such as
mar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
Get the classpath used for compilation . . . . . . . . . . . . . . . 364
Get the runtime classpath, including the project’s compiled classes365
Get the test classpath, including the project’s compiled test classes365
Use packaged jars on classpaths instead of class directories . . . . 366
Get all managed jars for a configuration . . . . . . . . . . . . . . 366
Get the files included in a classpath . . . . . . . . . . . . . . . . 366
Get the module and artifact that produced a classpath entry . . 367
Customizing paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
Change the default Scala source directory . . . . . . . . . . . . . 367
Change the default Java source directory . . . . . . . . . . . . . . 368
Change the default resource directory . . . . . . . . . . . . . . . 368
Change the default (unmanaged) library directory . . . . . . . . 368
Disable using the project’s base directory as a source directory . 369
Add an additional source directory . . . . . . . . . . . . . . . . . 369
15
Add an additional resource directory . . . . . . . . . . . . . . . . 369
Include/exclude files in the source directory . . . . . . . . . . . . 369
Include/exclude files in the resource directory . . . . . . . . . . . 370
Include only certain (unmanaged) libraries . . . . . . . . . . . . . 370
Generating files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Generate sources . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
Generate resources . . . . . . . . . . . . . . . . . . . . . . . . . . 372
Inspect the build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
Show or search help for a command, task, or setting . . . . . . . 373
List available tasks . . . . . . . . . . . . . . . . . . . . . . . . . . 374
List available settings . . . . . . . . . . . . . . . . . . . . . . . . 374
Display the description and type of a setting or task . . . . . . . 375
Display the delegation chain of a setting or task . . . . . . . . . . 375
Show the list of projects and builds . . . . . . . . . . . . . . . . . 376
Show the current session (temporary) settings . . . . . . . . . . . 376
Show basic information about sbt and the current build . . . . . 376
Show the value of a setting . . . . . . . . . . . . . . . . . . . . . 376
Show the result of executing a task . . . . . . . . . . . . . . . . . 377
Show the classpath used for compilation or testing . . . . . . . . 377
Show the main classes detected in a project . . . . . . . . . . . . 377
Show the test classes detected in a project . . . . . . . . . . . . . 378
Interactive mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Use tab completion . . . . . . . . . . . . . . . . . . . . . . . . . . 378
Show more tab completion suggestions . . . . . . . . . . . . . . . 379
Modify the default JLine keybindings . . . . . . . . . . . . . . . . 379
Configure the prompt string . . . . . . . . . . . . . . . . . . . . . 379
Use history . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
Change the location of the interactive history file . . . . . . . . . 380
Use the same history for all projects . . . . . . . . . . . . . . . . 380
Disable interactive history . . . . . . . . . . . . . . . . . . . . . . 380
Run commands before entering interactive mode . . . . . . . . . 381
16
Configure and use logging . . . . . . . . . . . . . . . . . . . . . . . . . 381
View the logging output of the previously executed command . . 381
View the previous logging output of a specific task . . . . . . . . 382
Show warnings from the previous compilation . . . . . . . . . . . 383
Change the logging level globally . . . . . . . . . . . . . . . . . . 383
Change the logging level for a specific task, configuration, or project384
Configure printing of stack traces . . . . . . . . . . . . . . . . . . 385
Print the output of tests immediately instead of buffering . . . . 385
Add a custom logger . . . . . . . . . . . . . . . . . . . . . . . . . 385
Log messages in a task . . . . . . . . . . . . . . . . . . . . . . . . 386
Log messages in a setting . . . . . . . . . . . . . . . . . . . . . . 386
Project metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
Set the project name . . . . . . . . . . . . . . . . . . . . . . . . . 386
Set the project version . . . . . . . . . . . . . . . . . . . . . . . . 387
Set the project organization . . . . . . . . . . . . . . . . . . . . . 387
Set the project’s homepage and other metadata . . . . . . . . . . 387
Configure packaging . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
Use the packaged jar on classpaths instead of class directory . . . 387
Add manifest attributes . . . . . . . . . . . . . . . . . . . . . . . 388
Change the file name of a package . . . . . . . . . . . . . . . . . 388
Modify the contents of the package . . . . . . . . . . . . . . . . . 388
Running commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389
Pass arguments to a command or task in batch mode . . . . . . . 389
Provide multiple commands to run consecutively . . . . . . . . . 389
Read commands from a file . . . . . . . . . . . . . . . . . . . . . 389
Define an alias for a command or task . . . . . . . . . . . . . . . 389
Quickly evaluate a Scala expression . . . . . . . . . . . . . . . . . 390
Configure and use Scala . . . . . . . . . . . . . . . . . . . . . . . . . . 390
Set the Scala version used for building the project . . . . . . . . 390
Disable the automatic dependency on the Scala library . . . . . . 390
Temporarily switch to a different Scala version . . . . . . . . . . 391
17
Use a local Scala installation for building a project . . . . . . . . 391
Build a project against multiple Scala versions . . . . . . . . . . 391
Enter the Scala REPL with a project’s dependencies on the class-
path, but not the compiled project classes . . . . . . . . . 391
Enter the Scala REPL with a project’s dependencies and com-
piled code on the classpath . . . . . . . . . . . . . . . . . 391
Enter the Scala REPL with plugins and the build definition on
the classpath . . . . . . . . . . . . . . . . . . . . . . . . . 391
Define the initial commands evaluated when entering the Scala
REPL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
Define the commands evaluated when exiting the Scala REPL . . 392
Use the Scala REPL from project code . . . . . . . . . . . . . . . 392
Generate API documentation . . . . . . . . . . . . . . . . . . . . . . . 393
Select javadoc or scaladoc . . . . . . . . . . . . . . . . . . . . . . 393
Set the options used for generating scaladoc independently of
compilation . . . . . . . . . . . . . . . . . . . . . . . . . . 393
Add options for scaladoc to the compilation options . . . . . . . 393
Set the options used for generating javadoc independently of com-
pilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Add options for javadoc to the compilation options . . . . . . . . 394
Enable automatic linking to the external Scaladoc of managed
dependencies . . . . . . . . . . . . . . . . . . . . . . . . . 394
Enable manual linking to the external Scaladoc of managed de-
pendencies . . . . . . . . . . . . . . . . . . . . . . . . . . 394
Define the location of API documentation for a library . . . . . . 395
Triggered execution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
Run a command when sources change . . . . . . . . . . . . . . . 395
Run multiple commands when sources change . . . . . . . . . . . 395
Configure the sources that are checked for changes . . . . . . . . 396
Set the time interval between checks for changes to sources . . . 396
Define Custom Tasks . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
Define a Task that runs tests in specific sub-projects . . . . . . . 396
How to take an action on startup . . . . . . . . . . . . . . . . . . . . . 397
Sequencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
18
Defining a sequential task with Def.sequential . . . . . . . . . . . 398
Defining a dynamic task with Def.taskDyn . . . . . . . . . . . . . 399
Doing something after an input task . . . . . . . . . . . . . . . . 400
Defining a dynamic input task with Def.inputTaskDyn . . . . . . 402
How to sequence using commands . . . . . . . . . . . . . . . . . 403
Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
.sbt build examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
.sbt build with .scala files example . . . . . . . . . . . . . . . . . . . . 408
project/Resolvers.scala . . . . . . . . . . . . . . . . . . . . . . . . 408
project/Dependencies.scala . . . . . . . . . . . . . . . . . . . . . 408
project/ShellPromptPlugin.scala . . . . . . . . . . . . . . . . . . 409
build.sbt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 410
Advanced configurations example . . . . . . . . . . . . . . . . . . . . . 411
Advanced command example . . . . . . . . . . . . . . . . . . . . . . . 413
Frequently Asked Questions . . . . . . . . . . . . . . . . . . . . . . . . 414
Project Information . . . . . . . . . . . . . . . . . . . . . . . . . 414
Usage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
Build definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . 415
Extending sbt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
Dependency Management . . . . . . . . . . . . . . . . . . . . . . 421
Miscellaneous . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Values and Types . . . . . . . . . . . . . . . . . . . . . . . . . . . 422
Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424
Developer’s Guide (Work in progress) . . . . . . . . . . . . . . . . . . 425
Towards sbt 1.0 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 425
Modularization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
Module summary . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
sbt Coding Guideline . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
General goal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 428
19
Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Modular design . . . . . . . . . . . . . . . . . . . . . . . . . . . . 429
Binary resiliency . . . . . . . . . . . . . . . . . . . . . . . . . . . 430
Other public API matters . . . . . . . . . . . . . . . . . . . . . . 431
Style matters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 432
sbt-datatype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Using the plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Datatype schema . . . . . . . . . . . . . . . . . . . . . . . . . . . 433
Using datatype to retain binary compatibility . . . . . . . . . . . 438
JSON codec generation . . . . . . . . . . . . . . . . . . . . . . . 440
Existing parameters for protocols, records, etc. . . . . . . . . . . 441
Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442
Syntax summary . . . . . . . . . . . . . . . . . . . . . . . . . . . 443
Compiler Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444
Fetching the most specific sources . . . . . . . . . . . . . . . . . . . . . 444
sbt Launcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
Getting Started with the sbt launcher . . . . . . . . . . . . . . . . . . 445
Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445
Sbt Launcher Architecture . . . . . . . . . . . . . . . . . . . . . . . . . 450
Module Resolution . . . . . . . . . . . . . . . . . . . . . . . . . . 450
Classloader Caching and Isolation . . . . . . . . . . . . . . . . . . 450
Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
Service Discovery and Isolation . . . . . . . . . . . . . . . . . . . 452
sbt Launcher Configuration . . . . . . . . . . . . . . . . . . . . . . . . 453
Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
Variable Substitution . . . . . . . . . . . . . . . . . . . . . . . . . 456
Syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457
Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Core Principles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 458
Introduction to build state . . . . . . . . . . . . . . . . . . . . . . 458
20
Settings Architecture . . . . . . . . . . . . . . . . . . . . . . . . . 459
Task Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
Settings Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 461
sbt Settings Discussion . . . . . . . . . . . . . . . . . . . . . . . . 464
Setting Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
Controlling Initialization . . . . . . . . . . . . . . . . . . . . . . . 467
Build Loaders . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
Custom Resolver . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
Custom Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . 471
Custom Transformer . . . . . . . . . . . . . . . . . . . . . . . . . 473
The BuildDependencies type . . . . . . . . . . . . . . . . . . . . 473
Creating Command Line Applications Using sbt . . . . . . . . . . . . 474
Hello World Example . . . . . . . . . . . . . . . . . . . . . . . . . 475
Nightly Builds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 477
Archived pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Hello, World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
sbt new command . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Running your app . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Exiting sbt shell . . . . . . . . . . . . . . . . . . . . . . . . . . . 478
Build definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479
Preface
sbt is a build tool for Scala, Java, and more. It requires Java 1.8 or later.
Install
21
Getting Started
To get started, please read the Getting Started Guide. You will save yourself a
lot of time if you have the right understanding of the big picture up-front. All
documentation may be found via the table of contents included at the end of
every page.
Use Stack Overflow for questions. Use the sbt-dev mailing list for discussing sbt
development. Use [@scala_sbt](https://twitter.com/scala_sbt) for questions
and discussions.
Features of sbt
Also
22
Getting Started with sbt
sbt uses a small number of concepts to support flexible and powerful build
definitions. There are not that many concepts, but sbt is not exactly like other
build systems and there are details you will stumble on if you haven’t read the
documentation.
The Getting Started Guide covers the concepts you need to know to create and
maintain an sbt build definition.
It is highly recommended to read the Getting Started Guide!
If you are in a huge hurry, the most important conceptual background can be
found in build definition, scopes, and task graph. But we don’t promise that
it’s a good idea to skip the other pages in the guide.
It’s best to read in order, as later pages in the Getting Started Guide build on
concepts introduced earlier.
Thanks for trying out sbt and have fun!
Installing sbt
To create an sbt project, you’ll need to take these steps:
Ultimately, the installation of sbt boils down to a launcher JAR and a shell
script, but depending on your platform, we provide several ways to make the
process less tedious. Head over to the installation steps for Mac, Windows, or
Linux.
If you have any trouble running sbt, see Setup Notes on terminal encodings,
HTTP proxies, and JVM options.
23
Installing from a universal package
Homebrew
Macports
Install JDK
Windows installer
24
Install JDK
You must first install a JDK. We recommend Oracle JDK 8 or OpenJDK 8. The
details around the package names differ from one distribution to another.
For example, Ubuntu xenial (16.04LTS) has openjdk-8-jdk.
Redhat family calls it java-1.8.0-openjdk-devel.
25
Figure 1: Ubuntu Software & Updates Screenshot
26
Red Hat Enterprise Linux and other RPM-based distributions use the RPM
format. Run the following from the terminal to install sbt (You’ll need superuser
privileges to do so, hence the sudo).
sudo dnf remove sbt # uninstalling sbt if sbt 0.13 was installed (may not be necessary)
sudo dnf --enablerepo=bintray--sbt-rpm install sbt
Gentoo
The official tree contains ebuilds for sbt. To install the latest available version
do:
emerge dev-java/sbt
sbt by example
$ mkdir foo-build
$ cd foo-build
$ touch build.sbt
27
Start sbt shell
$ sbt
[info] Updated file /tmp/foo-build/project/build.properties: set sbt.version to 1.1.4
[info] Loading project definition from /tmp/foo-build/project
[info] Loading settings from build.sbt ...
[info] Set current project to foo-build (in build file:/tmp/foo-build/)
[info] sbt server started at local:///Users/eed3si9n/.sbt/1.0/server/abc4fb6c89985a00fd95/sock
sbt:foo-build>
To leave sbt shell, type exit or use Ctrl+D (Unix) or Ctrl+Z (Windows).
sbt:foo-build> exit
Compile a project
As a convention, we will use the sbt:...> or > prompt to mean that we’re in
the sbt interactive shell.
$ sbt
sbt:foo-build> compile
Prefixing the compile command (or any other command) with ~ causes the
command to be automatically re-executed whenever one of the source files within
the project is modified. For example:
sbt:foo-build> ~compile
[success] Total time: 0 s, completed May 6, 2018 3:52:08 PM
1. Waiting for source changes... (press enter to interrupt)
28
package example
From sbt shell, press up-arrow twice to find the compile command that you
executed at the beginning.
sbt:foo-build> compile
Getting help
Use the help command to get basic help about the available commands.
sbt:foo-build> help
....
29
Run your app
sbt:foo-build> run
[info] Packaging /tmp/foo-build/target/scala-2.12/foo-build_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] Running example.Hello
Hello
[success] Total time: 1 s, completed May 6, 2018 4:10:44 PM
sbt:foo-build> scalaVersion
[info] 2.12.7
30
Reload the build
Use the reload command to reload the build. The command causes the
build.sbt file to be re-read, and its settings applied.
sbt:foo-build> reload
[info] Loading project definition from /tmp/foo-build/project
[info] Loading settings from build.sbt ...
[info] Set current project to Hello (in build file:/tmp/foo-build/)
sbt:Hello>
sbt:Hello> reload
Run tests
sbt:Hello> test
sbt:Hello> ~testQuick
31
Write a test
import org.scalatest._
import org.scalatest._
Confirm that the test passes, then press Enter to exit the continuous test.
32
Add a library dependency
sbt:Hello> console
[info] Starting scala interpreter...
Welcome to Scala 2.12.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala> :paste
// Entering paste mode (ctrl-D to finish)
// press Ctrl+D
import gigahorse._
33
import support.okhttp.Gigahorse
import scala.concurrent._
import duration._
res0: String = {"query":{"count":1,"created":"2018-05-06T22:49:55Z","lang":"en-US",
"results":{"channel":{"item":{"condition":{"code":"26","date":"Sun, 06 May 2018 06:00 PM EDT
"temp":"16","text":"Cloudy"}}}}}}
scala> :q // to quit
Make a subproject
sbt:Hello> projects
[info] In file:/tmp/foo-build/
[info] * hello
[info] helloCore
sbt:Hello> helloCore/compile
34
Add ScalaTest to the subproject
Broadcast commands
35
After reload, ~testQuick now runs on both subprojects:
sbt:Hello> ~testQuick
36
.aggregate(helloCore)
.dependsOn(helloCore)
.settings(
name := "Hello",
libraryDependencies += scalaTest % Test,
)
package example.core
object Weather {
lazy val http = Gigahorse.http(Gigahorse.config)
def weather: Future[String] = {
val r = Gigahorse.url("https://query.yahooapis.com/v1/public/yql").get.
addQueryString(
"q" -> """select item.condition
from weather.forecast where woeid in (select woeid from geo.places(1) where text='New York,
and u='c'""".stripMargin,
"format" -> "json"
)
import ExecutionContext.Implicits._
for {
f <- http.run(r, Gigahorse.asString)
x <- parse(f)
} yield x
}
37
}
}
}
package example
sbt:Hello> run
[info] Compiling 1 Scala source to /tmp/foo-build/core/target/scala-2.12/classes ...
[info] Done compiling.
[info] Compiling 1 Scala source to /tmp/foo-build/target/scala-2.12/classes ...
[info] Packaging /tmp/foo-build/core/target/scala-2.12/hello-core_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] Done compiling.
[info] Packaging /tmp/foo-build/target/scala-2.12/hello_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] Running example.Hello
Hello! The weather in New York is mostly cloudy.
38
val gigahorse = "com.eed3si9n" %% "gigahorse-okhttp" % "0.3.1"
val playJson = "com.typesafe.play" %% "play-json" % "2.6.9"
sbt:Hello> dist
[info] Wrote /tmp/foo-build/target/scala-2.12/hello_2.12-0.1.0-SNAPSHOT.pom
[info] Wrote /tmp/foo-build/core/target/scala-2.12/hello-core_2.12-0.1.0-SNAPSHOT.pom
[info] Your package is ready in /tmp/foo-build/target/universal/hello-0.1.0-SNAPSHOT.zip
$ /tmp/someother
$ cd /tmp/someother
$ unzip -o -d /tmp/someother /tmp/foo-build/target/universal/hello-0.1.0-SNAPSHOT.zip
$ ./hello-0.1.0-SNAPSHOT/bin/hello
Hello! The weather in New York is mostly cloudy.
sbt:Hello> Docker/publishLocal
....
[info] Successfully built b6ce1b6ab2c0
[info] Successfully tagged hello:0.1.0-SNAPSHOT
[info] Built image hello:0.1.0-SNAPSHOT
39
$ docker run hello:0.1.0-SNAPSHOT
Hello! The weather in New York is mostly cloudy
sbt:Hello> ++2.11.12!
[info] Forcing Scala version to 2.11.12 on all projects.
[info] Reapplying settings...
[info] Set current project to Hello (in build file:/tmp/foo-build/)
sbt:Hello> scalaVersion
[info] helloCore / scalaVersion
[info] 2.11.12
[info] scalaVersion
40
[info] 2.11.12 scalaVersion
[info] 2.12.7
Batch mode
You can also run sbt in batch mode, passing sbt commands directly from the
terminal.
Note: Running in batch mode requires JVM spinup and JIT each time, so your
build will run much slower. For day-to-day coding, we recommend using
the sbt shell or a continuous test like ~testQuick.
You can use the sbt new command to quickly setup a simple “Hello world” build.
41
Credits
This page is based on the Essential sbt tutorial written by William “Scala
William” Narmontas.
Directory structure
This page assumes you’ve installed sbt and seen sbt by example.
Base directory
In sbt’s terminology, the “base directory” is the directory containing the project.
So if you created a project hello containing /tmp/foo-build/build.sbt as in
the sbt by example, /tmp/foo-build is your base directory.
Source code
sbt uses the same directory structure as Maven for source files by default (all
paths are relative to the base directory):
src/
main/
resources/
<files to include in main jar here>
scala/
<main Scala sources>
java/
<main Java sources>
test/
resources
<files to include in test jar here>
scala/
<test Scala sources>
java/
<test Java sources>
42
sbt build definition files
The build definition is described in build.sbt (actually any files named *.sbt)
in the project’s base directory.
build.sbt
In addition to build.sbt, project directory can contain .scala files that de-
fines helper objects and one-off plugins. See organizing the build for more.
build.sbt
project/
Dependencies.scala
You may see .sbt files inside project/ but they are not equivalent to .sbt files
in the project’s base directory. Explaining this will come later, since you’ll need
some background information first.
Build products
Generated files (compiled classes, packaged jars, managed files, caches, and
documentation) will be written to the target directory by default.
Your .gitignore (or equivalent for other version control systems) should con-
tain:
target/
Note that this deliberately has a trailing / (to match only directories) and it
deliberately has no leading / (to match project/target/ in addition to plain
target/).
Running
This page describes how to use sbt once you have set up your project. It assumes
you’ve installed sbt and went through sbt by example.
43
sbt shell
$ sbt
Running sbt with no command line arguments starts sbt shell. sbt shell has a
command prompt (with tab completion and history!).
For example, you could type compile at the sbt shell:
> compile
Batch mode
You can also run sbt in batch mode, specifying a space-separated list of sbt
commands as arguments. For sbt commands that take arguments, pass the
command and arguments as one argument to sbt by enclosing them in quotes.
For example,
In this example, testOnly has arguments, TestA and TestB. The commands
will be run in sequence (clean, compile, then testOnly).
Note: Running in batch mode requires JVM spinup and JIT each time, so your
build will run much slower. For day-to-day coding, we recommend using
the sbt shell or Continuous build and test feature described below.
Beginning in sbt 0.13.16, using batch mode in sbt will issue an informational
startup message,
It will only be triggered for sbt compile, and it can also be supressed with
suppressSbtShellNotification := true.
44
Continuous build and test
To speed up your edit-compile-test cycle, you can ask sbt to automatically re-
compile or run tests whenever you save a source file.
Make a command run when one or more source files change by prefixing the
command with ~. For example, in sbt shell try:
> ~testQuick
Common commands
Here are some of the most common sbt commands. For a more complete list,
see Command Line Reference.
clean
Deletes all generated files (in the target directory).
compile
Compiles the main sources (in src/main/scala and src/main/java directories).
test
Compiles and runs all tests.
console
Starts the Scala interpreter with a classpath including the compiled sources
and all dependencies. To return to sbt, type :quit, Ctrl+D (Unix), or Ctrl+Z
(Windows).
run <argument>*
Runs the main class for the project in the same virtual machine as sbt.
package
Creates a jar file containing the files in src/main/resources and the classes com-
piled from src/main/scala and src/main/java.
help <command>
Displays detailed help for the specified command. If no command is provided,
displays brief descriptions of all commands.
reload
45
Reloads the build definition (build.sbt, project/.scala, project/.sbt files). Needed
if you change the build definition.
Tab completion
sbt shell has tab completion, including at an empty prompt. A special sbt
convention is that pressing tab once may show only a subset of most likely
completions, while pressing it more times shows more verbose choices.
History Commands
sbt shell remembers history, even if you exit sbt and restart it. The simplest
way to access history is with the up arrow key. The following commands are
also supported:
!
Show history command help.
!!
Execute the previous command again.
!:
Show all previous commands.
!:n
Show the last n commands.
!n
Execute the command with index n, as shown by the !: command.
!-n
Execute the nth command before this one.
!string
Execute the most recent command starting with ‘string.’
!?string
Execute the most recent command containing ‘string.’
Build definition
This page describes sbt build definitions, including some “theory” and the syntax
of build.sbt. It assumes you have installed a recent version of sbt, such as sbt
46
1.2.7, know how to use sbt, and have read the previous pages in the Getting
Started Guide.
This page discusses the build.sbt build definition.
As part of your build definition you will specify the version of sbt that your
build uses. This allows people with different versions of the sbt launcher to
build the same projects with consistent results. To do this, create a file named
project/build.properties that specifies the sbt version as follows:
sbt.version=1.2.7
If the required version is not available locally, the sbt launcher will download
it for you. If this file is not present, the sbt launcher will choose an arbitrary
version, which is discouraged because it makes your build non-portable.
47
How build.sbt defines settings
On the left-hand side, name, version, and scalaVersion are keys. A key is
an instance of SettingKey[T], TaskKey[T], or InputKey[T] where T is the
expected value type. The kinds of key are explained below.
Because key name is typed to SettingKey[String], the := operator on name
is also typed specifically to String. If you use the wrong value type, the build
definition will not compile:
48
build.sbt may also be interspersed with vals, lazy vals, and defs. Top-level
objects and classes are not allowed in build.sbt. Those should go in the
project/ directory as Scala source files.
Keys
Built-in Keys The built-in keys are just fields in an object called Keys. A
build.sbt implicitly has an import sbt.Keys._, so sbt.Keys.name can be
referred to as name.
Custom Keys Custom keys may be defined with their respective creation
methods: settingKey, taskKey, and inputKey. Each method expects the type
of the value associated with the key as well as a description. The name of the
key is taken from the val the key is assigned to. For example, to define a key
for a new task called hello,
Here we have used the fact that an .sbt file can contain vals and defs in
addition to settings. All such definitions are evaluated before settings regardless
of where they are defined in the file.
Note: Typically, lazy vals are used instead of vals to avoid initial-
ization order problems.
49
sbt’s key-value pairs describing the subproject can keep around a fixed string
value for a setting such as name, but it has to keep around some executable code
for a task such as compile – even if that executable code eventually returns a
string, it has to be re-run every time.
A given key always refers to either a task or a plain setting. That is, “taskiness”
(whether to re-run each time) is a property of the key, not the value.
Using :=, you can assign a value to a setting and a computation to a task. For
a setting, the value will be computed once at project load time. For a task, the
computation will be re-run each time the task is executed.
For example, to implement the hello task from the previous section:
Types for tasks and settings From a type-system perspective, the Setting
created from a task key is slightly different from the one created from a setting
key. taskKey := 42 results in a Setting[Task[T]] while settingKey := 42
results in a Setting[T]. For most purposes this makes no difference; the task
key still creates a value of type T when the task executes.
The T vs. Task[T] type difference has this implication: a setting can’t depend
on a task, because a setting is evaluated only once on project load and is not
re-run. More on this in task graph.
In sbt shell, you can type the name of any task to execute that task. This is
why typing compile runs the compile task. compile is a task key.
50
If you type the name of a setting key rather than a task key, the value of
the setting key will be displayed. Typing a task key name executes the task
but doesn’t display the resulting value; to see a task’s result, use show <task
name> rather than plain <task name>. The convention for keys names is to
use camelCase so that the command line name and the Scala identifiers are the
same.
To learn more about any key, type inspect <keyname> at the sbt interactive
prompt. Some of the information inspect displays won’t make sense yet, but
at the top it shows you the setting’s value type and a brief description of the
setting.
Imports in build.sbt
You can place import statements at the top of build.sbt; they need not be
separated by blank lines.
There are some implied default imports, as follows:
import sbt._
import Keys._
(In addition, if you have auto plugins, the names marked under autoImport will
be imported.)
The settings can be written directly into the build.sbt file instead of putting
them inside a .settings(...) call. We call this the “bare style.”
This syntax is recommended for ThisBuild scoped settings and adding plugins.
See later section about the scoping and the plugins.
To depend on third-party libraries, there are two options. The first is to drop
jars in lib/ (unmanaged dependencies) and the other is to add managed depen-
dencies, which will look like this in build.sbt:
51
val derby = "org.apache.derby" % "derby" % "10.4.1.3"
This is how you add a managed dependency on the Apache Derby library, version
10.4.1.3.
The libraryDependencies key involves two complexities: += rather than :=,
and the % method. += appends to the key’s old value rather than replacing it,
this is explained in Task Graph. The % method is used to construct an Ivy
module ID from strings, explained in Library dependencies.
We’ll skip over the details of library dependencies until later in the Getting
Started Guide. There’s a whole page covering it later on.
Multi-project builds
Multiple subprojects
The name of the val is used as the subproject’s ID, which is used to refer to the
subproject at the sbt shell.
52
Optionally the base directory may be omitted if it’s the same as the name of
the val.
Now we can bump up version in one place, and it will be reflected across
subprojects when you reload the build.
Common settings Another way to factor out common settings across multi-
ple projects is to create a sequence named commonSettings and call settings
method on each project.
53
lazy val util = (project in file("util"))
.settings(
commonSettings,
// other settings
)
Dependencies
Projects in the build can be completely independent of one another, but usually
they will be related to one another by some kind of dependency. There are two
types of dependencies: aggregate and classpath.
In the above example, the root project aggregates util and core. Start up sbt
with two subprojects as in the example, and try compile. You should see that
all three projects are compiled.
In the project doing the aggregating, the root project in this case, you can control
aggregation per-task. For example, to avoid aggregating the update task:
[...]
update / aggregate is the aggregate key scoped to the update task. (See
scopes.)
Note: aggregation will run the aggregated tasks in parallel and with no defined
ordering between them.
54
Classpath dependencies A project may depend on code in another project.
This is done by adding a dependsOn method call. For example, if core needed
util on its classpath, you would define core as:
Now code in core can use classes from util. This also creates an ordering
between the projects when compiling them; util must be updated and compiled
before core can be compiled.
To depend on multiple projects, use multiple arguments to dependsOn, like
dependsOn(bar, baz).
Inter-project dependencies
On extremely large projects with many files and many subprojects, sbt can
perform less optimally at watching files have changed during interactively and
using a lot of disk and system I/O.
sbt has trackInternalDependencies and exportToInternal settings.
These can be used to control whether to trigger compilation of a de-
pendent subprojects when you call compile. Both keys will take one of
three values: TrackLevel.NoTracking, TrackLevel.TrackIfMissing, and
TrackLevel.TrackAlways. By default they are both set to TrackLevel.TrackAlways.
When trackInternalDependencies is set to TrackLevel.TrackIfMissing,
sbt will no longer try to compile internal (inter-project) dependencies automat-
ically, unless there are no *.class files (or JAR file when exportJars is true)
in the output directory.
55
When the setting is set to TrackLevel.NoTracking, the compilation of internal
dependencies will be skipped. Note that the classpath will still be appended, and
dependency graph will still show them as dependencies. The motivation is to
save the I/O overhead of checking for the changes on a build with many subpro-
jects during development. Here’s how to set all subprojects to TrackIfMissing.
The exportToInternal setting allows the dependee subprojects to opt out of the
internal tracking, which might be useful if you want to track most subprojects
except for a few. The intersection of the trackInternalDependencies and
exportToInternal settings will be used to determine the actual track level.
Here’s an example to opt-out one project:
If a project is not defined for the root directory in the build, sbt creates a default
one that aggregates all other projects in the build.
Because project hello-foo is defined with base = file("foo"), it will be
contained in the subdirectory foo. Its sources could be directly under foo, like
foo/Foo.scala, or in foo/src/main/scala. The usual sbt directory structure
applies underneath foo with the exception of build definition files.
Any .sbt files in foo, say foo/build.sbt, will be merged with the build defi-
nition for the entire build, but scoped to the hello-foo project.
If your whole project is in hello, try defining a different version (version :=
"0.6") in hello/build.sbt, hello/foo/build.sbt, and hello/bar/build.sbt.
Now show version at the sbt interactive prompt. You should get something
like this (with whatever versions you defined):
56
[info] hello-foo/*:version
[info] 0.7
[info] hello-bar/*:version
[info] 0.9
[info] hello/*:version
[info] 0.5
At the sbt interactive prompt, type projects to list your projects and project
<projectname> to select a current project. When you run a task like compile,
it runs on the current project. So you don’t necessarily have to compile the root
project, you could compile only a subproject.
You can run a task in another project by explicitly specifying the project ID,
such as subProjectID/compile.
Common code
The definitions in .sbt files are not visible in other .sbt files. In order to share
code between .sbt files, define one or more Scala files in the project/ directory
of the build root.
See organizing the build for details.
Task graph
57
Rather than thinking of settings as key-value pairs, a better analogy would be
to think of it as a directed acyclic graph (DAG) of tasks where the edges denote
happens-before. Let’s call this the task graph.
Terminology
scalacOptions := {
val ur = update.value // update task happens-before scalacOptions
val x = clean.value // clean task happens-before scalacOptions
// ---- scalacOptions begins here ----
ur.allConfigurations.take(3)
}
58
.value is not a normal Scala method call. build.sbt DSL uses a macro to lift
these outside of the task body. Both update and clean tasks are completed
by the time task engine evaluates the opening { of scalacOptions
regardless of which line it appears in the body.
See the following example:
> scalacOptions
[info] Updating {file:/xxx/}root...
[info] Resolving jline#jline;2.14.1 ...
[info] Done updating.
[info] 123
[info] 456
[success] Total time: 0 s, completed Jan 2, 2017 10:38:24 PM
59
name := "Hello",
scalacOptions := {
val ur = update.value // update task happens-before scalacOptions
if (false) {
val x = clean.value // clean task happens-before scalacOptions
}
ur.allConfigurations.take(3)
}
)
> run
[info] Updating {file:/xxx/}root...
[info] Resolving jline#jline;2.14.1 ...
[info] Done updating.
[info] Compiling 1 Scala source to /Users/eugene/work/quick-test/task-graph/target/scala-2.12/
[info] Running example.Hello
hello
[success] Total time: 0 s, completed Jan 2, 2017 10:45:19 PM
> scalacOptions
[info] Updating {file:/xxx/}root...
[info] Resolving jline#jline;2.14.1 ...
[info] Done updating.
[success] Total time: 0 s, completed Jan 2, 2017 10:45:23 PM
60
scalacOptions := {
val x = clean.value
update.value.allConfigurations.take(3)
}
Note whether .value calls are inlined, or placed anywhere in the task body,
they are still evaluated before entering the task body.
This is how sbt knows which tasks depend on which other tasks.
For example, if you inspect tree compile you’ll see it depends on an-
other key incCompileSetup, which it in turn depends on other keys like
dependencyClasspath. Keep following the dependency chains and magic
happens.
61
[info] ||| +-compile:externalDependencyClasspath::streams = Task[sbt.std.TaskStreams[sbt.In
[info] ||| | +-*/*:streamsManager = Task[sbt.std.Streams[sbt.Init$ScopedKey[_ <: Any]]]
[info] | | | |
[info] ||| +-compile:managedClasspath = Task[scala.collection.Seq[sbt.Attributed[java.io.Fi
[info] ||| | +-compile:classpathConfiguration = Task[sbt.Configuration]
[info] | | | | | +-compile:configuration = compile
[info] | | | | | +-*/*:internalConfigurationMap = <function1>
[info] | | | | | +-*:update = Task[sbt.UpdateReport]
[info] | | | | |
....
When you type compile sbt automatically performs an update, for example. It
Just Works because the values required as inputs to the compile computation
require sbt to do the update computation first.
In this way, all build dependencies in sbt are automatic rather than explicitly
declared. If you use a key’s value in another computation, then the computation
depends on that key.
62
[info] * -deprecation
[info] * -unchecked
[success] Total time: 0 s, completed Jan 2, 2017 11:44:44 PM
> ++2.11.8!
[info] Forcing Scala version to 2.11.8 on all projects.
[info] Reapplying settings...
[info] Set current project to Hello (in build file:/xxx/)
> show scalacOptions
[info] * -encoding
[info] * utf8
[info] * -unchecked
[success] Total time: 0 s, completed Jan 2, 2017 11:44:51 PM
There is no way to go the other direction. That is, a setting key can’t depend
on a task key. That’s because a setting key is only computed once on project
load, so the task would not be re-run every time, and tasks expect to re-run
every time.
// Bad example: The checksums setting cannot be defined in terms of the scalacOptions task!
checksums := scalacOptions.value
63
Here’s a realistic example. This rewires scalaSource in Compile key to a
different directory only when scalaBinaryVersion is "2.11".
scalaSource in Compile := {
val old = (scalaSource in Compile).value
scalaBinaryVersion.value match {
case "2.11" => baseDirectory.value / "src-2.11" / "main" / "scala"
case _ => old
}
}
Intro to Make The basic Makefile syntax looks like the following:
target: dependencies
[tab] system command1
[tab] system command2
1. Make checks if the target’s dependencies have been built, and builds any
of the dependencies that hasn’t been built yet.
2. Make runs the system commands in order.
CC=g++
CFLAGS=-Wall
all: hello
%.o: %.cpp
$(CC) $(CFLAGS) -c $< -o $@
64
Running make, it will by default pick the target named all. The target lists
hello as its dependency, which hasn’t been built yet, so Make will build hello.
Next, Make checks if the hello target’s dependencies have been built yet. hello
lists two targets: main.o and hello.o. Once those targets are created using
the last pattern matching rule, only then the system command is executed to
link main.o and hello.o to hello.
If you’re just running make, you can focus on what you want as the target, and
the exact timing and commands necessary to build the intermediate products
are figured out by Make. We can think of this as dependency-oriented pro-
gramming, or flow-based programming. Make is actually considered a hybrid
system because while the DSL describes the task dependencies, the actions are
delegated to system commands.
Rake This hybridity is continued for Make successors such as Ant, Rake, and
sbt. Take a look at the basic syntax for Rakefile:
The breakthrough made with Rake was that it used a programming language
to describe the actions instead of the system commands.
Summary
The core data structure of the build definition is a DAG of tasks, where the
edges denote happens-before relationships. build.sbt is a DSL designed to
65
express dependency-oriented programming, or flow-based programming, similar
to Makefile and Rakefile.
The key motivation for the flow-based programming is de-duplication, parallel
processing, and customizability.
Scopes
This page describes scopes. It assumes you’ve read and understood the previous
pages, build definition and task graph.
Previously we pretended that a key like name corresponded to one entry in sbt’s
map of key-value pairs. This was a simplification.
In truth, each key can have an associated value in more than one context, called
a scope.
Some concrete examples:
• if you have multiple projects (also called subprojects) in your build defini-
tion, a key can have a different value in each project.
• the compile key may have a different value for your main sources and
your test sources, if you want to compile them differently.
• the packageOptions key (which contains options for creating jar pack-
ages) may have different values when packaging class files (packageBin)
or packaging source code (packageSrc).
There is no single value for a given key name, because the value may differ
according to scope.
However, there is a single value for a given scoped key.
If you think about sbt processing a list of settings to generate a key-value map
describing the project, as discussed earlier, the keys in that key-value map
are scoped keys. Each setting defined in the build definition (for example in
build.sbt) applies to a scoped key as well.
Often the scope is implied or has a default, but if the defaults are wrong, you’ll
need to mention the desired scope in build.sbt.
Scope axes
66
• The subproject axis
• The dependency configuration axis
• The task axis
If you’re not familiar with the notion of axis, we can think of the RGB color
cube as an example:
In the RGB color model, all colors are represented by a point in the cube
whose axes correspond to red, green, and blue components encoded by a number.
Similarly, a full scope in sbt is formed by a tuple of a subproject, a configuration,
and a task value:
scalacOptions in (
Select(projA: Reference),
Select(Compile: ConfigKey),
Select(console.key)
)
67
Scoping by the subproject axis If you put multiple projects in a single
build, each project needs its own settings. That is, keys can be scoped according
to the project.
The project axis can also be set to ThisBuild, which means the “entire build”,
so a setting applies to the entire build rather than a single project. Build-level
settings are often used as a fallback when a project doesn’t define a project-
specific setting. We will discuss more on build-level settings later in this page.
By default, all the keys associated with compiling, packaging, and running are
scoped to a configuration and therefore may work differently in each configu-
ration. The most obvious examples are the task keys compile, package, and
run; but all the keys which affect those keys (such as sourceDirectories or
scalacOptions or fullClasspath) are also scoped to the configuration.
Another thing to note about a configuration is that it can extend other configu-
rations. The following figure shows the extension relationship among the most
common configurations.
68
Scoping by Task axis Settings can affect how a task works. For example,
the packageSrc task is affected by the packageOptions setting.
To support this, a task key (such as packageSrc) can be a scope for another
key (such as packageOptions).
The various tasks that build a package (packageSrc, packageBin, packageDoc)
can share keys related to packaging, such as artifactName and packageOptions.
Those keys can have distinct values for each packaging task.
Zero scope component Each scope axis can be filled in with an instance
of the axis type (analogous to Some(_)), or the axis can be filled in with the
special value Zero. So we can think of Zero as None.
Zero is a universal fallback for all scope axes, but its direct use should be
reserved to sbt and plugin authors in most cases.
Global is a scope that sets Zero to all axes: Zero / Zero / Zero. In other
words, Global / someKey is a shorthand for Zero / Zero / Zero / someKey.
If you create a setting in build.sbt with a bare key, it will be scoped to (current
subproject / configuration Zero / task Zero):
Run sbt and inspect name to see that it’s provided by ProjectRef(uri("file:/private/tmp/hello/"),
"root") / name, that is, the project is ProjectRef(uri("file:/Users/xxx/hello/"),
"root"), and neither configuration nor task scope are shown (which means
Zero).
A bare key on the right hand side is also scoped to (current subproject / config-
uration Zero / task Zero):
organization := name.value
The types of any of the scope axes have been method enriched to have a /
operator. The argument to / can be a key or another scope axis. So for example,
though there’s no good reason to do this, you could have an instance of the name
key scoped to the Compile configuration:
69
or you could set the name scoped to the packageBin task (pointless! just an
example):
or you could set the name with multiple scope axes, for example in the
packageBin task in the Compile configuration:
On the command line and in the sbt shell, sbt displays (and parses) scoped keys
like this:
70
• the current project will be used if you omit the project.
• a key-dependent configuration will be auto-detected if you omit the con-
figuration or task.
• fullClasspath specifies just a key, so the default scopes are used: current
project, a key-dependent configuration, and Zero task scope.
• Test / fullClasspath specifies the configuration, so this is
fullClasspath in the Test configuration, with defaults for the
other two scope axes.
• root / fullClasspath specifies the project root, where the project is
identified with the project id.
• root / Zero / fullClasspath specified the project root, and specifies
Zero for the configuration, rather than the default configuration.
• doc / fullClasspath specifies the fullClasspath key scoped to the doc
task, with the defaults for the project and configuration axes.
• ProjectRef(uri("file:/tmp/hello/"), "root") / Test /
fullClasspath specifies a project ProjectRef(uri("file:/tmp/hello/"),
"root"). Also specifies configurtion Test, leaves the default task axis.
• ThisBuild / version sets the subproject axis to “entire build” where the
build is ThisBuild, with the default configuration.
• Zero / fullClasspath sets the subproject axis to Zero, with the default
configuration.
• root / Compile / doc / fullClasspath sets all three scope axes.
Inspecting scopes
In sbt shell, you can use the inspect command to understand keys and their
scopes. Try inspect test:fullClasspath:
$ sbt
sbt:Hello> inspect Test / fullClasspath
[info] Task: scala.collection.Seq[sbt.internal.util.Attributed[java.io.File]]
[info] Description:
[info] The exported classpath, consisting of build products and unmanaged and managed, internal
[info] Provided by:
[info] ProjectRef(uri("file:/tmp/hello/"), "root") / Test / fullClasspath
[info] Defined at:
[info] (sbt.Classpaths.classpaths) Defaults.scala:1639
[info] Dependencies:
71
[info] Test / dependencyClasspath
[info] Test / exportedProducts
[info] Test / fullClasspath / streams
[info] Reverse dependencies:
[info] Test / testLoader
[info] Delegates:
[info] Test / fullClasspath
[info] Runtime / fullClasspath
[info] Compile / fullClasspath
[info] fullClasspath
[info] ThisBuild / Test / fullClasspath
[info] ThisBuild / Runtime / fullClasspath
[info] ThisBuild / Compile / fullClasspath
[info] ThisBuild / fullClasspath
[info] Zero / Test / fullClasspath
[info] Zero / Runtime / fullClasspath
[info] Zero / Compile / fullClasspath
[info] Global / fullClasspath
[info] Related:
[info] Compile / fullClasspath
[info] Runtime / fullClasspath
On the first line, you can see this is a task (as opposed to a setting, as ex-
plained in .sbt build definition). The value resulting from the task will have
type scala.collection.Seq[sbt.Attributed[java.io.File]].
“Provided by” points you to the scoped key that defines the value, in this case
ProjectRef(uri("file:/tmp/hello/"), "root") / Test / fullClasspath
(which is the fullClasspath key scoped to the Test configuration and the
ProjectRef(uri("file:/tmp/hello/"), "root") project).
“Dependencies” was discussed in detail in the previous page.
We’ll discuss “Delegates” later.
Try inspect fullClasspath (as opposed to the above example, inspect Test
/ fullClasspath) to get a sense of the difference. Because the configuration is
omitted, it is autodetected as Compile. inspect Compile / fullClasspath
should therefore look the same as inspect fullClasspath.
Try inspect This / Zero / fullClasspath for another contrast.
fullClasspath is not defined in the Zero configuration scope by default.
Again, for more details, see Interacting with the Configuration System.
You need to specify the scope if the key in question is normally scoped. For
example, the compile task, by default, is scoped to Compile and Test configu-
72
rations, and does not exist outside of those scopes.
To change the value associated with the compile key, you need to write Compile
/ compile or Test / compile. Using plain compile would define a new compile
task scoped to the current project, rather than overriding the standard compile
tasks which are scoped to a configuration.
If you get an error like “Reference to undefined setting”, often you’ve failed to
specify a scope, or you’ve specified the wrong scope. The key you’re using may
be defined in some other scope. sbt will try to suggest what you meant as part
of the error message; look for “Did you mean Compile / compile?”
One way to think of it is that a name is only part of a key. In reality, all keys
consist of both a name, and a scope (where the scope has three axes). The
entire expression Compile / packageBin / packageOptions is a key name, in
other words. Simply packageOptions is also a key name, but a different one
(for keys with no in, a scope is implicitly assumed: current project, Zero config,
Zero task).
Build-level settings
73
// other settings
)
Scope delegation
A scoped key may be undefined, if it has no value associated with it in its scope.
For each scope axis, sbt has a fallback search path made up of other scope values.
Typically, if a key has no associated value in a more-specific scope, sbt will try
to get a value from a more general scope, such as the ThisBuild scope.
This feature allows you to set a value once in a more general scope, allowing mul-
tiple more-specific scopes to inherit the value. We will discuss scope delegation
in detail later.
Appending values
Assignment with := is the simplest transformation, but keys have other methods
as well. If the T in SettingKey[T] is a sequence, i.e. the key’s value type is a
sequence, you can append to the sequence rather than replacing it.
Or, using the file() function from the sbt package for convenience:
74
Compile / sourceDirectories += file("source")
When settings are undefined Whenever a setting uses :=, +=, or ++= to
create a dependency on itself or another key’s value, the value it depends on
must exist. If it does not, sbt will complain. It might say “Reference to undefined
setting”, for example. When this happens, be sure you’re using the key in the
scope that defines it.
It’s possible to create cycles, which is an error; sbt will tell you if you do this.
Tasks based on other keys’ values You can compute values of some tasks
or settings to define or append a value for another task. It’s done by using
Def.task as an argument to :=, +=, or ++=.
As a first example, consider appending a source generator using the project base
directory and compilation classpath.
Other keys can be used when appending to an existing setting or task, just like
they can for assigning with :=.
For example, say you have a coverage report named after the project, and you
want to add it to the files removed by clean:
75
Scope delegation (.value lookup)
This page describes scope delegation. It assumes you’ve read and understood
the previous pages, build definition and scopes.
Now that we’ve covered all the details of scoping, we can explain the .value
lookup in detail. It’s ok to skip this section if this is your first time reading this
page.
To summarize what we’ve learned so far:
Inside of foo’s setting body a dependency on the scoped key Test / bar is
declared. However, despite Test / bar being undefined in projX, sbt is still
able to resolve Test / bar to another scoped key, resulting in foo initialized
as 2.
sbt has a well-defined fallback search path called scope delegation. This feature
allows you to set a value once in a more general scope, allowing multiple more-
specific scopes to inherit the value.
76
• Rule 1: Scope axes have the following precedence: the subproject axis, the
configuration axis, and then the task axis.
• Rule 2: Given a scope, delegate scopes are searched by substituting the
task axis in the following order: the given task scoping, and then Zero,
which is non-task scoped version of the scope.
• Rule 3: Given a scope, delegate scopes are searched by substituting the
configuration axis in the following order: the given configuration, its par-
ents, their parents and so on, and then Zero (same as unscoped configu-
ration axis).
• Rule 4: Given a scope, delegate scopes are searched by substituting the
subproject axis in the following order: the given subproject, ThisBuild,
and then Zero.
• Rule 5: A delegated scoped key and its dependent settings/tasks are eval-
uated without carrying the original context.
• Rule 1: Scope axes have the following precedence: the subproject axis, the
configuration axis, and then the task axis.
In other words, given two scope candidates, if one has more specific value on
the subproject axis, it will always win regardless of the configuration or the
task scoping. Similarly, if subprojects are the same, one with more specific
configuration value will always win regardless of the task scoping. We will see
more rules to define more specific.
Here we have a concrete rule for how sbt will generate delegate scopes given a
key. Remember, we are trying to show the search path given an arbitrary (xxx
/ yyy).value.
Exercise A: Given the following build definition:
77
"foo-" + (packageBin / scalaVersion).value
},
scalaVersion := "2.11.11"
)
1. "foo-2.11.11"
2. "foo-2.12.7"
3. something else?
If we write out the full scope again, it’s projX / Test / Zero. Also recall that
Test extends Runtime, and Runtime extends Compile.
Test / bar is undefined, but due to Rule 3 sbt will look for bar scoped in
projX / Test / Zero, projX / Runtime / Zero, and then projX / Compile
/ Zero. The last one is found, which is Compile / bar.
78
Rule 4: The subproject axis search path
1. "abc-com.example"
2. "abc-org.tempuri"
3. something else?
Scope axis precedence, again Exercise C: Given the following build defi-
nition:
1. "foo-2.12.2"
79
2. "foo-2.11.11"
3. something else?
1. List()
2. List(-Ywarn-unused-import)
3. something else?
You might want to look up quickly what is going on. This is where inspect
can be used.
80
sbt:projd> inspect projD / Compile / console / scalacOptions
[info] Task: scala.collection.Seq[java.lang.String]
[info] Description:
[info] Options for the Scala compiler.
[info] Provided by:
[info] ProjectRef(uri("file:/tmp/projd/"), "projD") / Compile / scalacOptions
[info] Defined at:
[info] /tmp/projd/build.sbt:9
[info] Reverse dependencies:
[info] projD / test
[info] projD / Compile / console
[info] Delegates:
[info] projD / Compile / console / scalacOptions
[info] projD / Compile / scalacOptions
[info] projD / console / scalacOptions
[info] projD / scalacOptions
[info] ThisBuild / Compile / console / scalacOptions
[info] ThisBuild / Compile / scalacOptions
[info] ThisBuild / console / scalacOptions
[info] ThisBuild / scalacOptions
[info] Zero / Compile / console / scalacOptions
[info] Zero / Compile / scalacOptions
[info] Zero / console / scalacOptions
[info] Global / scalacOptions
• All the scopes with projD scoping on the subproject axis are listed first,
then ThisBuild, and Zero.
• Within a subproject, scopes with Compile scoping on the configuration
axis are listed first, then falls back to Zero.
• Finally, the task axis scoping lists the given task scoping console / and
the one without.
• Rule 5: A delegated scoped key and its dependent settings/tasks are eval-
uated without carrying the original context.
81
method named drawShape on a trait Shape, its subclasses can override the
behavior even when drawShape is used by other methods in the Shape trait,
which is called dynamic dispatch.
In sbt, however, scope delegation can delegate a scope to a more general scope,
like a project-level setting to a build-level settings, but that build-level setting
cannot refer to the project-level setting.
Exercise E: Given the following build definition:
1. "2.12.2_0.1.0"
2. "2.11.11_0.1.0"
3. something else?
82
println("bippy" + (Compile / compile / scalacOptions).value.mkString)
}
)
1. "bippy-D4"
2. "bippy-D2-D4"
3. "bippy-D0-D3-D4"
4. something else?
someKey := {
val old = someKey.value
old :+ "x"
}
Retrieving the old value would cause delegation, and due to Rule 5, it will go
to another scoped key. Let’s get rid of += first, and annotate the delegates for
old values:
ThisBuild / scalacOptions := {
// Global / scalacOptions <- Rule 4
val old = (ThisBuild / scalacOptions).value
old :+ "-D0"
}
scalacOptions := {
// ThisBuild / scalacOptions <- Rule 4
val old = scalacOptions.value
old :+ "-D1"
}
83
Compile / scalacOptions := {
// ThisBuild / scalacOptions <- Rules 3 and 4
val old = (Compile / scalacOptions).value
old :+ "-D3"
},
Compile / compile / scalacOptions := {
// projF / Compile / scalacOptions <- Rules 1 and 2
val old = (Compile / compile / scalacOptions).value
old :+ "-D4"
},
test := {
println("bippy" + (Compile / compile / scalacOptions).value.mkString)
}
)
This becomes:
ThisBuild / scalacOptions := {
Nil :+ "-D0"
}
scalacOptions := {
List("-D0") :+ "-D1"
}
Library dependencies
This page assumes you’ve already read the earlier Getting Started pages, in
particular build definition, scopes, and task graph.
Library dependencies can be added in two ways:
84
Unmanaged dependencies
Managed Dependencies
The libraryDependencies key Most of the time, you can simply list your
dependencies in the setting libraryDependencies. It’s also possible to write
a Maven POM file or Ivy configuration file to externally configure your depen-
dencies, and have sbt use those external configuration files. You can learn more
about that here.
Declaring a dependency looks like this, where groupId, artifactId, and
revision are strings:
85
libraryDependencies += groupID % artifactID % revision
The % methods create ModuleID objects from strings, then you add those
ModuleID to libraryDependencies.
Of course, sbt (via Ivy) has to know where to download the module. If your
module is in one of the default repositories sbt comes with, this will just work.
For example, Apache Derby is in the standard Maven2 repository:
If you type that in build.sbt and then update, sbt should download Derby to
~/.ivy2/cache/org.apache.derby/. (By the way, update is a dependency of
compile so there’s no need to manually type update most of the time.)
Of course, you can also use ++= to add a list of dependencies all at once:
Assuming the scalaVersion for your build is 2.11.1, the following is identical
(note the double %% after "org.scala-tools"):
86
libraryDependencies += "org.scala-tools" %% "scala-stm" % "0.3"
The idea is that many dependencies are compiled for multiple Scala versions,
and you’d like to get the one that matches your project to ensure binary com-
patibility.
See Cross Building for some more detail on this.
Resolvers Not all packages live on the same server; sbt uses the standard
Maven2 repository by default. If your dependency isn’t on one of the default
repositories, you’ll have to add a resolver to help Ivy find it.
To add an additional repository, use
87
resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/reposito
resolvers += Resolver.mavenLocal
Overriding default resolvers resolvers does not contain the default re-
solvers; only additional ones added by your build definition.
sbt combines resolvers with some default repositories to form externalResolvers.
Therefore, to change or remove the default resolvers, you would need to override
externalResolvers instead of resolvers.
You may also use the type-safe version of Test configuration as follows:
Using plugins
Please read the earlier pages in the Getting Started Guide first, in particular you
need to understand build.sbt, task graph, library dependencies, before reading
this page.
88
What is a plugin?
A plugin extends the build definition, most commonly by adding new settings.
The new settings could be new tasks. For example, a plugin could add a
codeCoverage task which would generate a test coverage report.
Declaring a plugin
If your project is in directory hello, and you’re adding sbt-site plugin to the
build definition, create hello/project/site.sbt and declare the plugin depen-
dency by passing the plugin’s Ivy module ID to addSbtPlugin:
Not every plugin is located on one of the default repositories and a plugin’s
documentation may instruct you to also add the repository where it can be
found:
resolvers += Resolver.sonatypeRepo("public")
Plugins usually provide settings that get added to a project to enable the plugin’s
functionality. This is described in the next section.
A plugin can declare that its settings be automatically added to the build defi-
nition, in which case you don’t have to do anything to add them.
As of sbt 0.13.5, there is a new auto plugins feature that enables plugins to
automatically, and safely, ensure their settings and dependencies are on a project.
Many auto plugins should have their default settings automatically, however
some may require explicit enablement.
If you’re using an auto plugin that requires explicit enablement, then you have
to add the following to your build.sbt:
89
The enablePlugins method allows projects to explicitly define the auto plugins
they wish to consume.
Projects can also exclude plugins using the disablePlugins method. For ex-
ample, if we wish to remove the IvyPlugin settings from util, we modify our
build.sbt as follows:
> plugins
In file:/home/jsuereth/projects/sbt/test-ivy-issues/
sbt.plugins.IvyPlugin: enabled in scala-sbt-org
sbt.plugins.JvmPlugin: enabled in scala-sbt-org
sbt.plugins.CorePlugin: enabled in scala-sbt-org
sbt.plugins.JUnitXmlReportPlugin: enabled in scala-sbt-org
Here, the plugins output is showing that the sbt default plugins are all enabled.
sbt’s default settings are provided via three plugins:
site.settings
90
to enable it for that project.
If the build defines multiple projects, instead add it directly to the project:
Global plugins
Plugins can be installed for all your projects at once by declaring them in
~/.sbt/1.0/plugins/. ~/.sbt/1.0/plugins/ is an sbt project whose class-
path is exported to all sbt build definition projects. Roughly speaking, any
.sbt or .scala files in ~/.sbt/1.0/plugins/ behave as if they were in the
project/ directory for all projects.
You can create ~/.sbt/1.0/plugins/build.sbt and put addSbtPlugin() ex-
pressions in there to add plugins to all your projects at once. Because doing so
would increase the dependency on the machine environment, this feature should
be used sparingly. See Best Practices.
Available Plugins
• those for IDEs (to import an sbt project into your IDE)
• those supporting web frameworks, such as xsbt-web-plugin.
For more details, including ways of developing plugins, see Plugins. For best
practices, see Plugins-Best-Practices.
This page gets you started creating your own settings and tasks.
To understand this page, be sure you’ve read earlier pages in the Getting Started
Guide, especially build.sbt and task graph.
91
Defining a key
Keys is packed with examples illustrating how to define keys. Most of the keys
are implemented in Defaults.
Keys have one of three types. SettingKey and TaskKey are described in .sbt
build definition. Read about InputKey on the Input Tasks page.
Some examples from Keys:
The key constructors have two string parameters: the name of the key
("scalaVersion") and a documentation string ("The version of scala used
for building.").
Remember from .sbt build definition that the type parameter T in
SettingKey[T] indicates the type of value a setting has. T in TaskKey[T]
indicates the type of the task’s result. Also remember from .sbt build definition
that a setting has a fixed value until project reload, while a task is re-computed
for every “task execution” (every time someone types a command at the sbt
interactive prompt or in batch mode).
Keys may be defined in an .sbt file, a .scala file, or in an auto plugin. Any vals
found under autoImport object of an enabled auto plugin will be imported
automatically into your .sbt files.
Implementing a task
Once you’ve defined a key for your task, you’ll need to complete it with a task
definition. You could be defining your own task, or you could be planning to
redefine an existing task. Either way looks the same; use := to associate some
code with the task key:
92
val sum = 1 + 2
println("sum: " + sum)
sum
}
)
If the task has dependencies, you’d reference their value using value, as dis-
cussed in task graph.
The hardest part about implementing tasks is often not sbt-specific; tasks are
just Scala code. The hard part could be writing the “body” of your task that
does whatever you’re trying to do. For example, maybe you’re trying to format
HTML in which case you might want to use an HTML library (you would add a
library dependency to your build definition and write code based on the HTML
library, perhaps).
sbt has some utility libraries and convenience functions, in particular you can
often use the convenient APIs in IO to manipulate files and directories.
When depending on other tasks from a custom task using value, an important
detail to note is the execution semantics of the tasks. By execution semantics,
we mean exactly when these tasks are evaluated.
If we take sampleIntTask for instance, each line in the body of the task should
be strictly evaluated one after the other. That is sequential semantics:
sampleIntTask := {
val sum = 1 + 2 // first
println("sum: " + sum) // second
sum // third
}
In reality JVM may inline the sum to 3, but the observable effect of the task
will remain identical as if each line were executed one after the other.
Now suppose we define two more custom tasks startServer and stopServer,
and modify sampleIntTask as follows:
93
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "2.12.7"
> sampleIntTask
stopping...
starting...
sum: 3
[success] Total time: 1 s, completed Dec 22, 2014 5:00:00 PM
94
Figure 4: task-dependency
> sampleStringTask
stopping...
starting...
sum: 3
s: 3
[success] Total time: 1 s, completed Dec 22, 2014 5:30:00 PM
Cleanup task How should one implement stopServer task? The notion
of cleanup task does not fit into the execution model of tasks because tasks
are about tracking dependencies. The last operation should become the task
that depends on other intermediate tasks. For instance stopServer should
depend on sampleStringTask, at which point stopServer should be the
sampleStringTask.
95
Figure 5: task-dependency
> sampleStringTask
starting...
sum: 3
s: 3
stopping...
96
[success] Total time: 1 s, completed Dec 22, 2014 6:00:00 PM
Figure 6: task-dependency
Use plain Scala Another way of making sure that something happens
after some other thing is to use Scala. Implement a simple function in
project/ServerUtil.scala for example, and you can write:
sampleIntTask := {
ServerUtil.startServer
try {
val sum = 1 + 2
println("sum: " + sum)
} finally {
ServerUtil.stopServer
}
sum
}
If you find you have a lot of custom code, consider moving it to a plugin for
re-use across multiple builds.
It’s very easy to create a plugin, as teased earlier and discussed at more length
here.
This page has been a quick taste; there’s much much more about custom tasks
on the Tasks page.
97
sbt is recursive
build.sbt conceals how sbt really works. sbt builds are defined with Scala code.
That code, itself, has to be built. What better way than with sbt?
The project directory is another build inside your build, which knows how to
build your build. To distinguish the builds, we sometimes use the term proper
build to refer to your build, and meta-build to refer to the build in project.
The projects inside the metabuild can do anything any other project can do.
Your build definition is an sbt project.
And the turtles go all the way down. If you like, you can tweak the build defini-
tion of the build definition project, by creating a project/project/ directory.
Here’s an illustration.
Don’t worry! Most of the time you are not going to need all that. But under-
standing the principle can be helpful.
By the way: any time files ending in .scala or .sbt are used, naming them
build.sbt and Dependencies.scala are conventions only. This also means
that multiple files are allowed.
98
Tracking dependencies in one place
One way of using the fact that .scala files under project becomes part of the
build definition is to create project/Dependencies.scala to track dependen-
cies in one place.
import sbt._
object Dependencies {
// Versions
lazy val akkaVersion = "2.3.8"
// Libraries
val akkaActor = "com.typesafe.akka" %% "akka-actor" % akkaVersion
val akkaCluster = "com.typesafe.akka" %% "akka-cluster" % akkaVersion
val specs2core = "org.specs2" %% "specs2-core" % "2.4.17"
// Projects
val backendDeps =
Seq(akkaActor, specs2core % Test)
}
import Dependencies._
This technique is useful when you have a multi-project build that’s getting large,
and you want to ensure that subprojects have consistent dependencies.
In .scala files, you can write any Scala code, including top-level classes and
objects.
99
The recommended approach is to define most settings in a multi-project
build.sbt file, and using project/*.scala files for task implementations or
to share values, such as keys. The use of .scala files also depends on how
comfortable you or your team are with Scala.
For more advanced users, another way of organizing your build is to define
one-off auto plugins in project/*.scala. By defining triggered plugins, auto
plugins can be used as a convenient way to inject custom tasks and commands
across all subprojects.
• the basics of Scala. It’s undeniably helpful to be familiar with Scala syn-
tax. Programming in Scala written by the creator of Scala is a great
introduction.
• .sbt build definition
• your build definition is a big DAG of tasks and their dependencies.
• to create a Setting, call one of a few methods on a key: :=, +=, or ++=.
• each setting has a value of a particular type, determined by the key.
• tasks are special settings where the computation to produce the key’s value
will be re-run each time you kick off a task. Non-tasks compute the value
once, when first loading the build definition.
• Scopes
• each key may have multiple values, in distinct scopes.
• scoping may use three axes: configuration, project, and task.
• scoping allows you to have different behaviors per-project, per-task, or
per-configuration.
• a configuration is a kind of build, such as the main one (Compile) or the
test one (Test).
• the per-project axis also supports “entire build” scope.
100
• scopes fall back to or delegate to more general scopes.
• put most of your configuration in build.sbt, but use .scala build defi-
nition files for defining classes and larger task implementations.
• the build definition is an sbt project in its own right, rooted in the project
directory.
• Plugins are extensions to the build definition
• add plugins with the addSbtPlugin method in project/plugins.sbt
(NOT build.sbt in the project’s base directory).
If any of this leaves you wondering rather than nodding, please ask for help, go
back and re-read, or try some experiments in sbt’s interactive mode.
Good luck!
Advanced Notes
Since sbt is open source, don’t forget you can check out the source code too!
General Information
Credits
101
Grzegorz Kossakowski ([@gkossakowski][@gkossakowski]) worked on a better in-
cremental compiler algorithm called “name hashing” during his time on the
Scala team at Typesafe. Name hashing became the default incremental com-
piler in sbt 0.13.6 (2014). Lightbend later commissioned Grzegorz to refine
name hashing using a technique called class-based name hashing, which was
adopted by Zinc 1. Another notable contribution from Grzegorz was hosting a
series of meetups with @WarszawScaLa, and (with his arm in a sling!) guiding
the Warszaw Scala community to fix the infamous blank-line problem.
In May 2015, Dale Wijnand ([@dwijnand][@dwijnand]) became a committer
from the community after contributing features such as inThisBuild and -=.
From June 2015 to early 2016, Martin Duhem ([@Duhemm][@Duhemm]) joined
Typesafe as an intern, working on sbt. During this time, Martin worked on
crucial components such as making the compiler bridge configurable for Zinc,
and code generation for pseudo case classes (which later became Contraband).
Around this time, Eugene, Martin, and Dale started the sbt 1.x code-
base, splitting the code base into multiple modules: sbt/sbt, Zinc 1,
sbt/librarymanagement, sbt/util, and sbt/io. The aim was to make Zinc 1, an
incremental compiler usable by all build tools.
In August 2016, Dale joined the Tooling team at Lightbend. Dale and Eugene
oversaw the releases 0.13.12 through 0.13.16, as well as the development of sbt
1.0.
In spring 2017, the Scala Center joined the Zinc 1 development effort. Jorge
Vicente Cantero ([@jvican][@jvican]) has contributed a number of improvements
including the fix for the “as seen from” bug that had blocked Zinc 1.
According to git shortlog -sn --no-merges on sbt/sbt, sbt/zinc,
sbt/librarymanagement, sbt/util, sbt/io, sbt/contraband, and sbt/website
there were 9151 non-merge commits by 318 contributors.
102
• Jason Zaugg 40
• Josh Soref 39
• Krzysztof Romanowski 39
• Pierre DAL-PRA 36
• Andrzej Jozwik 33
• Antonio Cunei 30
• Aaron S. Hawley 29
• Guillaume Martres 25
• James Roper 24
• Chua Chee Seng (cheeseng) 24
• Paolo G. Giarrusso 23
• Matej Urbas 22
• Stu Hood 22
• Adriaan Moors 18
• Jean-Rémi Desjardins 16
• Sanjin Sehic 16
• Fedor Korotkov 14
• Andrew Johnson 13
• David Perez 13
• Havoc Pennington 13
• Liang Tang 12
• Peter Vlugter 12
• Taro L. Saito 10
• Paul Phillips 9
• Roberto Tyley 9
• Vojin Jovanovic 9
• William Benton 9
• �� (Yang Bo) 9
• Brian Topping 8
• Bruno Bieth 8
• Johannes Rudolph 8
• KAWACHI Takashi 8
• Ken Kaizu (krrrr38) 8
• Artyom Olshevskiy 7
• Eugene Platonov 7
• Matthew Farwell 7
• Michael Allman 7
• David Pratt 6
• Luca Milanesio 6
• Nepomuk Seiler 6
• Peiyu Wang 6
• Simeon H.K. Fitch 6
103
• Stephen Samuel 6
• Thierry Treyer 6
• James Earl Douglas 5
• Jean-Remi Desjardins 5
• Miles Sabin 5
• Seth Tisue 5
• qgd 5
• Anthony Whitford 4
• Bardur Arantsson 4
• Ches Martin 4
• Chris Birchall 4
• Daniel C. Sobral 4
• Heikki Vesalainen 4
• Krzysztof Nirski 4
• Lloyd Meta 4
• Michael Schmitz 4
• Orr Sella 4
• Philipp Dörfler 4
• Tim Harper 4
• Vasya Novikov 4
• Vincent Munier 4
• Jürgen Keck (j-keck) 4
• Richard Summerhayes (rasummer) 4
• Adam Warski 3
• Ben McCann 3
• Enno Runne 3
• Eric Bowman 3
• Henrik Engstrom 3
• Ian Forsey 3
• James Ward 3
• Jesse Kinkead 3
• Justin Pihony 3
• Kazuhiro Sera 3
• Krzysztof Borowski 3
• Lars Hupel 3
• Leif Wickland 3
• Lukas Rytz 3
• Max Worgan 3
• Oliver Wickham 3
• Olli Helenius 3
• Roman Timushev 3
• Simon Schäfer 3
104
• ZhiFeng Hu 3
• daniel-shuy 3
• Roland Schatz 3
• soc 3
• wpitula 3
• Alex Dupre 2
• Alexey Alekhin 2
• Allan Erskine 2
• Alois Cochard 2
• Andreas Flierl 2
• Anthony 2
• Antoine Gourlay 2
• Arnout Engelen 2
• Ben Hutchison 2
• Benjamin Darfler 2
• Brendan W. McAdams 2
• Brennan Saeta 2
• Brian McKenna 2
• Brian Smith 2
• BrianLondon 2
• Charles Feduke 2
• Christian Dedie 2
• Cody Allen 2
• Damien Lecan 2
• David Barri 2
• David Harcombe 2
• David Hotham 2
• Derek Wickern 2
• Eric D. Reichert 2
• Eric J. Christeson 2
• Evgeny Goldin 2
• Evgeny Vereshchagin 2
• Francois Armand (fanf42) 2
• Fred Dubois 2
• Heejong Lee 2
• Henri Kerola 2
• Hideki Ikio 2
• Ikenna Nwaiwu 2
• Ismael Juma 2
• Jakob Odersky 2
• Jan Berkel 2
• Jan Niehusmann 2
105
• Jarek Sacha 2
• Jens Halm 2
• Joachim Hofer 2
• Joe Barnes 2
• Johan Andrén 2
• Jonas Fonseca 2
• Josh Kalderimis 2
• Juan Manuel Caicedo Carvajal 2
• Justin Kaeser 2
• Konrad Malawski 2
• Lex Spoon 2
• Li Haoyi 2
• Lloyd 2
• Lukasz Piepiora 2
• Marcus Lönnberg 2
• Marko Elezovic 2
• Michael Parrott 2
• Mikael Vallerie 2
• Myyk Seok 2
• Ngoc Dao 2
• Nicolas Rémond 2
• Oscar Vargas Torres 2
• Paul Draper 2
• Paulo “JCranky” Siqueira 2
• Petro Verkhogliad 2
• Piotr Kukielka 2
• Robin Green 2
• Roch Delsalle 2
• Roman Iakovlev 2
• Scott Royston 2
• Simon Hafner 2
• Sukant Hajra 2
• Suzanne Hamilton 2
• Tejas Mandke 2
• Thomas Koch 2
• Thomas Lockney 2
• Tobias Neef 2
• Tomasz Bartczak 2
• Travis 2
• Vitalii Voloshyn 2
• Wei Chen 2
• Wojciech Langiewicz 2
106
• Xin Ren 2
• Zava 2
• amishak 2
• beolnix 2
• ddworak 2
• drdamour 2
• Eric K Richardson (ekrich) 2
• fsi206914 2
• henry 2
• kaatzee 2
• kalmanb 2
• nau 2
• qvaughan 2
• sam 2
• softprops 2
• tbje 2
• timt 2
• Aaron D. Valade 1
• Alexander Buchholtz 1
• Alexandr Nikitin 1
• Alexandre Archambault 1
• Alexey Levan 1
• Anatoly Fayngelerin 1
• Andrea 1
• Andrew D Bate 1
• Andrew Miller 1
• Ashley Mercer 1
• Bruce Mitchener 1
• Cause Cheng 1
• Cause Chung 1
• Christian Krause 1
• Christophe Vidal 1
• Claudio Bley 1
• Daniel Peebles 1
• Denis T 1
• Devis Lucato 1
• Dmitry Melnichenko 1
• EECOLOR 1
• Edward Samson 1
• Erik Bakker 1
• Erik Bruchez 1
• Ethan 1
107
• Federico Ragona 1
• Felix Leipold 1
• Geoffroy Couprie 1
• Gerolf Seitz 1
• Gilad Hoch 1
• Gregor Heine 1
• HairyFotr 1
• Heiko Seeberger 1
• Holden Karau 1
• Hussachai Puripunpinyo 1
• Jacques 1
• Jakob Grunig 1
• James Koch 1
• Jan Polák 1
• Jan Ziniewicz 1
• Jisoo Park 1
• Joonas Javanainen 1
• Joscha Feth 1
• Josef Vlach 1
• Joseph Earl 1
• João Costa 1
• Justin Ko 1
• Kamil Kloch 1
• Kazuyoshi Kato 1
• Kevin Scaldeferri 1
• Knut Petter Meen 1
• Krzysztof 1
• Kunihiko Ito 1
• LMnet 1
• Luc Bourlier 1
• Lucas Mogari 1
• Lutz Huehnken 1
• Mal Graty 1
• Marcos Savoury 1
• Marek Żebrowski 1
• Markus Siemens 1
• Martynas Mickevicius 1
• Martynas Mickevičius 1
• Michael Bayne 1
• Michael Ledin 1
• Nathan Hamblen 1
• Nyavro 1
108
• OlegYch 1
• Olivier ROLAND 1
• Pavel Penkov 1
• Pedro Larroy 1
• Peter Pan 1
• Piotr Kukiełka 1
• Rikard Pavelic 1
• Robert Jacob 1
• Rogach 1
• Sergey Andreev 1
• Shanbin Wang 1
• Shane Hender 1
• Simon Olofsson 1
• Stefan Zeiger 1
• Stephen Duncan Jr 1
• Steve Gury 1
• Sören Brunk 1
• Thomas Grainger 1
• Tim Sheppard 1
• Todor Todorov 1
• Toshiyuki Takahashi 1
• Travis Brown 1
• Tsubasa Irisawa 1
• Victor Hiairrassary 1
• Yasuo Nakanishi 1
• Yoshitaka Fujii 1
• adinath 1
• albuch 1
• cchantep 1
• cdietze 1
• choucri 1
• hokada 1
• joiskov 1
• jozic 1
• jyane 1
• k.bigwheel 1
• kavedaa 1
• mmcbride 1
• pishen tsai 1
• sanjiv sahayam 1
• saturday06 1
• seroperson 1
109
• slideon 1
• thricejamie 1
• todesking 1
• totem3 1
• upescatore 1
• valydia 1
• walidbenchikha 1
• Wiesław Popielarski 1
• Łukasz Indykiewicz 1
• Josh Cough
• Nolan Darilek
• Viktor Klang
• David R. MacIver
• Ross McDonald
• Andrew O’Malley
• Jorge Ortiz
• Mikko Peltonen
• Ray Racine
• Stuart Roebuck
• Harshad RJ
• Tony Sloane
• Francisco Treacy
• Vesa Vilhonen
The sbt ecosystem would not be the same without so many awesome plugins.
Here are some of the plugins and their contributors:
110
• sbt-docker by Marcus Lönnberg
• tut by Rob Norris (tpolecat)
• sbt-release by Gerolf Seitz
• sbt-jmh by Konrad Malawski (ktoso)
• sbt-updates by Roman Timushev
• xsbt-web-plugin by James Earl Douglas and Artyom Olshevskiy
• sbt-scoverage by Stephen Samuel and Mikko Koponen
• sbt-web by Lightbend (Christopher Hunt, Peter Vlugter, et al)
• sbt-buildinfo by Eugene Yokota (eed3si9n)
• sbt-pack by Taro L. Saito (xerial)
• sbt-onejar by Jason Zaugg (retronym)
• sbt-git by Josh Suereth
• sbt-scalariform by Heiko Seeberger, Daniel Trinh, et al
• ensime-sbt by Sam Halliday (fommil)
• sbt-fresh by Heiko Seeberger
• sbt-web-scalajs by Vincent Munier
• sbt-sonatype by Taro L. Saito (xerial)
• sbt-sublime by Orr Sella
• sbt-errors-summary by Martin Duhem
• sbt-bintray by Doug Tangren (softprops)
• Migration Manager by Lightbend (Mirco Dotta, Seth Tisue, et al)
• sbt-protobuf by Gerolf Seitz and Kenji Yoshida (xuwei-k)
• sbt-site by Jonas Fonseca, Josh Suereth, et al
• sbt-doctest by KAWACHI Takashi
• sbt-robovm by Jan Polák
• scalastyle-sbt-plugin by Matthew Farwell
• sbt-microsites by 47 Degrees (Juan Pedro Moreno, Javier de Silóniz
Sandino, et al)
• sbt-header by Heiko Seeberger and Benedikt Ritter
• sbt-groll by Heiko Seeberger
• sbt-ctags by Cody Allen
• sbt-aws-lambda by Gilt (Brendan St John, et al)
• sbt-heroku by Heroku (Joe Kutner)
• sbt-dynver by Dale Wijnand
• sbt-unidoc by Eugene Yokota and Peter Vlugter
• sbt-docker-compose by Tapad (Kurt Kopchik et al)
• sbt-coveralls by Ian Forsey and Stephen Samuel
• gatling-sbt by Pierre Dal-Pra
• sbt-boilerplate by Johannes Rudolph
• fm-sbt-s3-resolver by Tim Underwood
• sbt-reactjs by Dan Di Spaltro
• sbt-scalabuff by Aloïs Cochard
111
• sbt-pgp by Josh Suereth
• jacoco4sbt by Joachim Hofer
• sbt-s3-resolver by Alexey Alekhin (laughedelic)
• sbt-maven-plugin by Shiva Wu
• sbt-newrelic by Gilt (Gary Coady et al)
• naptime by Coursera (Brennan Saeta, Bryan Kane et al)
• neo-sbt-scalafmt by Lucid Software (Paul Draper et al)
• Courier by Coursera (Joe Betz et al)
• sbt-optimizer by Johannes Rudolph
• sbt-appengine by Eugene Yokota (eed3si9n) and Yasushi Abe
• sbt/sbt-ghpages by Josh Suereth
• kotlin-plugin by Perry (pfn)
• sbt-avro by Juan Manuel Caicedo Carvajal (cavorite), Ben McCann, et al
• sbt-aspectj by Lightbend (Peter Vlugter et al)
• sbt-crossproject Denys Shabalin and Guillaume Massé
• sbt-scapegoat by Stephen Samuel
• sbt-dependency-graph-sugar by Gilt (Brendan St John et al)
• sbt-aether-deploy by Arktekk (Erlend Hamnaberg et al)
• sbt-spark-submit by Forest Fang
• sbt-proguard by Lightbend (Peter Vlugter et al)
• Jenkins CI sbt plugin by Uzi Landsmann
• sbt-quickfix by Dave Cleaver
• sbt-growl-plugin Doug Tangren (softprops)
• sbt-dependency-check by Alexander v. Buchholtz
• sbt-structure by JetBrains (Justin Kaeser et al)
• sbt-typescript by Brandon Arp
• sbt-javacv by Bytedeco (Lloyd Chan et al)
• sbt-stats by Orr Sella
• sbt-rig by Verizon (Timothy Perrett et al)
• sbt-swagger-codegen by UniCredit (Andrea Peruffo, Francesco MDE, et
al)
• sbt-pom-reader by Josh Suereth
• sbt-class-diagram by Kenji Yoshida (xuwei-k)
Kudos also to people who have answered questions on Stack Overflow (Jacek
Laskowski, Lukasz Piepiora, et al) and sbt Gitter channel, and many who have
reported issues and contributed ideas on GitHub.
Thank you all.
112
Community Plugins
sbt Organization
The sbt organization is available for use by any sbt plugin. Developers who
contribute their plugins into the community organization will still retain control
over their repository and its access. The goal of the sbt organization is to
organize sbt software into one central location.
A side benefit to using the sbt organization for projects is that you can use
gh-pages to host websites under the https://www.scala-sbt.org domain.
Lightbend has provided a freely available Ivy Repository for sbt projects to
use. This Ivy repository is mirrored from the freely available Bintray service. If
you’d like to submit your plugin, please follow these instructions: Bintray For
Plugins.
[Edit] this page to submit a pull request that adds your plugin to the list.
Documentation plugins
113
• sbt-unidoc: create unified API documentation across subprojects.
• sbt-ghpages: publish generated sites to GitHub pages.
• sbt-class-diagram: generate class diagrams from Scala source code.
• sbt-api-mappings: generate Scaladoc apiMappings for common Scala li-
braries.
• literator: generate literate-style markdown docs from your sources.
• sbt-example: generate ScalaTest test suites from examples in Scaladoc.
Release plugins
114
• sbt-appengine deploy your webapp to Google App Engine.
• sbt-marathon: deploy applications on Apache Mesos using the Marathon
framework.
Test plugins
115
• sbt-multi-jvm: run tests using multiple JVMs.
• sbt-scalaprops: scalaprops property-based testing integration.
• sbt-testng: TestNG framework integration.
• sbt-jcstress: Java Concurrency Stress Test (jcstress) integration.
• sbt-stryker4s: Test your tests with mutation testing.
116
Database plugins
Framework-specific plugins
117
Code coverage plugins
In-house plugins
Verification plugins
The community repository has the following guideline for artifacts published to
it:
1. All published artifacts are the authors own work or have an appropriate
license which grants distribution rights.
2. All published artifacts come from open source projects, that have an open
patch acceptance policy.
3. All published artifacts are placed under an organization in a DNS domain
for which you have the permission to use or are an owner (scala-sbt.org is
available for sbt plugins).
4. All published artifacts are signed by a committer of the project (coming
soon).
118
This will make your plugins available for all sbt users without additional config-
uration (besides declaring a dependency on your plugin).
To do this, we need to perform the following steps:
Now, we’ll create a repository to host our personal sbt plugins. In bintray, create
a generic repository called sbt-plugins.
First, go to your user page and click on the new repository link:
You should see the following dialog:
Fill it out similarly to the above image, the settings are:
• Name: sbt-plugins
• Type: Generic
• Desc: My sbt plugins
• Tags: sbt
Once this is done, you can begin to configure your sbt-plugins to publish to
bintray.
Next, a make sure your build.sbt file has the following settings
119
// This is an example. sbt-bintray requires licenses to be specified
// (using a canonical name).
ThisBuild / licenses += ("Apache-2.0", url("https://www.apache.org/licenses/LICENSE-2.0.html
Make sure your project has a valid license specified, as well as unique name and
organization.
Make a release
Once your build is configured, open the sbt console in your build and run
sbt> publish
The plugin will ask you for your credentials. If you don’t know where they are,
you can find them on Bintray.
This will get you your password. The sbt-bintray plugin will save your API key
for future use.
NOTE: We have to do this before we can link our package to the sbt org.
Now that your plugin is packaged on bintray, you can include it in the commu-
nity sbt repository. To do so, go to the Community sbt repository screen.
120
1. Click the green include my package button and select your plugin.
2. Search for your plugin by name and click on the link.
3. Your request should be automatically filled out, just click send
4. Shortly, one of the sbt repository admins will approve your link request.
From here on, any releases of your plugin will automatically appear in the
community sbt repository. Congratulations and thank you so much for your
contributions!
If you’re a member of the sbt organization on bintray, you can link your package
to the sbt organization, but via a different means. To do so, first navigate to
the plugin you wish to include and click on the link button:
After clicking this you should see a link like the following:
Click on the sbt/sbt-plugin-releases repository and you’re done! Any future
releases will be included in the sbt-plugin repository.
Summary
After setting up the repository, all new releases will automatically be included
the sbt-plugin-releases repository, available for all users. When you create a
new plugin, after the initial release you’ll have to link it to the sbt community
repository, but the rest of the setup should already be completed. Thanks for
you contributions and happy hacking.
Setup Notes
Terminal encoding
The character encoding used by your terminal may differ from Java’s default
encoding for your platform. In this case, you will need to add the option
-Dfile.encoding=<encoding> in your sbt script to set the encoding, which
might look like:
121
java -Dfile.encoding=UTF8
If you find yourself running out of permgen space or your workstation is low on
memory, adjust the JVM configuration as you would for any application. For
example a common set of memory-related options is:
Boot directory
sbt-launch.jar is just a bootstrap; the actual meat of sbt, and the Scala
compiler and standard library, are downloaded to the shared directory
$HOME/.sbt/boot/.
To change the location of this directory, set the sbt.boot.directory system
property in your sbt script. A relative path will be resolved against the current
working directory, which can be useful if you want to avoid sharing the boot
directory between projects. For example, the following uses the pre-0.11 style
of putting the boot directory in project/boot/:
java -Dsbt.boot.directory=project/boot/
HTTP/HTTPS/FTP Proxy
On Unix, sbt will pick up any HTTP, HTTPS, or FTP proxy settings from the
standard http_proxy, https_proxy, and ftp_proxy environment variables. If
you are behind a proxy requiring authentication, your sbt script must also
pass flags to set the http.proxyUser and http.proxyPassword properties
for HTTP, ftp.proxyUser and ftp.proxyPassword properties for FTP, or
https.proxyUser and https.proxyPassword properties for HTTPS.
For example,
On Windows, your script should set properties for proxy host, port, and if
applicable, username and password. For example, for HTTP:
Replace http with https or ftp in the above command line to configure HTTPS
or FTP.
122
Using Sonatype
Sonatype setup
The OSSRH Guide walks you through the required process of setting up the
account with Sonatype. It’s as simple as creating a Sonatype’s JIRA account
and then a New Project ticket. When creating the account, try to use the same
domain in your email address that the project is hosted on. It makes it easier for
Sonatype to validate the relationship with the groupId requested in the ticket,
but it is not the only method used to confirm the ownership.
Creation of the New Project ticket is as simple as:
After creating your Sonatype account on JIRA, you can log in to the Nexus
Repository Manager using the same credentials, although this is not required in
the guide, it can be helpful later to check on published artifacts.
sbt setup
123
step 1: PGP Signatures Follow Working with PGP Signatures.
First, you should install GnuGP, and verify the version:
$ gpg --version
gpg (GnuPG/MacGPG2) 2.2.8
libgcrypt 1.8.3
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
$ gpg --gen-key
$ gpg --list-keys
/home/foo/.gnupg/pubring.gpg
------------------------------
step 2: sbt-pgp With the PGP key you want to use, you can sign the artifacts
you want to publish to the Sonatype repository with the sbt-pgp plugin. Follow
the instructions for the plugin and you’ll have PGP signed artifacts in no time.
In short, add the following line to your ~/.sbt/1.0/plugins/gpg.sbt file to
enable it globally for SBT projects:
• make sure that the gpg command is in PATH available to the sbt,
• add useGpg := true to your build.sbt to make the plugin gpg-aware
124
step 3: Credentials The credentials for your Sonatype OSSRH account need
to be stored somewhere safe (e.g. NOT in the repository). Common convention
is a ~/.sbt/1.0/sonatype.sbt file, with the following:
125
// Remove all additional repository other than Maven Central from POM
ThisBuild / pomIncludeRepository := { _ => false }
ThisBuild / publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots")
else Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
ThisBuild / publishMavenStyle := true
The full format of a pom.xml (an end product of the project configuration used
by Maven) file is outlined here. You can add more data to it with the pomExtra
option in build.sbt.
> publishSigned
Check the published artifacts in the Nexus Repository Manager (same login as
Sonatype’s Jira account).
Close the staging repository and promote the release to central, by hitting
“Close” button, then “Release” button.
Optional steps
sbt-sonatype
To simplify the usage of the Sonatype’s Nexus, add the following line to
project/plugins.sbt to import the sbt-sonatype plugin to your project:
This plugin will facilitate the publishing process, but in short, these are the
main steps for publishing the libraries to the repository:
126
4. Close the staging repository and promote the release to central:
sonatypeRelease
Publishing tips Use staged releases to test across large projects of indepen-
dent releases before pushing the full project.
To automate the publishing approach above with the sbt-release plugin, you
should simply add the publishing commands as steps in the releaseProcess
task:
...
releaseStepCommand("sonatypeOpen \"your groupId\" \"Some staging name\""),
...
releaseStepCommand("publishSigned"),
...
releaseStepCommand("sonatypeRelease"),
...
Contributing to sbt
Below is a running list of potential areas of contribution. This list may become
out of date quickly, so you may want to check on the sbt-dev mailing list if you
are interested in a specific topic.
127
• ‘compile’ produces an Analysis of the source code containing
– Source dependencies
– Inter-project source dependencies
– Binary dependencies (jars + class files)
– data structure representing the API of the source code There
is some code already for generating dot files that isn’t hooked
up, but graphing dependencies and inheritance relationships is a
general area of work.
• ‘update’ produces an Update Report mapping Configura-
tion/ModuleID/Artifact to the retrieved File
• Ivy produces more detailed XML reports on dependencies. These
come with an XSL stylesheet to view them, but this does not scale to
large numbers of dependencies. Working on this is pretty straightfor-
ward: the XML files are created in ~/.ivy2 and the .xsl and .css
are there as well, so you don’t even need to work with sbt. Other
approaches described in the email thread
• Tasks are a combination of static and dynamic graphs and it would
be useful to view the graph of a run
• Settings are a static graph and there is code to generate the dot files,
but isn’t hooked up anywhere.
2. There is support for dependencies on external projects, like on GitHub. To
be more useful, this should support being able to update the dependencies.
It is also easy to extend this to other ways of retrieving projects. Support
for svn and hg was a recent contribution, for example.
3. If you like parsers, sbt commands and input tasks are written using cus-
tom parser combinators that provide tab completion and error handling.
Among other things, the efficiency could be improved.
4. The javap task hasn’t been reintegrated
5. Implement enhanced 0.11-style warn/debug/info/error/trace commands.
Currently, you set it like any other setting:
warn test:run
Also, trace is currently an integer, but should really be an abstract data type.
6. Each sbt version has more aggressive incremental compilation and repro-
ducing bugs can be difficult. It would be helpful to have a mode that
generates a diff between successive compilations and records the options
passed to scalac. This could be replayed or inspected to try to find the
cause.
128
Documentation
1. There’s a lot to do with this documentation. If you check it out from git,
there’s a directory called Dormant with some content that needs going
through.
2. the main page mentions external project references (e.g. to a git repo) but
doesn’t have anything to link to that explains how to use those.
3. API docs are much needed.
4. Find useful answers or types/methods/values in the other docs, and pull
references to them up into /faq or /Name-Index so people can find the
docs. In general the /faq should feel a bit more like a bunch of pointers
into the regular docs, rather than an alternative to the docs.
5. A lot of the pages could probably have better names, and/or little 2-4
word blurbs to the right of them in the sidebar.
Changes
Many of the case classes are replaced with pseudo case classes generated using
Contraband. Migrate .copy(foo = xxx) to withFoo(xxx). Suppose you have
m: ModuleID, and you’re currently calling m.copy(revision = "1.0.1"). Here
how you can migrate it:
m.withRevision("1.0.1")
SbtPlugin
sbt 0.13, sbt 1.0, and sbt 1.1 required sbtPlugin setting and scripted plugin to
develop an sbt plugin. sbt 1.2.1 combined both into SbtPlugin plugin.
Remove scripted-plugin from project/plugins.sbt, and just use:
129
sbt version specific source directory
If you are cross building an sbt plugin, one escape hatch we have is
sbt version specific source directory src/main/scala-sbt-0.13 and
src/main/scala-sbt-1.0. In there you can define an object named
PluginCompat as follows:
package sbtfoo
import sbt._
import Keys._
object PluginCompat {
type UpdateConfiguration = sbt.librarymanagement.UpdateConfiguration
In sbt 0.13 keys were scoped with 2 different syntaxes: one for sbt’s shell and
one for in code.
Starting sbt 1.1.0, the syntax for scoping keys has been unified for both the shell
and the build definitions to the slash syntax as follows:
130
They are now written as:
sbt:hello> name
[info] hello
sbt:hello> ThisBuild / version
[info] 1.0.0-SNAPSHOT
sbt:hello> show Compile / scalacOptions
[info] * -Xlint
sbt:hello> show Compile / console / scalacOptions
[info] * -Xlint
sbt:hello> Test / fork
[info] true
Before sbt 0.13 (sbt 0.9 to 0.12) it was very common to see in builds the usage
of three aspects of sbt:
The release of sbt 0.13 (which was over 3 years ago!) introduced the .value DSL
which allowed for much easier to read and write code, effectively making the first
two aspects redundant and they were removed from the official documentation.
Similarly, sbt 0.13’s introduction of multi-project build.sbt made the Build
trait redundant. In addition, the auto plugin feature that’s now standard in sbt
0.13 enabled automatic sorting of plugin settings and auto import feature, but
it made Build.scala more difficult to maintain.
As they are removed in sbt 1.0.0, and here we’ll help guide you to how to migrate
your code.
131
Migrating sbt 0.12 style operators With simple expressions such as:
a <<= aTaskDef
b <+= bTaskDef
c <++= cTaskDefs
a := aTaskDef.value
b += bTaskDef.value
c ++= cTaskDefs.value
sett1 := "s1"
sett2 := "s2"
sett3 <<= (sett1, sett2)(_ + _)
(Remember you can define tasks in terms of settings, but not the other way
round)
With the .value DSL you don’t have to know or remember if your key is a
SettingKey or a TaskKey:
sett1 := "s1"
sett2 := "s2"
sett3 := sett1.value + sett2.value
132
task1 := { println("t1"); "t1" }
task2 := { println("t2"); "t2" }
task3 := { println(task1.value + task2.value); task1.value + task2.value }
task4 := { println(sett1.value + sett2.value); sett1.value + sett2.value }
a <<= a dependsOn b
define it as:
a := (a dependsOn b).value
Note: You’ll need to use the <<= operator with .triggeredBy and .runBefore
in sbt 0.13.13 and earlier due to issue #1444.
Migrating when you need to set Tasks For keys such as sourceGenerators
and resourceGenerators which use sbt’s Task type:
val sourceGenerators =
settingKey[Seq[Task[Seq[File]]]]("List of tasks that generate sources")
val resourceGenerators =
settingKey[Seq[Task[Seq[File]]]]("List of tasks that generate resources")
or in general,
133
Migrating with InputKey When using InputKey instead of:
run := docsRunSetting.evaluated
import sbt._
import Keys._
import xyz.XyzPlugin.autoImport._
134
val shared = Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.12.1"
)
135
Note: Build traits is deprecated, but you can still use project/*.scala file
to organize your build and/or define ad-hoc plugins. See Organizing the build.
In 0.13.x, you use other repositories instead of the Maven Central repository:
sbt 1.2.1
Forward bincompat breakage If you are writing a plugin, please use 1.2.1+,
and avoid 1.2.0.
We unintentionally broke forward binary compatibility in 1.2.0. If someone
publishes an sbt plugin using sbt 1.2.0, it cannot be used from sbt 1.0.x or
1.1.x. sbt 1.2.1 reverts the change, so the forward compatibility is restored.
Unfortunately, this means we won’t be able to use varargs in inThisBuild(...)
etc again.
Note that we might eventually break forward compatibility, like we did in 0.13.5
for AutoPlugin, but only when the tradeoff is worth it.
136
The original issue was that unified slash syntax doesn’t pick the configuration
names when the configuration is not part of the subproject. Since this warning
is immaterial, we are removing them in this patch release.
One thing the plugin authors can start doing is declaring the custom configura-
tion as hidden, and adding them into the subprojects as follows:
import sbt._
import sbt.Keys._
....
}
We are also looking into improving unified slash syntax parser to make it more
robust.
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
builds, porting plugins, and submitting and reviewing pull requests.
sbt 1.2.1 was brought to you by 4 contributors, according to git shortlog
-sn --no-merges v1.2.1...v1.2.0 on sbt, zinc, librarymanagement, util, io,
launcher-packege, and website: Eugene Yokota, Aaron S. Hawley, Ethan Atkins,
and Allan Renucci. Thanks! Also special thanks to Ches Martin and Yoshida-
san for reporting these issues.
sbt 1.2.0
137
SbtPlugin for plugin development SbtPlugin is a plugin to declare a
project for sbt plugins. This automatically brings in scripted tests, and sets
sbtPlugin := true.
Cross JDK forking For forked run and test, java++ can now switch Java
Home.
sbt:helloworld> run
[info] Running (fork) Hello
[info] 1.8.0_171
sbt:helloworld> java++ 10!
[info] Reapplying settings...
sbt:helloworld> run
[info] Running (fork) Hello
[info] 10.0.1
sbt will try to detect Java homes into discoveredJavaHomes setting, supporting
shyiko/jabba. This can be augmented by Global / javaHomes:
This feature is intended for testing your library in an older JVM to check com-
patibility.
#4139 by @2m, [@cunei][@cunei], and [@eed3si9n][@eed3si9n]
This will aggregate only the subproject where ++2.12.7 is valid, which is useful
when you have a build where some subprojects are 2.11 only etc.
138
Composite project sbt 1.2.0 introduces “composite project” trait, which
allows plugin authors to generate subprojects, for example for cross building.
trait CompositeProject {
def componentProjects: Seq[Project]
}
The aim of the plugin is to support a generic notion of cross building (Scala
version, platform, etc) expressed using subprojects. In the above projectMarix
will produce three subprojects: coreJVM2_12, coreJVM2_11, and appJVM2_12.
Semantic Version selector API sbt 1.2.0 introduces Semantic Version se-
lector on VersionNumber() datatype supporting basic match, comparison (<=,
<, >=, >), combination (>1.0.0 <2.0.0, ||), ranges (A.B.C - D.E.F), and
wildcard (2.12.x).
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector(">=2.12"))
res1: Boolean = true
139
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("<2.12"))
res2: Boolean = false
scala> VersionNumber("2.13.0-M4").matchesSemVer(SemanticSelector("2.13"))
res3: Boolean = false
scala> VersionNumber("2.12.5").matchesSemVer(SemanticSelector("2.12.x"))
res5: Boolean = true
$ cat /tmp/extra.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.7")
$ sbt -addPluginSbtFile=/tmp/extra.sbt
...
sbt:helloworld> plugins
In file:/xxxx/hellotest/
...
sbtassembly.AssemblyPlugin: enabled in root
Extensible sbt server Experimental. sbt server can now be extended via
the plugin.
140
{
case r: JsonRpcRequestMessage if r.method == "lunar/helo" =>
jsonRpcNotify("lunar/oleh", "")
()
},
PartialFunction.empty
)
This feature is still experimental and the API may change in the future.
#3975 by [@eed3si9n][@eed3si9n]
Thin client(s) Experimental. sbt 1.2.0 adds a new mode called -client.
When sbt is started with -client command, it no longer to loads the build, and
instead tries to connect to an instance of sbt server over JSON-RPC. When the
server is not running (portfile is not found), it will fork a new instance of sbt
entirely in a new JVM.
This lets you invoke sbt from the terminal shell or from an editor.
# server stays
$ ps | rg java
21860 ttys015 1:22.43 java -Xms2048M -Xmx2048M -Xss2M -jar /usr/local/Cellar/sbt/1.1.6/libexe
22014 ttys015 0:00.00 rg java
141
In addition, there are also an alternative thin clients cb372/sbt-client and dwij-
nand/sbtl implemented using Rust.
142
• Fixes link to the documentation of deprecated 0.10/0.12 DSL syntax.
#3901 by [@colindean]
• Displays only the eviction warning summary by default, and make it config-
urable using ThisBuild / evictionWarningOptions. lm211 and #3947
by [@exoego][@exoego]
• Allow varargs in inThisBuild(...), inConfig(C)(...), inTask(t)(...),
inScope(scope)(...). #4106 by [@dwijnand][@dwijnand]
• Adds fgRun and fgRunMain tasks that behaves like sbt 0.13’s run. #4216
by [@agaro1121][@agaro1121]
• Supports test.script and pending.script as the scripted file name.
#4220 by [@regadas][@regadas]
• Supports aliases in inspect command. #4221 by [@gpoirier][@gpoirier]
143
• Adds lastGrep, loadFailed, etc commands to replace the kebab-
cased commands. #4080 by [@naferx][@naferx], #4159 by [@Asam-
sig][@Asamsig], and #4169 by [@tiqwab][@tiqwab]
Internal
Contributors Thanks again to everyone who’s helped improve sbt and Zinc
1.
sbt 1.2.0 was brought to you by 60 contributors. Dale Wijnand, Eugene Yokota,
Kenji Yoshida (xuwei-k), Yasuhiro Tatsuno (exoego), Łukasz Wawrzyk, Jorge
Vicente Cantero (jvican), Alistair Johnson, Antonio Cunei, Jason Zaugg, Rik-
ito Taniguchi (tanishiking), Seiya Mizuno, Tim Harper, Aloisia Davì (alodavi),
Arnout Engelen, Ethan Atkins, Johannes Rudolph, Krzysztof Romanowski, Al-
lan Renucci, Brian P. Holt, Filipe Regadas, Hiroshi Ito, Martijn Hoekstra, Ol-
egYch, Seth Tisue, natans, Aaron S. Hawley, Alex Khvatov, Alexander Samsig,
Andreas Jim-Hartmann, Andrei Pozolotin, Andrey Kaygorodov, Anthony Garo,
Christopher Hunt, Colin Dean, Daniel Riquelme, Deokhwan Kim, Gerard Maas,
144
Guillaume Poirier, Heikki Vesalainen, Jason Pickens, Jonas Fonseca, Julien Jer-
phanion, Justin Pihony, Kazufumi Nishida, Kyle Goodale, Maksym Fedorov,
Mark Canlas, Martynas Mickevičius, Michael Pollmeier, Mike Skells, Nafer
Sanabria, Naohisa Murakami (tiqwab), PanAeon, Peter Neyens, Rui Gonçalves,
Sean Sullivan, Stephen Nancekivell, Veera Venky, blakkan, ortigali. Thank you!
sbt 1.1.6
Bug fixes
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
builds, porting plugins, and submitting and reviewing pull requests.
sbt 1.1.6 was brought to you by 15 contributors, according to git shortlog
-sn --no-merges v1.1.5...v1.1.6 on sbt, zinc, librarymanagement, util,
io, launcher-package, and website: Ethan Atkins, Eugene Yokota, Dale Wij-
nand, Aaron S. Hawley, OlegYch, Richard Summerhayes, Jannis (dadarakt),
Rikito Taniguchi (tanishiking), Øyvind Høisæther, Daniel Westheide, Harrison
Houghton, Holden Karau, Håkon Wold, Jason Zaugg, and tekay.
145
sbt 1.1.5
Bug fixes
• Fixes the latency between file modification events and triggered execution.
io#142 and sbt#4096 by [@eatkins][@eatkins]
• Fixes NPE that could arise from WatchEvent io#140 by [@oneill][@oneill]
• Fixes deleted files not triggering ~. sbt#4098 by [@eatkins][@eatkins]
• Fixes MacOSXWatchService to meet the WatchService API. io#142 by
[@eatkins][@eatkins]
• Avoids printing RejectedExectionExeption stack trace after cancellation.
sbt#4058 by [@retronym][@retronym]
• Fixes Java version checking on Windows. lp#227 / lp#228 by [@jes-
sicah][@jessicah] and [@spangaer][@spangaer]
• Fixes unexpected responses from sbt server. sbt#4093 by [@laughedelic][@laughedelic]
• Re-fix console and JLine bug. sbt#4123 by [@eed3si9n][@eed3si9n]
• Fixes grammar for contributors guide. sbt#4133 by [@som-snytt][@som-
snytt]
Improvements
146
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
builds, porting plugins, and submitting and reviewing pull requests.
sbt 1.1.5 was brought to you by 21 constributors, according to git shortlog
-sn --no-merges v1.1.4...v1.1.5 on sbt, zinc, librarymanagement, util, io,
launcher-packege, and website: Eugene Yokota, Ethan Atkins, Jason Zaugg, Liu
Fengyun, Antonio Cunei, Dale Wijnand, Roberto Bonvallet, Alexey Alekhin,
Daniel Parks, Heikki Vesalainen, Jean-Luc Deprez, Jessica Hamilton, Kenji
Yoshida (xuwei-k), Nikita Gazarov, OlegYch, Richard Summerhayes, Robert
Walker, Seth Tisue, Som Snytt, oneill, and �� (Yang Bo)
sbt 1.1.4
Bug fixes
Improvements
Triggered execution on macOS sbt has long had issues with triggered
execution on macOS. Ethan Atkins has contributed a fix for this problem by
merging MacOSXWatchService from his CloseWatch. Thanks, Ethan!
Credit also goes to Greg Methvin and Takari’s directory-watcher. #3860/#4071/io#138
by [@eatkins][@eatkins]
147
Running sbt with standby One of the tricky things you come across while
profiling is figuring out the process ID, while wanting to profile the beginning
of the application.
For this purpose, we’ve added sbt.launcher.standby JVM flag. Starting sbt
1.1.4, you can run:
This will count down for 20s before doing anything else. launcher#51 by
[@eed3si9n][@eed3si9n]
Contributors
A huge thank you to everyone who’s helped improve sbt and Zinc 1 by using
them, reporting bugs, improving our documentation, porting builds, porting
plugins, and submitting and reviewing pull requests.
sbt 1.1.4 was brought to you by 11 contributors, according to git shortlog
-sn --no-merges v1.1.2...v1.1.4 on sbt, zinc, librarymanagement, util, io,
launcher-packege, and website: Eugene Yokota, Dale Wijnand, �� (Yang Bo),
Ethan Atkins, Sam Halliday, Aaron S. Hawley, Gabriele Petronella, Jason
Steenstra-Pickens, Jason Zaugg, Julien Jean Paul Sirocchi, and aumann.
sbt 1.1.2
Bug fixes
148
• Fixes triggered execution’s resource leak by caching the watch service.
#3999 by [@eatkins][@eatkins]
• Fixes classloader inheriting the dependencies of Scala compiler during run
zinc#505 by [@eed3si9n][@eed3si9n]
• Fixes forked test concurrency issue. #4030 by [@eatkins][@eatkins]
• Fixes new command leaving behind target directory #4033 by
[@eed3si9n][@eed3si9n]
• Fixes handling on null Content-Type. lm214 by [@staale][@staale]
• Fixes null handling of managedChecksums in ivySettings file. lm#218
by [@IanGabes][@IanGabes]
• Adds sbt.boot.lock as a JVM property to opt-out of locking. #3927 by
[@dwijnand][@dwijnand]
• Provides SBT_GLOBAL_SERVER_DIR env var as a workaround to long socket
file path on UNIX. #3932 by [@dwijnand][@dwijnand]
• Fixes forked runs reporting noisy “Stream closed” exception. #3970 by
[@retronym][@retronym]
• Fixes test compilation not getting included in VS Code save trigger.
#4022 by [@tmiyamon][@tmiyamon]
• Fixes sbt server responding with string id when number id passed. #4025
by [@tiqwab][@tiqwab]
• Fixes getDecoder in Analysis format zinc#502 by [@jilen][@jilen]
• Fixes equal / hashCode inconsistencies around Array. zinc#513 by
[@eed3si9n][@eed3si9n]
• Whitelists java9-rt-ext-output in rt export process lp#211 by
[@eatkins][@eatkins]
• Fixes JDK version detection for Java 10 friendliness. lp#219 by
[@eed3si9n][@eed3si9n] and @2m
• Fixes quoting in Windows bat file. lp#220 by [@ForNeVeR][@ForNeVeR]
• Fixes -error not suppressing startup logs. #4036 by [@eed3si9n][@eed3si9n]
Improvements
Internal
149
• Updates contribution guide. #3960/#4019 by [@eed3si9n][@eed3si9n] and
[@itohiro73][@itohiro73]
• Deletes buildinfo.BuildInfo from sbt main that was intended for test-
ing. 3967 by [@dwijnand][@dwijnand] and [@xuwei-k][@xuwei-k]
• Various improvements around Zinc benchmark by [@retronym][@retronym]
sbt 1.1.1
Bug fixes
Improvements
150
autoStartServer setting sbt 1.1.1 adds a new global Boolean setting called
autoStartServer, which is set to true by default. When set to true, sbt shell
will automatically start sbt server. Otherwise, it will not start the server until
startSever command is issued. This could be used to opt out of server for
security reasons.
#3922 by [@swaldman][@swaldman]
sbt 1.1.0
Features
Fixes
151
• Provides workaround for File#lastModified() losing millisecond-
precision by using native code when possible. io#92/io#106 by
[@cunei][@cunei]
• Fixes IO.relativize not working with relative path. io#108 by [@dwij-
nand][@dwijnand]
• Fixes warning message when multiple instances are detected. #3828 by
[@eed3si9n][@eed3si9n]
• Fixes over-compilation bug with Java 9. zinc#450 by [@retronym][@retronym]
• Fixes handling of deeply nested Java classes. zinc#423 by [@ro-
manowski][@romanowski]
• Fixes JavaDoc not printing all errors. zinc#415 by [@raboof][@raboof]
• Preserves JAR order in ScalaInstance.otherJars. zinc#411 by [@dwi-
jnand][@dwijnand]
• Fixes used name when it contains NL. zinc#449 by [@jilen][@jilen]
• Fixes handling of ThisProject. #3609 by [@dwijnand][@dwijnand]
• Escapes imports from sbt files, so if user creates a backquoted definition
then task evalution will not fail. #3635 by [@panaeon][@panaeon]
• Removes reference to version 0.14.0 from a warning message. #3693 by
[@saniyatech][@saniyatech]
• Fixes screpl throwing “Not a valid key: console-quick”. #3762 by [@xuwei-
k][@xuwei-k]
• Restores Scala 2.13.0-M1 support. #461 by [@dwijnand][@dwijnand]
• Fixes the encoding of Unix-like file path to use file:///. #3805 by
[@eed3si9n][@eed3si9n]
• Fixes Log4J2 initialization error during startup. #3814 by [@dwij-
nand][@dwijnand]
Improvements
152
• Adds POSIX persmission operations in IO, such as IO.chmod(..). io#76
by [@eed3si9n][@eed3si9n]
• Treat sbt 1 modules using Semantic Versioning in the eviction warning.
lm#188 by [@eed3si9n][@eed3si9n]
• Uses kind-projector in the code. #3650 by [@dwijnand][@dwijnand]
• Make displayOnly etc methods strict in Completions. #3763 by
[@xuwei-k][@xuwei-k]
Unified slash syntax for sbt shell and build.sbt This adds unified
slash syntax for both sbt shell and the build.sbt DSL. Instead of the current
<project-id>/config:intask::key, this adds <project-id>/<config-ident>/intask/key
where <config-ident> is the Scala identifier notation for the configurations
like Compile and Test. (The old shell syntax will continue to function)
These examples work both from the shell and in build.sbt.
Global / cancelable
ThisBuild / scalaVersion
Test / test
root / Compile / compile / scalacOptions
ProjectRef(uri("file:/xxx/helloworld/"),"root")/Compile/scalacOptions
Zero / Zero / name
sbt server sbt server feature was reworked to use Language Server Protocol
3.0 (LSP) as the wire protocol, a protocol created by Microsoft for Visual Studio
Code.
153
To discover a running server, sbt 1.1.0 creates a port file at ./project/target/active.json
relative to a build:
{"uri":"local:///Users/foo/.sbt/1.0/server/0845deda85cb41abcdef/sock"}
local: indicates a UNIX domain socket. Here’s how we can say hello to the
server using nc. (ˆM can be sent Ctrl-V then Return):
$ nc -U /Users/foo/.sbt/1.0/server/0845deda85cb41abcdef/sock
Content-Length: 99^M
^M
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }
sbt server adds network access to sbt’s shell command so, in addition to ac-
cepting input from the terminal, server also to accepts input from the network.
Here’s how we can call compile:
Content-Length: 93^M
^M
{ "jsonrpc": "2.0", "id": 2, "method": "sbt/exec", "params": { "commandLine": "compile" } }^M
The running sbt session should now queue compile, and return back with com-
piler warnings and errors, if any:
Content-Length: 296
Content-Type: application/vscode-jsonrpc; charset=utf-8
{"jsonrpc":"2.0","method":"textDocument/publishDiagnostics","params":{"uri":"file:/Users/foo
#3524/#3556 by [@eed3si9n][@eed3si9n]
VS Code extension The primary use case we have in mind for the sbt server
is tooling integration such as editors and IDEs. As a proof of concept, we created
a Visual Studio Code extension called Scala (sbt).
Currently this extension is able to:
• Run compile at the root project when *.scala files are saved. #3524 by
[@eed3si9n][@eed3si9n]
• Display compiler errors.
• Display log messages. #3740 by [@laughedelic][@laughedelic]
• Jump to class definitions. #3660 by [@wpopielarski][@wpopielarski]
154
Filtering scripted tests using project/build.properties For all scripted
tests in which project/build.properties exist, the value of the sbt.version
property is read. If its binary version is different from sbtBinaryVersion in
pluginCrossBuild the test will be skipped and a message indicating this will
be logged.
This allows you to define scripted tests that track the minimum supported sbt
versions, e.g. 0.13.9 and 1.0.0-RC2. #3564/#3566 by [@jonas][@jonas]
sbt 1.0.4
Bug fixes
155
• Works around Scala compiler’s templateStats() not being thread-safe.
#3743 by [@cunei][@cunei]
• Fixes “Attempting to overwrite” error message. lm#174 by [@dwij-
nand][@dwijnand]
• Fixes incorrect eviction warning message. lm#179 by [@xuwei-k][@xuwei-
k]
• Registers Ivy protocol only for http: and https: to be more plugin
friendly. lm183 by [@tpunder][@tpunder]
• Fixes script issues related to bc by using expr. launcher-package#199 by
[@thatfulvioguy][@thatfulvioguy]
Enhancement
Internal
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
builds, porting plugins, and submitting and reviewing pull requests.
This release was brought to you by 17 contributors, according to git shortlog
-sn --no-merges v1.0.3..v1.0.4 on sbt, zinc, librarymanagement, util, io,
and website: Eugene Yokota, Kenji Yoshida (xuwei-k), Jorge Vicente Cantero
(jvican), Dale Wijnand, Leonard Ehrenfried, Antonio Cunei, Brett Randall,
Guillaume Martres, Arnout Engelen, Fulvio Valente, Jens Grassel, Matthias
Kurz, OlegYch, Philippus Baalman, Sam Halliday, Tim Underwood, Tom Most.
Thank you!
sbt 1.0.3
Bug fixes
156
• Fixes undercompilation on inheritance on same source. zinc#424 by
[@eed3si9n][@eed3si9n]
• Fixes the compilation of package-protected objects. zinc#431 by [@jvi-
can][@jvican]
• Workaround for Java returning null for getGenericParameterTypes.
zinc#446 by [@jvican][@jvican]
• Fixes test detection regression. sbt 1.0.3 filters out nested objects/classes
from the list, restoring compatibility with 0.13. #3669 by [@cunei][@cunei]
• Uses Scala 2.12.4 for the build definition. This includes fix for run-
time reflection of empty package members under Java 9. #3587 by
[@eed3si9n][@eed3si9n]
• Fixes extra / in Ivy style patterns. lm#170 by [@laughedelic][@laughedelic]
• Fixes “destination file exist” error message by including the file name.
lm171 by [@leonardehrenfried][@leonardehrenfried]
• Fixes JDK 9 warning “Illegal reflective access” in library management
module and Ivy. lm173 by [@dwijnand][@dwijnand]
Improvements
Alternative watch mode sbt 1.0.0 introduced a new mechanism for watch-
ing for source changes based on the NIO WatchService in Java 1.7. On some
platforms (namely macOS) this has led to long delays before changes are picked
up. An alternative WatchService for these platforms is planned for sbt 1.1.0
(#3527), in the meantime an option to select which watch service has been
added.
The new sbt.watch.mode JVM flag has been added with the following sup-
ported values:
• polling: (default for macOS) poll the filesystem for changes (mechanism
used in sbt 0.13).
• nio (default for other platforms): use the NIO based WatchService.
If you are experiencing long delays on a non-macOS machine then try adding
-Dsbt.watch.mode=polling to your sbt options.
#3597 by [@stringbean][@stringbean]
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
builds, porting plugins, and submitting and reviewing pull requests.
157
This release was brought to you by 15 contributors, according to git shortlog
-sn --no-merges v1.0.2..v1.0.3 on sbt, zinc, librarymanagement, util, io,
and website: Eugene Yokota, Dale Wijnand, Michael Stringer, Jorge Vicente
Cantero (jvican), Alexey Alekhin, Antonio Cunei, Andrey Artemov, Jeffrey Ol-
chovy, Kenji Yoshida (xuwei-k), Dominik Winter, Long Jinwei, Arnout Engelen,
Justin Kaeser, Leonard Ehrenfried, Sakib Hadžiavdić. Thank you!
sbt 1.0.2
Bug fixes
Internal
158
Contributors A huge thank you to everyone who’s helped improve sbt and
Zinc 1 by using them, reporting bugs, improving our documentation, porting
plugins, and submitting and reviewing pull requests.
This release was brought to you by 19 contributors, according to git shortlog
-sn --no-merges v1.0.1..v1.0.2 on sbt, zinc, librarymanagement, and web-
site: Dale Wijnand, Eugene Yokota, Kenji Yoshida (xuwei-k), Antonio Cunei,
David Pratt, Karol Cz (kczulko), Amanj Sherwany, Emanuele Blanco, Eric Pe-
ters, Guillaume Bort, James Roper, Joost de Vries, Marko Elezovic, Martynas
Mickevičius, Michael Stringer, Răzvan Flavius Panda, Peter Vlugter, Philippus
Baalman, and Wiesław Popielarski. Thank you!
sbt 1.0.1
Bug fixes
WatchSource The watch source feature went through a major change from
sbt 0.13 to sbt 1.0 using NIO; however, it did not have clear migration path, so
we are rectifying that in sbt 1.0.1.
First, sbt.WatchSource is a new alias for sbt.internal.io.Source. Hopefully
this is easy enough to remember because the key is named watchSources. Next,
def apply(base: File) and def apply(base: File, includeFilter:
FileFilter, excludeFilter: FileFilter) constructors were added to the
companion object of sbt.WatchSource.
159
For backward compatiblity, sbt 1.0.1 adds += support (Append instance) from
File to Seq[WatchSource].
So, if you have a directory you want to watch:
watchSources += WatchSource(sourceDirectory.value)
sbt 1.0.0
• sbt 1.0 uses Scala 2.12 for build definitions and plugins. This also requires
JDK 8.
• Many of the case classes are replaced with pseudo case classes generated
using Contraband. Migrate .copy(foo = xxx) to withFoo(xxx).
For example, UpdateConfiguration, RetrieveConfiguration,
PublishConfiguration are refactored to use builder pattern.
• Zinc 1 drops support for Scala 2.9 and earlier. Scala 2.10 must use 2.10.2
and above. Scala 2.11 must use 2.11.2 and above. (latest patch releases
are recommended)
• config("xyz") must be directly assigned to a capitalized val, like val Xyz
= config("xyz"). This captures the lhs identifier into the configuration
so we can use it from the shell later.
• Changes publishTo and otherResolvers from SettingKeys to TaskKeys.
#2059/#2662 by [@dwijnand][@dwijnand]
• Path.relativizeFile(baseFile, file) is renamed to IO.relativizeFile(baseFile,
file).
• PathFinder’s .*** method is renamed to .allPaths method.
• PathFinder.x_!(mapper) is moved to def pair on PathFinder.
• A number of the methods on sbt.Path (such as relativeTo and rebase
and flat) are now no longer in the default namespace by virtue of being
mixed into the sbt package object. Use sbt.io.Path to access them again.
• sbt 1.0 renames Global as scope component to Zero to disambiguate from
GlobalScope. [@eed3si9n][@eed3si9n]
160
• sbt 1.0 uses ConfigRef in places where String was used to reference
configuration, such as update.value.configuration(...). Pass in
Configuration, which implicitly converts to ConfigRef.
• Changes sourceArtifactTypes and docArtifactTypes from Set[String]
to Seq[String] settings.
• Renames early command feature from --<command> to early(<command>).
• Drops sbt 0.12 style hyphen-separated key names (use publishLocal in-
stead of publish-local).
• Log options -error, -warn, -info, -debug are added as shorthand for
"early(error)" etc.
• sbt.Process and sbt.ProcessExtra are dropped. Use scala.sys.process
instead.
• incOptions.value.withNameHashing(...) option is removed because
name hashing is always on.
• TestResult.Value is now called TestResult.
• The scripted plugin is cross-versioned now, so you must use %% when de-
pending on it.
Dropped dreprecations:
• sbt 0.12 style Build trait that was deprecated in sbt 0.13.12, is removed.
Please migrate to build.sbt. Auto plugins and Build trait do not work
well together, and its feature is now largely subsumed by multi-project
build.sbt.
• sbt 0.12 style Project(...) constructor is restricted down to two param-
eters. This is because settings parameter does not work well with Auto
Plugins. Use project instead.
• sbt 0.12 style key dependency operators <<=, <+=, <++= are removed.
Please migrate to :=, +=, and ++=. These operators have been sources
of confusion for many users, and have long been removed from 0.13 docs,
and have been formally deprecated since sbt 0.13.13.
• Non-auto sbt.Plugin trait is dropped. Please migrate to AutoPlugin.
Auto plugins are easier to configure, and work better with each other.
• Removes the settingsSets method from Project (along with
add/setSbtFiles).
• Drops deprecated InputTask apply method and inputTask DSL method.
Use Def.inputTask and Def.spaceDelimited().parsed.
• Drops deprecated ProjectReference implicit lifts. Use RootProject(<uri>),
RootProject(<file>) or LocalProject(<string>).
• Drops deprecated seq(..) DSL method. Use Seq or pass in the settings
without wrapping.
• Drops deprecated File/Seq[File] setting enrichments. Use .value and
Def.setting.
161
• Drops deprecated SubProcess apply overload. Use SubProcess(ForkOptions(runJVMOptions
= ..)).
• Drops toError(opt: Option[String]): Unit (equivalent to opt
foreach sys.error); if used to wrap ScalaRun#run then the replacement
is scalaRun.run(...).failed foreach (sys error _.getMessage)
Features
Fixes
Improvements
162
• ivyLoggingLevel is dropped to UpdateLogging.Quiet when CI environ-
ment is detected. [@eed3si9n][@eed3si9n]
• Add logging of the name of the different build.sbt (matching *.sbt) files
used. #1911 by [@valydia][@valydia]
• Add the ability to call aggregate for the current project inside a build
sbt file. By [@xuwei-k][@xuwei-k]
• Add new global setting asciiGraphWidth that controls the maximum
width of the ASCII graphs printed by commands like inspect tree. De-
fault value corresponds to the previously hardcoded value of 40 characters.
By [@RomanIakovlev][@RomanIakovlev].
• Revamped documentation for Scopes, and added Scope Delegation.
[@eed3si9n][@eed3si9n]
• Ports sbt-cross-building’s ˆ and ˆˆ commands for plugin cross building.
See below.
• Adds support for cross-versioned exclusions. #1518/lm#88 by [@jvi-
can][@jvican]
• Adds new offline mode to the Ivy-based library management. lm#92 by
[@jvican][@jvican]
• A number of features related to dependency locking. See below.
• Improved eviction warning presentation. See below.
• A better main class detection. zinc#287 by [@smarter][@smarter]
• For faster startup, sbt will use Java refection to discover autoImport .
#3115 by [@jvican][@jvican]
• For faster startup, reuse the same global instance for parsing. #3115 by
[@jvican][@jvican]
• Adds InteractionService from sbt-core-next to keep compatibility with
sbt 0.13. #3182 by [@eed3si9n][@eed3si9n]
• Adds new WatchService that abstracts PollingWatchService and Java
NIO. io#47 by [@Duhemm][@Duhemm] on behalf of The Scala Center.
• Adds variants of IO.copyFile and IO.copyDirectory that accept
sbt.io.CopyOptions(). See below for details.
• Path.directory and Path.contentOf are donated from sbt-native-
packager io#38 by [@muuki88][@muuki88]
• ApiDiff feature used to debug Zinc uses Scala implementation borrowed
from Dotty. zinc#346 by [@Krever][@Krever]
• In Zinc internal, make ExtractAPI use perRunCaches. zinc#347 by
[@gheine][@gheine]
Internals
163
to run sbt tests, which reduces the CI build times by 50% #3151 by
[@jvican][@jvican]
• sbt 1.0.0-M6 is built using sbt 1.0.0-M5. #3184 by [@dwijnand][@dwijnand]
This depends on some factors such as how your classes are organized, but you can
see 3x ~ 40x improvements. The reason for the speedup is because it compiles
fewer source files than before by untangling the classes from source files. In
the example adding a method to scala/scala’s Platform class, sbt 0.13’s name
hashing used to compile 72 sources, but the new Zinc compiles 6 sources.
sbt server: JSON API for tooling integration sbt 1.0 includes server
feature, which allows IDEs and other tools to query the build for settings, and
invoke commands via a JSON API. Similar to the way that the interactive shell
164
in sbt 0.13 is implemented with shell command, “server” is also just shell
command that listens to both human input and network input. As a user, there
should be minimal impact because of the server.
In March 2016, we rebooted the “server” feature to make it as small as possible.
We worked in collaboration with JetBrains’ @jastice who works on IntelliJ’s sbt
interface to narrow down the feature list. sbt 1.0 will not have all the things
we originally wanted, but in the long term, we hope to see better integration
between IDE and sbt ecosystem using this system. For example, IDEs will be
able to issue the compile task and retrieve compiler warning as JSON events:
Another related feature that was added is the bgRun task which, for example,
enables a server process to be run in the background while you run tests against
it.
Static validation of build.sbt sbt 1.0 prohibits .value calls inside the bod-
ies of if expressions and anonymous functions in a task, @sbtUnchecked anno-
tation can be used to override the check.
The static validation also catches if you forget to call .value in a body of a
task.
#3216 and #3225 by [@jvican][@jvican]
Eviction warning presentation sbt 1.0 improves the eviction warning pre-
setation.
Before:
After:
[warn] Found version conflict(s) in library dependencies; some are suspected to be binary incomp
[warn]
[warn] * com.typesafe.akka:akka-actor_2.12:2.5.0 is selected over 2.4.17
[warn] +- de.heikoseeberger:akka-log4j_2.12:1.4.0 (depends on 2.5.0)
[warn] +- com.typesafe.akka:akka-parsing_2.12:10.0.6 (depends on 2.4.17)
[warn] +- com.typesafe.akka:akka-stream_2.12:2.4.17 () (depends on 2.4.17)
[warn]
[warn] Run 'evicted' to see detailed eviction warnings
#3202 by [@eed3si9n][@eed3si9n]
165
sbt-cross-building [@jrudolph][@jrudolph]’s sbt-cross-building is a plugin
author’s plugin. It adds cross command ˆ and sbtVersion switch command
ˆˆ, similar to + and ++, but for switching between multiple sbt versions across
major versions. sbt 0.13.16 merges these commands into sbt because the feature
it provides is useful as we migrate plugins to sbt 1.0.
To switch the sbtVersion in pluginCrossBuild from the shell use:
^^ 1.0.0-M5
Your plugin will now build with sbt 1.0.0-M5 (and its Scala version 2.12.2).
If you need to make changes specific to a sbt version, you can now include them
into src/main/scala-sbt-0.13, and src/main/scala-sbt-1.0.0-M5, where
the binary sbt version number is used as postfix.
To run a command across multiple sbt versions, set:
Then, run:
^ compile
IO.copyDirectory(source, target)
io#53 by [@dwijnand][@dwijnand]
166
Library management API and parallel artifact download sbt 1.0 adds
Library management API co-authored by Eugene Yokota ([@eed3si9n][@eed3si9n])
from Lightbend and Martin Duhem ([@Duhemm][@Duhemm]) from Scala Cen-
ter. This API aims to abstract Apache Ivy as well as alternative dependency
resolution engines Ivy, cached resolution, and Coursier.
Parallel artifact download for Ivy engine was contributed by Jorge ([@jvi-
can][@jvican]) from Scala Center. It also introduces Gigahorse OkHttp as the
Network API, and it uses Square OkHttp for artifact download as well.
lm#124 by [@eed3si9n][@eed3si9n]/[@Duhemm][@Duhemm], lm#90 by [@jvi-
can][@jvican]/[@jsuereth][@jsuereth] and lm#104 by [@eed3si9n][@eed3si9n].
zinc#351 by [@jvican][@jvican]
• Frozen mode to the Ivy-based library management, which makes sure that
the resolution is always intransitive. lm#100
• Adds support to specify a resolver for dependencies. lm#97
• Adds “managed checksums”, which tells Ivy to skip the checksum process.
lm#111
Detailed Topics
This part of the documentation has pages documenting particular sbt topics in
detail. Before reading anything in here, you will need the information in the
Getting Started Guide as a foundation.
Other resources include the How to and Developer’s Guide sections in this ref-
erence, and the API Documentation
167
Using sbt
This part of the documentation has pages documenting particular sbt topics in
detail. Before reading anything in here, you will need the information in the
Getting Started Guide as a foundation.
Project-level tasks
Configuration-level tasks
168
code (the compile configuration). test:compile compiles the test source code
(test test configuration). Most tasks for the compile configuration have an
equivalent in the test configuration that can be run using a test: prefix.
169
• testOnly <test>* Runs the tests provided as arguments. * (will be)
interpreted as a wildcard in the test name. See Testing for details.
• testQuick <test>* Runs the tests specified as arguments (or all tests if
no arguments are given) that:
1. have not been run yet OR
2. failed the last time they were run OR
3. had any transitive dependencies recompiled since the last successful
run * (will be) interpreted as a wildcard in the test name. See Testing
for details.
General commands
• + <command> Executes the project specified action or method for all ver-
sions of Scala defined in the crossScalaVersions setting.
170
• ; A ; B Execute A and if it succeeds, run B. Note that the leading
semicolon is required.
171
Boolean
false
If true, disable ANSI color codes. Useful on build servers or terminals that do
not support color.
sbt.global.base
Directory
~/.sbt/1.0
The directory containing global settings and plugins
sbt.ivy.home
Directory
~/.ivy2
The directory containing the local Ivy repository and artifact cache
sbt.boot.directory
Directory
~/.sbt/boot
Path to shared boot directory
sbt.main.class
String
xsbt.inc.debug
Boolean
false
sbt.extraClasspath
Classpath Entries
(jar files or directories) that are added to sbt’s classpath. Note that the entries
are deliminted by comma, e.g.: entry1, entry2,... See also resource in the sbt
launcher documentation.
sbt.version
Version
1.2.7
sbt version to use, usually taken from project/build.properties.
sbt.boot.properties
File
172
The path to find the sbt boot properties file. This can be a relative path, relative
to the sbt base directory, the users home directory or the location of the sbt jar
file, or it can be an absolute path or an absolute file URI.
sbt.override.build.repos
Boolean
false
If true, repositories configured in a build definition are ignored and the reposito-
ries configured for the launcher are used instead. See sbt.repository.config and
the sbt launcher documentation.
sbt.repository.config
File
~/.sbt/repositories
A file containing the repositories to use for the launcher. The format is the same
as a [repositories] section for a sbt launcher configuration file. This setting is
typically used in conjunction with setting sbt.override.build.repos to true (see
previous row and the sbt launcher documentation).
Console Project
Description
The consoleProject task starts the Scala interpreter with access to your project
definition and to sbt. Specifically, the interpreter is started up with these
commands already executed:
import sbt._
import Keys._
import <your-project-definition>._
import currentState._
import extracted._
import cpHelpers._
For example, running external processes with sbt’s process library (to be in-
cluded in the standard library in Scala 2.9):
173
consoleProject can be useful for creating and modifying your build in the
same way that the Scala interpreter is normally used to explore writing code.
Note that this gives you raw access to your build. Think about what you pass
to IO.delete, for example.
Accessing settings
Examples
Evaluating tasks
State
174
Examples Show the remaining commands to be executed in the build (more
interesting if you invoke consoleProject like ; consoleProject ; clean ;
compile):
> remainingCommands
> definedCommands.size
Cross-building
Introduction
Publishing Conventions
To use a library built against multiple versions of Scala, double the first % in an
inline dependency to be %%. This tells sbt that it should append the current
version of Scala being used to build the library to the dependency’s name. For
example:
175
Cross-Building a Project
> + package
> ++ 2.12.2
[info] Setting version to 2.12.2
> ++ 2.11.11
[info] Setting version to 2.11.11
> compile
> + publish
you make your project available to users for different versions of Scala. See
Publishing for more details on publishing your project.
In order to make this process as quick as possible, different output and managed
dependency directories are used for different versions of Scala. For example,
when building against Scala 2.12.7,
176
Packaged jars, wars, and other artifacts have _<scala-version> appended to
the normal artifact ID as mentioned in the Publishing Conventions section
above.
This means that the outputs of each build against each version of Scala are
independent of the others. sbt will resolve your dependencies for each version
separately. This way, for example, you get the version of Dispatch compiled
against 2.11 for your 2.11.x build, the version compiled against 2.12 for your
2.12.x builds, and so on. You can have fine-grained control over the behavior
for different Scala versions by using the cross method on ModuleID These are
equivalent:
This overrides the defaults to always use the full Scala version instead of the
binary Scala version:
This uses a custom function to determine the Scala version to use based on the
binary Scala version:
This uses a custom function to determine the Scala version to use based on the
full Scala version:
177
"a" % "b" % "1.0" cross CrossVersion.fullMapped {
case "2.9.1" => "2.9.0"
case x => x
}
{<build-uri>}<project-id>/config:intask::key
This “scoped key” reference is used by commands like last and inspect and
when selecting a task to run. Only key is usually required by the parser; the
remaining optional pieces select the scope. These optional pieces are individ-
ually referred to as scope axes. In the above description, {<build-uri>} and
<project-id>/ specify the project axis, config: is the configuration axis, and
intask is the task-specific axis. Unspecified components are taken to be the
current project (project axis) or auto-detected (configuration and task axes).
An asterisk (*) is used to explicitly refer to the Global context, as in */*:key.
> compile
> compile:compile
> root/compile
> root/compile:compile
> {file:/home/user/sample/}root/compile:compile
178
As another example, run by itself refers to compile:run because there is no
global run task and the first configuration searched, compile, defines a run.
Therefore, to reference the run task for the Test configuration, the configuration
axis must be specified like test:run. Some other examples that require the
explicit test: axis:
> test:consoleQuick
> test:console
> test:doc
> test:package
Task-specific Settings Some settings are defined per-task. This is used when
there are several related tasks, such as package, packageSrc, and packageDoc,
in the same configuration (such as compile or test). For package tasks, their
settings are the files to package, the options to use, and the output file to
produce. Each package task should be able to have different values for these
settings.
This is done with the task axis, which selects the task to apply a setting to. For
example, the following prints the output jar for the different package tasks.
> package::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1.jar
> packageSrc::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1-src.jar
> packageDoc::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/demo_2.8.1-0.1-doc.jar
> test:package::artifactPath
[info] /home/user/sample/target/scala-2.8.1.final/root_2.8.1-0.1-test.jar
Note that a single colon : follows a configuration axis and a double colon ::
follows a task axis.
This section discusses the inspect command, which is useful for exploring re-
lationships between settings. It can be used to determine which setting should
be modified in order to affect another setting, for example.
179
Value and Provided By The first piece of information provided by inspect
is the type of a task or the value and type of a setting. The following section of
output is labeled “Provided by”. This shows the actual scope where the setting
is defined. For example,
This shows that libraryDependencies has been defined on the current project
({file:/home/user/sample/}root) in the global configuration (*:). For a task
like update, the output looks like:
Related Settings The “Related” section of inspect output lists all of the
definitions of a key. For example,
Dependencies Forward dependencies show the other settings (or tasks) used
to define a setting (or task). Reverse dependencies go the other direction, show-
ing what uses a given setting. inspect provides this information based on either
the requested dependencies or the actual dependencies. Requested dependen-
cies are those that a setting directly specifies. Actual settings are what those
dependencies get resolved to. This distinction is explained in more detail in the
following sections.
180
Requested Dependencies As an example, we’ll look at console:
...
This shows the inputs to the console task. We can see that it gets its classpath
and options from fullClasspath and scalacOptions(for console). The in-
formation provided by the inspect command can thus assist in finding the right
setting to change. The convention for keys, like console and fullClasspath,
is that the Scala identifier is camel case, while the String representation is
lowercase and separated by dashes. The Scala identifier for a configuration is
uppercase to distinguish it from tasks like compile and test. For example, we
can infer from the previous example how to add code to be run when the Scala
interpreter starts up:
181
> inspect actual console
...
[info] Dependencies:
[info] compile:scalacOptions
[info] compile:fullClasspath
[info] *:scalaInstance
[info] */*:initialCommands
[info] */*:cleanupCommands
[info] */*:taskTemporaryDirectory
[info] *:console::compilers
[info] compile:console::streams
...
For initialCommands, we see that it comes from the global scope (*/*:). Com-
bining this with the relevant output from inspect console:
compile:console::initialCommands
The reason we might want to set it here this is that other console tasks will use
this value now. We can see which ones use our new setting by looking at the
reverse dependencies output of inspect actual:
182
> set initialCommands in console := "import mypackage._"
> set initialCommands in consoleQuick := "import mypackage._"`
or configuration axis:
The next part describes the Delegates section, which shows the chain of delega-
tion for scopes.
Delegates A setting has a key and a scope. A request for a key in a scope A
may be delegated to another scope if A doesn’t define a value for the key. The
delegation chain is well-defined and is displayed in the Delegates section of the
inspect command. The Delegates section shows the order in which scopes are
searched when a value is not defined for the requested key.
As an example, consider the initial commands for console again:
Triggered Execution
You can make a command run when certain files change by prefixing the com-
mand with ~. Monitoring is terminated when enter is pressed. This triggered
execution is configured by the watch setting, but typically the basic settings
watchSources and pollInterval are modified.
• watchSources defines the files for a single project that are monitored
for changes. By default, a project watches resources and Scala and Java
sources.
183
• watchTransitiveSources then combines the watchSources for the cur-
rent project and all execution and classpath dependencies (see .scala build
definition for details on interProject dependencies).
• pollInterval selects the interval between polling for changes in millisec-
onds. The default value is 500 ms.
Compile
> ~ test:compile
> ~ compile
Testing
You can use the triggered execution feature to run any command or task. One
use is for test driven development, as suggested by Erick on the mailing list.
The following will poll for changes to your source code (main or test) and run
testOnly for the specified test.
Occasionally, you may need to trigger the execution of multiple commands. You
can use semicolons to separate the commands to be triggered.
The following will poll for source changes and run clean and test.
sbt has two alternative entry points that may be used to:
184
These entry points should be considered experimental. A notable disadvantage
of these approaches is the startup time involved.
Setup
To set up these entry points, you can either use conscript or manually construct
the startup scripts. In addition, there is a setup script for the script mode that
only requires a JRE installed.
Manual Setup Duplicate your standard sbt script, which was set up accord-
ing to Setup, as scalas and screpl (or whatever names you like).
scalas is the script runner and should use sbt.ScriptMain as the main class,
by adding the -Dsbt.main.class=sbt.ScriptMain parameter to the java com-
mand. Its command line should look like:
For the REPL runner screpl, use sbt.ConsoleMain as the main class:
Usage
sbt Script runner The script runner can run a standard Scala script, but
with the additional ability to configure sbt. sbt settings may be embedded in
the script in a comment block that opens with /***.
185
Example Copy the following script and make it executable. You may need to
adjust the first line depending on your script name and operating system. When
run, the example should retrieve Scala, the required dependencies, compile the
script, and run it directly. For example, if you name it shout.scala, you would
do on Unix:
#!/usr/bin/env scalas
/***
scalaVersion := "2.12.7"
import sbt.io.IO
import sbt.io.Path._
import sbt.io.syntax._
import java.io.File
import java.net.{URI, URL}
import sys.process._
def file(s: String): File = new File(s)
def uri(s: String): URI = new URI(s)
This script will take all *.scala files under src/, append “!” at the end of the
line, and write them under target/.
186
sbt REPL with dependencies The arguments to the REPL mode configure
the dependencies to use when starting up the REPL. An argument may be either
a jar to include on the classpath, a dependency definition to retrieve and put
on the classpath, or a resolver to use when retrieving dependencies.
A dependency definition looks like:
organization%module%revision
organization%%module%revision
"id at url"
Example: To add the Sonatype snapshots repository and add Scalaz 7.0-
SNAPSHOT to REPL classpath:
This syntax was a quick hack. Feel free to improve it. The relevant class is
IvyConsole.
sbt Server
sbt server is a feature that is newly introduced in sbt 1.x, and it’s still a work
in progress. You might at first imagine server to be something that runs on
remote servers, and does great things, but for now sbt server is not that.
Actually, sbt server just adds network access to sbt’s shell command so, in
addition to accepting input from the terminal, server also to accepts input from
the network. This allows multiple clients to connect to a single session of sbt.
The primary use case we have in mind for the client is tooling integration such
as editors and IDEs. As a proof of concept, we created a Visual Studio Code
extension called Scala (sbt).
187
Language Server Protocol 3.0
The wire protocol we use is Language Server Protocol 3.0 (LSP), which in turn
is based on JSON-RPC.
The base protocol consists of a header and a content part (comparable to HTTP).
The header and content part are separated by a \r\n.
Currently the following header fields are supported:
Here is an example:
188
Content-Type: application/vscode-jsonrpc; charset=utf-8\r\n
Content-Length: ...\r\n
\r\n
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32602,
"message": "some error message"
}
}
In addition to the responses, the server might also send events (“notifications”
in LSP terminology).
Server modes
Sbt server can run in two modes, which differ in wire protocol and initialization.
The default mode since sbt 1.1.x is domain socket mode, which uses either
Unix domain sockets (on Unix) or named pipes (on Windows) for data transfer
between server and client. In addition, there is a TCP mode, which uses TCP
for data transfer.
The mode which sbt server starts in is goverened by the key serverConnectionType,
which can be set to ConnectionType.Local for domain socket/named pipe
mode, or to ConnectionType.Tcp for TCP mode.
189
on whether the server is running in TCP mode or domain socket/named pipe
mode. They will look something like this:
In domain socket/named pipe mode, on Unix:
{"uri":"local:///Users/someone/.sbt/1.0/server/0845deda85cb41abdb9f/sock"}
where the uri key will contain a string starting with local:// followed by the
socket address sbt server is listening on.
In domain socket/named pipe mode, on Windows, it will look something like
{"uri":"local:sbt-server-0845deda85cb41abdb9f"}
where the uri key will contain a string starting with local: followed by the
name of the named pipe. In this example, the path of the named pipe will be
\.\pipe\sbt-server-0845deda85cb41abdb9f.
In TCP mode it will look something like the following:
{
"uri":"tcp://127.0.0.1:5010",
"tokenfilePath":"/Users/xxx/.sbt/1.0/server/0845deda85cb41abdb9f/token.json",
"tokenfileUri":"file:/Users/xxx/.sbt/1.0/server/0845deda85cb41abdb9f/token.json"
}
In this case, the uri key will hold a TCP uri with the address the server
is listening on. In this mode, the port file will contain two additional keys,
tokenfilePath and tokenfileUri. These point to the location of a token file.
The location of the token file will not change between runs. It’s contents will
look something like this:
{
"uri":"tcp://127.0.0.1:5010",
"token":"12345678901234567890123456789012345678"
}
The uri field is the same, and the token field contains a 128-bits non-negative
integer.
190
Initialize request
To initiate communication with sbt server, the client (such as a tool like VS
Code) must first send an initialize request. This means that the client
must send a request with method set to “initialize” and the InitializeParams
datatype as the params field.
If the server is running in TCP mode, to authenticate yourself, you must pass
in the token in initializationOptions as follows:
type InitializationOptionsParams {
token: String!
}
If the server is running in named pipe mode, no token is needed, and the
initializationOptions should be the empty object {}.
On Unix, using netcat, sending the initialize message in domain socket/named
pipe mode will look something like this:
$ nc -U /Users/foo/.sbt/1.0/server/0845deda85cb41abcdef/sock
Content-Length: 99^M
^M
{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "initializationOptions": { } } }
Connections to the server when it’s running in named pipe mode are exclusive
to the first process that connects to the socket or pipe.
After sbt receives the request, it will send an initialized event.
textDocument/publishDiagnostics event
The compiler warnings and errors are sent to the client using the
textDocument/publishDiagnostics event.
• method: textDocument/publishDiagnostics
• params: PublishDiagnosticsParams
191
Here’s an example output (with JSON-RPC headers omitted):
{
"jsonrpc": "2.0",
"method": "textDocument/publishDiagnostics",
"params": {
"uri": "file:/Users/xxx/work/hellotest/Hello.scala",
"diagnostics": [
{
"range": {
"start": {
"line": 2,
"character": 0
},
"end": {
"line": 2,
"character": 1
}
},
"severity": 1,
"source": "sbt",
"message": "')' expected but '}' found."
}
]
}
}
textDocument/didSave event
As of sbt 1.1.0, sbt will execute the compile task upon receiving a
textDocument/didSave notification. This behavior is subject to change.
sbt/exec request
• method: sbt/exec
• params:
type SbtExecParams {
commandLine: String!
}
192
Content-Length: 91
Note that there might be other commands running on the build, so in that case
the request will be queued up.
sbt/setting request
• method: sbt/setting
• params:
type SettingQuery {
setting: String!
}
Content-Length: 102
{"jsonrpc":"2.0","id":"3","result":{"value":"2.12.2","contentType":"java.lang.String"}}
sbt/completion request
(sbt 1.3.0+)
A sbt/completion request is used to emulate tab completions for sbt shell.
• method: sbt/completion
• params:
type CompletionParams {
query: String!
}
193
On telnet it would look as follows:
Content-Length: 100
{ "jsonrpc": "2.0", "id": 15, "method": "sbt/completion", "params": { "query": "testOnly org." }
Content-Length: 79
Content-Type: application/vscode-jsonrpc; charset=utf-8
{"jsonrpc":"2.0","id":15,"result":{"items":["testOnly org.sbt.ExampleSpec"]}}
This will respond immediatly based on the last available state of sbt.
sbt/cancelRequest
(sbt 1.3.0+)
A sbt/cancelRequest request can be used to terminate the execution of an
on-going task.
• method: sbt/cancelRequest
• params:
type CancelRequestParams {
id: String!
}
Content-Length: 93
{"jsonrpc":"2.0","id":"bar","result":{"status":"Task cancelled","channelName":"network-1","ex
194
Understanding Incremental Recompilation
Compiling Scala code with scalac is slow, but sbt often makes it faster. By
understanding how, you can even understand how to make compilation even
faster. Modifying source files with many dependencies might require recompiling
only those source files (which might take 5 seconds for instance) instead of all
the dependencies (which might take 2 minutes for instance). Often you can
control which will be your case and make development faster with a few coding
practices.
Improving the Scala compilation performance is a major goal of sbt, and thus
the speedups it gives are one of the major motivations to use it. A significant
portion of sbt’s sources and development efforts deal with strategies for speeding
up compilation.
To reduce compile times, sbt uses two strategies:
Reduce the overhead for restarting Scalac
Implement smart and transparent strategies for incremental recompilation, so
that only modified files and the needed dependencies are recompiled.
sbt always runs Scalac in the same virtual machine. If one compiles source code
using sbt, keeps sbt alive, modifies source code and triggers a new compilation,
this compilation will be faster because (part of) Scalac will have already been
JIT-compiled.
Reduce the number of recompiled source.
When a source file A.scala is modified, sbt goes to great effort to recompile other
source files depending on A.scala only if required - that is, only if the interface of
A.scala was modified. With other build management tools (especially for Java,
like ant), when a developer changes a source file in a non-binary-compatible way,
she needs to manually ensure that dependencies are also recompiled - often by
manually running the clean command to remove existing compilation output;
otherwise compilation might succeed even when dependent class files might need
to be recompiled. What is worse, the change to one source might make depen-
dencies incorrect, but this is not discovered automatically: One might get a
compilation success with incorrect source code. Since Scala compile times are
so high, running clean is particularly undesirable.
By organizing your source code appropriately, you can minimize the amount of
code affected by a change. sbt cannot determine precisely which dependencies
have to be recompiled; the goal is to compute a conservative approximation, so
that whenever a file must be recompiled, it will, even though we might recompile
extra files.
195
sbt heuristics
sbt tracks source dependencies at the granularity of source files. For each source
file, sbt tracks files which depend on it directly; if the interface of classes,
objects or traits in a file changes, all files dependent on that source must be
recompiled. At the moment sbt uses the following algorithm to calculate source
files dependent on a given source file:
The heuristics used by sbt imply the following user-visible consequences, which
determine whether a change to a class affects other classes.
196
returned, can reduce the code to be recompiled when the implementation
of such a method changes. (Explicitly annotating return types of a public
API is a good practice in general.)
All the above discussion about methods also applies to fields and members in
general; similarly, references to classes also extend to objects and traits.
Overview
• recompile as little source files as possible cover all changes to type checking
and produced
• byte code triggered by changed source files and/or classpath
The first goal is about making recompilation fast and it’s a sole point of in-
cremental compiler existence. The second goal is about correctness and sets a
lower limit on the size of a set of recompiled files. Determining that set is the
core problem incremental compiler tries to solve. We’ll dive a little bit into this
problem in the overview to understand what makes implementing incremental
compiler a challenging task.
Let’s consider this very simple example:
// A.scala
package a
class A {
def foo(): Int = 12
}
// B.scala
package b
class B {
def bar(x: a.A): Int = x.foo()
}
197
Let’s assume both of those files are already compiled and user changes A.scala
so it looks like this:
// A.scala
package a
class A {
def foo(): Int = 23 // changed constant
}
// A.scala
package a
class A {
def foo(): String = "abc" // changed constant and return type
}
• index source files so it knows whether there were API changes that might
affect other source files; e.g. it needs to detect changes to method signa-
tures as in the example above
• track dependencies between source files; once the change to an API is
detected the algorithm needs to determine the set of files that might be
potentially affected by this change
Both of those pieces of information are extracted from the Scala compiler.
198
Interaction with the Scala compiler
API extraction phase The API extraction phase extracts information from
Trees, Types and Symbols and maps it to incremental compiler’s internal data
structures described in the api.specification file.Those data structures allow to
express an API in a way that is independent from Scala compiler version. Also,
such representation is persistent so it is serialized on disk and reused between
compiler runs or even sbt runs.
The API extraction phase consist of two major components:
Mapping Types and Symbols The logic responsible for mapping Types
and Symbols is implemented in API.scala. With introduction of Scala reflection
we have multiple variants of Types and Symbols. The incremental compiler uses
the variant defined in scala.reflect.internal package.
Also, there’s one design choice that might not be obvious. When type corre-
sponding to a class or a trait is mapped then all inherited members are copied
instead of declarations in that class/trait. The reason for doing so is that it
greatly simplifies analysis of API representation because all relevant informa-
tion to a class is stored in one place so there’s no need for looking up parent
type representation. This simplicity comes at a price: the same information
199
is copied over and over again resulting in a performance hit. For example, ev-
ery class will have members of java.lang.Object duplicated along with full
information about their signatures.
200
// A.scala
class A {
def inc(x: Int): Int = x+1
}
// B.scala
class B {
def foo(a: A, x: Int): Int = a.inc(x)
}
Let’s assume both of those files are compiled and user changes A.scala so it
looks like this:
// A.scala
class A {
def inc(x: Int): Int = x+1
def dec(x: Int): Int = x-1
}
Once user hits save and asks incremental compiler to recompile it’s project it
will do the following:
To summarize, we’ll invoke Scala compiler twice: one time to recompile A.scala
and then to recompile B.scala because A has a new method dec.
However, one can easily see that in this simple scenario recompilation of B.scala
is not needed because addition of dec method to A class is irrelevant to the B
class as its not using it and it is not affected by it in any way.
In case of two files the fact that we recompile too much doesn’t sound too bad.
However, in practice, the dependency graph is rather dense so one might end
up recompiling the whole project upon a change that is irrelevant to almost all
files in the whole project. That’s exactly what happens in Play projects when
routes are modified. The nature of routes and reversed routes is that every
201
template and every controller depends on some methods defined in those two
classes (Routes and ReversedRoutes) but changes to specific route definition
usually affects only small subset of all templates and controllers.
The idea behind name hashing is to exploit that observation and make the
invalidation algorithm smarter about changes that can possibly affect a small
number of files.
Inheritance We’ll see how a method not used in another source file might
affect its compilation result. Let’s consider this structure:
// A.scala
abstract class A
// B.scala
class B extends A
// A.scala
abstract class A {
def foo(x: Int): Int
}
Now, once we recompile A.scala we could just say that since A.foo is not used
in B class then we don’t need to recompile B.scala. However, this is not true
202
because B doesn’t implement a newly introduced, abstract method and an error
should be reported.
Therefore, a simple strategy of looking at used methods for determining whether
a given dependency is relevant or not is not enough.
Enrichment pattern Here we’ll see another case of newly introduced method
(that is not used anywhere yet) that affects compilation results of other files.
This time, no inheritance will be involved but we’ll use enrichment pattern
(implicit conversions) instead.
Let’s assume we have the following structure:
// A.scala
class A
// B.scala
class B {
class AOps(a: A) {
def foo(x: Int): Int = x+1
}
implicit def richA(a: A): AOps = new AOps(a)
def bar(a: A): Int = a.foo(12) // this is expanded to richA(a).foo so we are calling AOPs.
}
// A.scala
class A {
def foo(x: Int): Int = x-1
}
Now, once we recompile A.scala and detect that there’s a new method defined
in the A class we would need to consider whether this is relevant to the depen-
dency of B.scala on A.scala. Notice that in B.scala we do not use A.foo
(it didn’t exist at the time B.scala was compiled) but we use AOps.foo and
it’s not immediately clear that AOps.foo has anything to do with A.foo. One
would need to detect the fact that a call to AOps.foo as a result of implicit
conversion richA that was inserted because we failed to find foo on A before.
This kind of analysis gets us very quickly to the implementation complexity of
Scala’s type checker and is not feasible to implement in a general case.
203
Too much information to track All of the above assumed we actually have
full information about the structure of the API and used methods preserved so
we can make use of it. However, as described in Hashing an API representation
we do not store the whole representation of the API but only its hash sum. Also,
dependencies are tracked at source file level and not at class/method level.
One could imagine reworking the current design to track more information but
it would be a very big undertaking. Also, the incremental compiler used to
preserve the whole API structure but it switched to hashing due to the resulting
infeasible memory requirements.
// A.scala
class A
// B.scala
class B {
class AOps(a: A) {
def foo(x: Int): Int = x+1
}
implicit def richA(a: A): AOps = new AOps(a)
def bar(a: A): Int = a.foo(12) // this is expanded to richA(a).foo so we are calling AOPs.
}
During the compilation of those two files we’ll extract the following information:
usedNames("A.scala"): A
usedNames("B.scala"): B, AOps, a, A, foo, x, Int, richA, AOps, bar
204
The usedNames relation track all the names mentioned in the given source file.
The nameHashes relation gives us a hash sum of the groups of members that
are put together in one bucket if they have the same simple name. In addition
to the information presented above we still track the dependency of B.scala on
A.scala.
Now, if we add a foo method to A class:
// A.scala
class A {
def foo(x: Int): Int = x-1
}
usedNames("A.scala"): A, foo
nameHashes("A.scala"): A -> ..., foo -> ...
The incremental compiler compares the name hashes before and after the change
and detects that the hash sum of foo has changed (it’s been added). Therefore,
it looks at all the source files that depend on A.scala, in our case it’s just
B.scala, and checks whether foo appears as a used name. It does, therefore it
recompiles B.scala as intended.
You can see now, that if we added another method to A like xyz then B.scala
wouldn’t be recompiled because nowhere in B.scala is the name xyz mentioned.
Therefore, if you have reasonably non-clashing names you should benefit from
a lot of dependencies between source files marked as irrelevant.
It’s very nice that this simple, name-based heuristic manages to withstand the
“enrichment pattern” test. However, name-hashing fails to pass the other test of
inheritance. In order to address that problem, we’ll need to take a closer look
at the dependencies introduced by inheritance vs dependencies introduced by
member references.
205
reference separately from dependencies introduced by inheritance. All depen-
dencies introduced by inheritance are not subject to name-hashing analysis so
they are never marked as irrelevant.
The intuition behind the dependency introduced by inheritance is very simple:
it’s a dependency a class/trait introduces by inheriting from another class/trait.
All other dependencies are called dependencies by member reference because
they are introduced by referring (selecting) a member (method, type alias, inner
class, val, etc.) from another class. Notice that in order to inherit from a class
you need to refer to it so dependencies introduced by inheritance are a strict
subset of member reference dependencies.
Here’s an example which illustrates the distinction:
// A.scala
class A {
def foo(x: Int): Int = x+1
}
// B.scala
class B(val a: A)
// C.scala
trait C
// D.scala
trait D[T]
// X.scala
class X extends A with C with D[B] {
// dependencies by inheritance: A, C, D
// dependencies by member reference: A, C, D, B
}
// Y.scala
class Y {
def test(b: B): Int = b.a.foo(12)
// dependencies by member reference: B, Int, A
}
206
To sum it up, the way we want to handle inheritance and the problems it
introduces is to track all dependencies introduced by inheritance separately and
have a much more strict way of invalidating dependencies. Essentially, whenever
there’s a dependency by inheritance it will react to any (even minor) change in
parent types.
Computing name hashes One thing we skimmed over so far is how name
hashes are actually computed.
As mentioned before, all definitions are grouped together by their simple name
and then hashed as one bucket. If a definition (for example a class) contains
other definition then those nested definitions do not contribute to a hash sum.
The nested definitions will contribute to hashes of buckets selected by their
name.
207
1. Enable the incremental compiler’s apiDebug option.
2. Add diff-utils library to sbt’s classpath. Check documentation of
sbt.extraClasspath system property in the Command-Line-Reference.
warning
Enabling the apiDebug option increases significantly the memory
consumption and degrades the performance of the incremental com-
piler. The underlying reason is that in order to produce meaningful
debugging information about interface differences the incremental
compiler has to retain the full representation of the interface instead
of just the hash sum as it does by default.
Keep this option enabled when you are debugging the incremental
compiler problem only.
curl -O https://java-diff-utils.googlecode.com/files/diffutils-1.2.1.jar
sbt -Dsbt.extraClasspath=diffutils-1.2.1.jar
[info] Loading project definition from /Users/grek/tmp/sbt-013/project
[info] Set current project to sbt-013 (in build file:/Users/grek/tmp/sbt-013/)
> set incOptions := incOptions.value.withApiDebug(true)
[info] Defining *:incOptions
[info] The new value will be used by compile:incCompileSetup, test:incCompileSetup
[info] Reapplying settings...
[info] Set current project to sbt-013 (in build file:/Users/grek/tmp/sbt-013/)
class A {
def b: Int = 123
}
class A {
def b: String = "abc"
}
and run compile again. Now if you run last compile you should see the
following lines in the debugging log
208
> last compile
[...]
[debug] Detected a change in a public API:
[debug] --- /Users/grek/tmp/sbt-013/Test.scala
[debug] +++ /Users/grek/tmp/sbt-013/Test.scala
[debug] @@ -23,7 +23,7 @@
[debug] ^inherited^ final def ##(): scala.this#Int
[debug] ^inherited^ final def synchronized[ java.lang.Object.T0 >: scala.this#Nothing <: scala
[debug] ^inherited^ final def $isInstanceOf[ java.lang.Object.T0 >: scala.this#Nothing <: scal
[debug] ^inherited^ final def $asInstanceOf[ java.lang.Object.T0 >: scala.this#Nothing <: scal
[debug] def <init>(): this#A
[debug] -def b: scala.this#Int
[debug] +def b: java.lang.this#String
[debug] }
You can see a unified diff of the two interface textual represetantions. As you
can see, the incremental compiler detected a change to the return type of b
method.
import java.io._
object A {
def openFiles(list: List[File]) =
list.map(name => new FileWriter(name))
}
Let us now consider the public interface of trait A. Note that the return type
of method openFiles is not specified explicitly, but computed by type infer-
ence to be List[FileWriter]. Suppose that after writing this source code, we
introduce some client code and then modify A.scala as follows:
import java.io._
object A {
def openFiles(list: List[File]) =
Vector(list.map(name => new BufferedWriter(new FileWriter(name))): _*)
}
209
Type inference will now compute the result type as Vector[BufferedWriter];
in other words, changing the implementation lead to a change to the public
interface, with two undesirable consequences:
1. Concerning our topic, the client code needs to be recompiled, since chang-
ing the return type of a method, in the JVM, is a binary-incompatible
interface change.
2. If our component is a released library, using our new version requires
recompiling all client code, changing the version number, and so on. Often
not good, if you distribute a library where binary compatibility becomes
an issue.
3. More in general, the client code might now even be invalid. The following
code will for instance become invalid after the change:
val a: Seq[Writer] =
new BufferedWriter(new FileWriter("bar.input")) +:
A.openFiles(List(new File("foo.input")))
Bytecode Enhancers
sbt added an extension point whereby users can effectively manipulate Java
bytecode (.class files) before the incremental compiler attempts to cache the
210
classfile hashes. This allows libraries like Ebean to function with sbt without
corrupting the compiler cache and rerunning compile every few seconds.
This splits the compile task into several subTasks:
manipulateBytecode in Compile := {
val previous = (manipulateBytecode in Compile).value
// Note: This must return a new Compiler.CompileResult with our changes.
doManipulateBytecode(previous)
}
Further references
Configuration
This part of the documentation has pages documenting particular sbt topics in
detail. Before reading anything in here, you will need the information in the
Getting Started Guide as a foundation.
211
Classpaths, sources, and resources
This page discusses how sbt builds up classpaths for different actions, like
compile, run, and test and how to override or augment these classpaths.
Basics
In sbt, the classpath includes the Scala library and (when declared as a depen-
dency) the Scala compiler. Classpath-related settings and tasks typically pro-
vide a value of type Classpath. This is an alias for Seq[Attributed[File]].
Attributed is a type that associates a heterogeneous map with each classpath
entry. Currently, this allows sbt to associate the Analysis resulting from com-
pilation with the corresponding classpath entry and for managed entries, the
ModuleID and Artifact that defined the dependency.
To explicitly extract the raw Seq[File], use the files method implicitly added
to Classpath:
sourceGenerators in Compile +=
generate( (sourceManaged in Compile).value / "some_directory")
In this example, generate is some function of type File => Seq[File] that
actually does the work. So, we are appending a new task to the list of main
source generators (sourceGenerators in Compile).
To insert a named task, which is the better approach for plugins:
212
val mySourceGenerator = taskKey[Seq[File]](...)
mySourceGenerator in Compile :=
generate( (sourceManaged in Compile).value / "some_directory")
The task method is used to refer to the actual task instead of the result of the
task.
For resources, there are similar keys resourceGenerators and resourceManaged.
Read more on How to exclude .scala source file in project folder - Google Groups
External vs internal Classpaths are also divided into internal and external
dependencies. The internal dependencies are inter-project dependencies. These
effectively put the outputs of one project on the classpath of another project.
External classpaths are the union of the unmanaged and managed classpaths.
• unmanagedClasspath
• managedClasspath
• externalDependencyClasspath
• internalDependencyClasspath
For sources:
For resources
213
• unmanagedResources These are by default built up from unmanage-
dResourceDirectories, which by default is resourceDirectory, excluding
files matched by defaultExcludes.
• managedResources By default, this is empty for standard projects. sbt
plugins will have a generated descriptor file here.
• resourceGenerators These are tasks that generate resource files. Typi-
cally, these tasks will put resources in the directory provided by resource-
Managed.
Example You have a standalone project which uses a library that loads
xxx.properties from classpath at run time. You put xxx.properties inside di-
rectory “config”. When you run “sbt run”, you want the directory to be in
classpath.
autoCompilerPlugins := true
To use a compiler plugin, you either put it in your unmanaged library directory
(lib/ by default) or add it as managed dependency in the plugin configura-
tion. addCompilerPlugin is a convenience method for specifying plugin as the
configuration for a dependency:
The compile and testCompile actions will use any compiler plugins found
in the lib directory or in the plugin configuration. You are responsible for
configuring the plugins as necessary. For example, Scala X-Ray requires the
extra option:
scalacOptions += "-Xplugin:<path-to-sxr>/sxr-0.3.0.jar"
214
Continuations Plugin Example
autoCompilerPlugins := true
scalacOptions += "-P:continuations:enable"
autoCompilerPlugins := true
libraryDependencies +=
compilerPlugin("org.scala-lang.plugins" % ("scala-continuations-plugin_" + scalaVersion.
scalacOptions += "-P:continuations:enable"
Configuring Scala
sbt needs to obtain Scala for a project and it can do this automatically or you
can configure it explicitly. The Scala version that is configured for a project
will compile, run, document, and provide a REPL for the project code. When
compiling a project, sbt needs to run the Scala compiler as well as provide
the compiler with a classpath, which may include several Scala jars, like the
reflection jar.
The most common case is when you want to use a version of Scala that is
available in a repository. The only required configuration is the Scala version
you want to use. For example,
215
scalaVersion := "2.10.0"
This will retrieve Scala from the repositories configured via the resolvers set-
ting. It will use this version for building your project: compiling, running,
scaladoc, and the REPL.
autoScalaLibrary := false
In order to compile Scala sources, the Scala library needs to be on the classpath.
When autoScalaLibrary is true, the Scala library will be on all classpaths: test,
runtime, and compile. Otherwise, you need to add it like any other dependency.
For example, the following dependency definition uses Scala only for tests:
autoScalaLibrary := false
managedScalaInstance := false
216
project in that case. Otherwise, sbt will still need access to the jars for the
Scala compiler for compilation and other tasks. You can provide them by either
declaring a dependency in the scala-tool configuration or by explicitly defining
scalaInstance.
In the first case, add the scala-tool configuration and add a dependency on
scala-compiler in this configuration. The organization is not important, but
sbt needs the module name to be scala-compiler and scala-library in order
to handle those jars appropriately. For example,
managedScalaInstance := false
// Add the usual dependency on the library as well on the compiler in the
// 'scala-tool' configuration
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-library" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value % "scala-tool"
)
managedScalaInstance := false
scalaInstance := ...
Compile / unmanagedJars += scalaInstance.value.libraryJar
217
directory may be used as the source for jars by setting scalaHome. For
example,
scalaHome := Some(file("/home/user/scala-2.10/"))
scalaHome := Some(file("/home/user/scala-2.10/"))
This will be resolved as normal, except that sbt will see if /home/user/scala-2.10/lib/scala-reflect.jar
exists. If it does, that file will be used in place of the artifact from the managed
dependency.
scalaHome := Some(file("/home/user/scala-2.10/"))
To add only some jars, filter the jars from scalaInstance before adding them.
sbt needs Scala jars to run itself since it is written in Scala. sbt uses that same
version of Scala to compile the build definitions that you write for your project
because they use sbt APIs. This version of Scala is fixed for a specific sbt release
and cannot be changed. For sbt 1.2.7, this version is Scala 2.12.7. Because this
Scala version is needed before sbt runs, the repositories used to retrieve this
version are configured in the sbt launcher.
218
Forking
By default, the run task runs in the same JVM as sbt. Forking is required under
certain circumstances, however. Or, you might want to fork Java processes when
implementing new tasks.
By default, a forked process uses the same Java and Scala versions being used
for the build and the working directory and JVM options of the current process.
This page discusses how to enable and configure forking for both run and test
tasks. Each kind of task may be configured separately by scoping the relevant
keys as explained below.
Enable forking
The fork setting controls whether forking is enabled (true) or not (false). It
can be set in the run scope to only fork run commands or in the test scope to
only fork test commands.
To fork all test tasks (test, testOnly, and testQuick) and run tasks (run,
runMain, Test / run, and Test / runMain),
fork := true
Note: run and runMain share the same configuration and cannot be configured
separately.
To enable forking all test tasks only, set fork to true in the Test scope:
See Testing for more control over how tests are assigned to JVMs and what
options to pass to each group.
219
Change working directory
// sets the working directory for `Test / run` and `Test / runMain` only
Test / run / baseDirectory := file("/path/to/working/directory/")
or specify the configuration to affect only the main or test run tasks:
Java Home
javaHome := Some(file("/path/to/jre/"))
Note that if this is set globally, it also sets the Java installation used to compile
Java sources. You can restrict it to running only by setting it in the run scope:
As with the other settings, you can specify the configuration to affect only the
main or test run tasks or just the test tasks.
220
Configuring output
By default, forked output is sent to the Logger, with standard output logged at
the Info level and standard error at the Error level. This can be configured
with the outputStrategy setting, which is of type OutputStrategy.
// send output to the provided Logger `log` after the process terminates
outputStrategy := Some(BufferedOutput(log: Logger))
As with other settings, this can be configured individually for main or test run
tasks or for test tasks.
Configuring Input
By default, the standard input of the sbt process is not forwarded to the forked
process. To enable this, configure the connectInput setting:
Direct Usage
To fork a new Java process, use the Fork API. The values of interest are
Fork.java, Fork.javac, Fork.scala, and Fork.scalac. These are of type
Fork and provide apply and fork methods. For example, to fork a new Java
process, :
ForkOptions defines the Java installation to use, the working directory, environ-
ment variables, and more. For example, :
221
val cwd: File = ...
val javaDir: File = ...
val options = ForkOptions(
envVars = Map("KEY" -> "value"),
workingDirectory = Some(cwd),
javaHome = Some(javaDir)
)
Global Settings
com.typesafe.sbteclipse.core.EclipsePlugin.EclipseKeys.withSource := true
To change the default shellPrompt for every project using this approach, create
a local plugin ~/.sbt/1.0/plugins/ShellPrompt.scala:
222
import sbt._
import Keys._
Java Sources
sbt has support for compiling Java sources with the limitation that dependency
tracking is limited to the dependencies present in compiled class files.
Usage
javacOptions += "-g:none"
As with options for the Scala compiler, the arguments are not parsed by sbt.
Multi-element options, such as -source 1.5, are specified like:
You can specify the order in which Scala and Java sources are built with the
compileOrder setting. Possible values are from the CompileOrder enumeration:
Mixed, JavaThenScala, and ScalaThenJava. If you have circular dependencies
between Scala and Java sources, you need the default, Mixed, which passes
both Java and Scala sources to scalac and then compiles the Java sources with
223
javac. If you do not have circular dependencies, you can use one of the other
two options to speed up your build by not passing the Java sources to scalac.
For example, if your Scala sources depend on your Java sources, but your Java
sources do not depend on your Scala sources, you can do:
compileOrder := CompileOrder.JavaThenScala
To specify different orders for main and test sources, scope the setting by con-
figuration:
The Scala compiler does not identify compile-time constant variables (Java spec-
ification 4.12.4) in Java source code if their definition is not a literal. This issue
has several symptoms, described in the Scala ticket SI-5333:
Since Scala 2.11.4, a similar issue arises when using a Java-defined annotation
in a Scala class. The Scala compiler does not recognize @Retention annotations
when parsing the annotation @interface from source and therefore emits the
annotation with visibility RUNTIME (SI-8928).
224
Ignoring the Scala source directories
However, there should not be any harm in leaving the Scala directories if they
are empty.
Mapping Files
Relative to a directory
The Path.relativeTo method is used to map a File to its path String relative
to a base directory or directories. The relativeTo method accepts a base
directory or sequence of base directories to relativize an input file against. The
first directory that is an ancestor of the file is used in the case of a sequence of
base directories.
For example:
import Path.relativeTo
val files: Seq[File] = file("/a/b/C.scala") :: Nil
val baseDirectories: Seq[File] = file("/a") :: Nil
val mappings: Seq[(File,String)] = files pair relativeTo(baseDirectories)
225
val expected = (file("/a/b/C.scala") -> "b/C.scala") :: Nil
assert( mappings == expected )
Rebase
The Path.rebase method relativizes an input file against one or more base
directories (the first argument) and then prepends a base String or File (the
second argument) to the result. As with relativeTo, the first base directory
that is an ancestor of the input file is used in the case of multiple base directories.
For example, the following demonstrates building a Seq[(File, String)] using
rebase:
import Path.rebase
val files: Seq[File] = file("/a/b/C.scala") :: Nil
val baseDirectories: Seq[File] = file("/a") :: Nil
val mappings: Seq[(File,String)] = files pair rebase(baseDirectories, "pre/")
import Path.rebase
val files: Seq[File] = file("/a/b/C.scala") :: Nil
val baseDirectories: Seq[File] = file("/a") :: Nil
val newBase: File = file("/new/base")
val mappings: Seq[(File,File)] = files pair rebase(baseDirectories, newBase)
Flatten
The Path.flat method provides a function that maps a file to the last compo-
nent of the path (its name). For a File to File mapping, the input file is mapped
to a file with the same name in a given target directory. For example:
import Path.flat
val files: Seq[File] = file("/a/b/C.scala") :: Nil
val mappings: Seq[(File,String)] = files pair flat
226
To build a Seq[(File, File)] using flat:
import Path.flat
val files: Seq[File] = file("/a/b/C.scala") :: Nil
val newBase: File = file("/new/base")
val mappings: Seq[(File,File)] = files pair flat(newBase)
Alternatives
To try to apply several alternative mappings for a file, use |, which is implicitly
added to a function of type A => Option[B]. For example, to try to relativize
a file against some base directories but fall back to flattening:
import Path.relativeTo
val files: Seq[File] = file("/a/b/C.scala") :: file("/zzz/D.scala") :: Nil
val baseDirectories: Seq[File] = file("/a") :: Nil
val mappings: Seq[(File,String)] = files pair ( relativeTo(baseDirectories) | flat )
Local Scala
To use a locally built Scala version, define the scalaHome setting, which is of
type Option[File]. This Scala version will only be used for the build and not
for sbt, which will still use the version it was compiled against.
Example:
scalaHome := Some(file("/path/to/scala"))
Using a local Scala version will override the scalaVersion setting and will not
work with cross building.
sbt reuses the class loader for the local Scala version. If you recompile your
local Scala version and you are using sbt interactively, run
> reload
227
Macro Projects
Introduction
1. The current macro implementation in the compiler requires that macro im-
plementations be compiled before they are used. The solution is typically
to put the macros in a subproject or in their own configuration.
2. Sometimes the macro implementation should be distributed with the main
code that uses them and sometimes the implementation should not be
distributed at all.
228
package demo
import language.experimental.macros
import scala.reflect.macros.Context
object Demo {
// Returns the tree of `a` after the typer, printed as source code.
def desugar(a: Any): String = macro desugarImpl
val s = show(a.tree)
c.Expr(
Literal(Constant(s))
)
}
}
macro/src/test/scala/demo/Usage.scala:
package demo
object Usage {
def main(args: Array[String]): Unit = {
val s = Demo.desugar(List(1, 2, 3).reverse)
println(s)
}
}
$ sbt
> macroSub/test:run
scala.collection.immutable.List.apply[Int](1, 2, 3).reverse
package demo
229
object Usage {
def main(args: Array[String]): Unit = {
val s = Demo.desugar(List(6, 4, 5).sorted)
println(s)
}
}
$ sbt
> core/run
scala.collection.immutable.List.apply[Int](6, 4, 5).sorted[Int](math.this.Ordering.Int)
Common Interface
Sometimes, the macro implementation and the macro usage should share some
common code. In this case, declare another subproject for the common code and
have the main project and the macro subproject depend on the new subproject.
For example, the project definitions from above would look like:
230
Code in util/src/main/scala/ is available for both the macroSub and main
projects to use.
Distribution
To include the macro code with the core code, add the binary and source map-
pings from the macro subproject to the core project. And also macro subproject
should be removed from core project dependency in publishing. For example,
the core Project definition above would now look like:
You may wish to disable publishing the macro implementation. This is done by
overriding publish and publishLocal to do nothing:
The techniques described here may also be used for the common interface de-
scribed in the previous section.
Paths
This page describes files, sequences of files, and file filters. The base type used
is java.io.File, but several methods are augmented through implicits:
231
Constructing a File
sbt uses java.io.File to represent a file and defines the type alias File for
java.io.File so that an extra import is not necessary. The file method
is an alias for the single-argument File constructor to simplify constructing a
new file from a String:
Additionally, sbt augments File with a / method, which is an alias for the two-
argument File constructor for building up a path:
Relative files should only be used when defining the base directory of a Project,
where they will be resolved properly.
This setting sets the location of the shell history to be in the base directory of
the build, irrespective of the project the setting is defined in:
Path Finders
232
Selecting descendants The ** method accepts a java.io.FileFilter and
selects all files matching that filter.
get This selects all files that end in .scala that are in src or a descendent
directory. The list of files is not actually evaluated until get is called:
If the filesystem changes, a second call to get on the same PathFinder object
will reflect the changes. That is, the get method reconstructs the list of files
each time. Also, get only returns Files that existed at the time it was called.
This selects all files that end in .scala that are in the src directory.
Name Filter The argument to the child and descendent selectors * and ** is
actually a NameFilter. An implicit is used to convert a String to a NameFilter
that interprets * to represent zero or more characters of any value. See the Name
Filters section below for more information.
233
When evaluated using get, this will return src/main/, lib/, and
target/classes/. The concatenated finder supports all standard meth-
ods. For example,
The first selector selects all Scala sources and the second selects all sources that
are a descendent of a .svn directory. The --- method removes all files returned
by the second selector from the sequence of files returned by the first selector.
Filtering There is a filter method that accepts a predicate of type File =>
Boolean and is non-strict:
234
Mappings The packaging and file copying methods in sbt expect values of
type Seq[(File,String)] and Seq[(File,File)], respectively. These are
mappings from the input file to its (String) path in the jar or its (File) destina-
tion. This approach replaces the relative path approach (using the ## method)
from earlier versions of sbt.
Mappings are discussed in detail on the Mapping-Files page.
File Filters
There are some useful combinators added to FileFilter. The || method de-
clares alternative FileFilters. The following example selects all Java or Scala
source files under “src”:
The -- method excludes a files matching a second filter from the files matched
by the first:
This will get right.png and left.png, but not logo.png, for example.
Parallel Execution
Task ordering
read := IO.read(file("/tmp/sample.txt"))
235
sbt is free to execute write first and then read, read first and then write, or
read and write simultaneously. Execution of these tasks is non-deterministic
because they share a file. A correct declaration of the tasks would be:
write := {
val f = file("/tmp/sample.txt")
IO.write(f, "Some content.")
f
}
read := IO.read(write.value)
This establishes an ordering: read must run after write. We’ve also guaranteed
that read will read from the same file that write created.
Practical constraints
Note: The feature described in this section is experimental. The default config-
uration of the feature is subject to change in particular.
236
Configuration
The system is thus dependent on proper tagging of tasks and then on a good
set of rules.
compile := myCompileTask.value
download := downloadImpl.value
237
The example limits:
Note that these restrictions rely on proper tagging of tasks. Also, the value
provided as the limit must be at least 1 to ensure every task is able to be
executed. sbt will generate an error if this condition is not met.
Most tasks won’t be tagged because they are very short-lived. These tasks are
automatically assigned the label Untagged. You may want to include these tasks
in the CPU rule by using the limitSum method. For example:
...
Tags.limitSum(2, Tags.CPU, Tags.Untagged)
...
Note that the limit is the first argument so that tags can be provided as varargs.
Another useful convenience function is Tags.exclusive. This specifies that a
task with the given tag should execute in isolation. It starts executing only
when no other tasks are running (even if they have the exclusive tag) and no
other tasks may start execution until it completes. For example, a task could
be tagged with a custom tag Benchmark and a rule configured to ensure such a
task is executed by itself:
...
Tags.exclusive(Benchmark)
...
Finally, for the most flexibility, you can specify a custom function of type
Map[Tag,Int] => Boolean. The Map[Tag,Int] represents the weighted
tags of a set of tasks. If the function returns true, it indicates that the
set of tasks is allowed to execute concurrently. If the return value is false,
the set of tasks will not be allowed to execute concurrently. For example,
Tags.exclusive(Benchmark) is equivalent to the following:
...
Tags.customLimit { (tags: Map[Tag,Int]) =>
val exclusive = tags.getOrElse(Benchmark, 0)
// the total number of tasks in the group
val all = tags.getOrElse(Tags.All, 0)
// if there are no exclusive tasks in this group, this rule adds no restrictions
238
exclusive == 0 ||
// If there is only one task, allow it to execute.
all == 1
}
...
There are some basic rules that custom functions must follow, but the main one
to be aware of in practice is that if there is only one task, it must be allowed to
execute. sbt will generate a warning if the user defines restrictions that prevent
a task from executing at all and will then execute the task anyway.
Built-in Tags and Rules Built-in tags are defined in the Tags object. All
tags listed below must be qualified by this object. For example, CPU refers to
the Tags.CPU value.
The built-in semantic tags are:
Of additional note is that the default test task will propagate its tags to each
child task created for each test class.
The default rules provide the same behavior as previous versions of sbt:
239
Global / concurrentRestrictions := {
val max = Runtime.getRuntime.availableProcessors
Tags.limitAll(if(parallelExecution.value) max else 1) :: Nil
}
Custom Tags To define a new tag, pass a String to the Tags.Tag method.
For example:
aCustomTask := aImpl.value
Global / concurrentRestrictions +=
Tags.limit(Custom, 1)
Future work
This is an experimental feature and there are several aspects that may change
or require further work.
compile := myCompileTask.value
compile := {
val result = compile.value
... do some post processing ...
}
240
Fractional weighting Weights are currently ints, but could be changed to
be doubles if fractional weights would be useful. It is important to preserve a
consistent notion of what a weight of 1 means so that built-in and custom tasks
share this definition and useful rules can be written.
Default Behavior User feedback on what custom rules work for what work-
loads will help determine a good set of default tags and rules.
External Processes
Usage
Scala includes a process library to simplify working with external processes. Use
import scala.sys.process._ to bring the implicit conversions into scope.
To run an external command, follow it with an exclamation mark !:
241
instance of scala.sys.process.Process, which can be used to destroy the
process before it completes. With no arguments, the ! method sends output to
standard output and standard error. You can pass a Logger to the ! method
to send output to the Logger:
If you need to set the working directory or modify the environment, call
scala.sys.process.Process explicitly, passing the command sequence (com-
mand and argument list) or command string first and the working directory
second. Any environment variables can be passed as a vararg list of key/value
String pairs.
Process("ls" :: "-l" :: Nil, Path.userHome, "key1" -> value1, "key2" -> value2) ! log
• a #&& b Execute a. If the exit code is nonzero, return that exit code and
do not execute b. If the exit code is zero, execute b and return its exit
code.
• a #|| b Execute a. If the exit code is zero, return zero for the exit code
and do not execute b. If the exit code is nonzero, execute b and return
its exit code.
• a #| b Execute a and b, piping the output of a to the input of b.
There are also operators defined for redirecting output to Files and input from
Files and URLs. In the following definitions, url is an instance of URL and file
is an instance of File.
• a #< url or url #> a Use url as the input to a. a may be a File or a
command.
• a #< file or file #> a Use file as the input to a. a may be a File or
a command.
• a #> file or file #< a Write the output of a to file. a may be a File,
URL, or a command.
• a #>> file or file #<< a Append the output of a to file. a may be a
File, URL, or a command.
There are some additional methods to get the output from a forked process into
a String or the output lines as a Stream[String]. Here are some examples,
but see the ProcessBuilder API for details.
242
val listed: String = "ls" !!
val lines2: Stream[String] = "ls" lines_!
Finally, there is a cat method to send the contents of Files and URLs to standard
output.
Copy a File:
"find src -name *.scala -exec grep null {} ;" #| "xargs test -z" #&& "echo null-free" #
Use cat:
The run and console actions provide a means for running user code in the same
virtual machine as sbt.
run also exists in a variant called runMain that takes an additional initial argu-
ment allowing you to specify the fully qualified name of the main class you want
243
to run. run andrunMain share the same configuration and cannot be configured
separately.
This page describes the problems with running user code in the same virtual
machine as sbt, how sbt handles these problems, what types of code can use
this feature, and what types of code must use a forked jvm. Skip to User Code
if you just want to see when you should use a forked jvm.
Problems
System.exit User code can call System.exit, which normally shuts down the
JVM. Because the run and console actions run inside the same JVM as sbt,
this also ends the build and requires restarting sbt.
Threads User code can also start other threads. Threads can be left running
after the main method returns. In particular, creating a GUI creates several
threads, some of which may not terminate until the JVM terminates. The
program is not completed until either System.exit is called or all non-daemon
threads terminate.
sbt’s Solutions
Threads sbt makes a list of all threads running before executing user code.
After the user code returns, sbt can then determine the threads created by the
user code. For each user-created thread, sbt replaces the uncaught exception
handler with a custom one that handles the custom SecurityException thrown
by calls to System.exit and delegates to the original handler for everything else.
sbt then waits for each created thread to exit or for System.exit to be called.
sbt handles a call to System.exit as described above.
244
A user-created thread is one that is not in the system thread group and is not
an AWT implementation thread (e.g. AWT-XAWT, AWT-Windows). User-created
threads include the AWT-EventQueue-* thread(s).
User Code Given the above, when can user code be run with the run and
console actions?
The user code cannot rely on shutdown hooks and at least one of the following
situations must apply for user code to run in the same JVM:
The requirements on threading and shutdown hooks are required because the
JVM does not actually shut down. So, shutdown hooks cannot be run and
threads are not terminated unless they stop when interrupted. If these require-
ments are not met, code must run in a forked jvm.
The feature of allowing System.exit and multiple threads to be used cannot
completely emulate the situation of running in a separate JVM and is intended
for development. Program execution should be checked in a forked jvm when
using multiple threads or System.exit.
As of sbt 0.13.1, multiple run instances can be managed. There can only be one
application that uses AWT at a time, however.
Testing
Basics
245
The resources may be accessed from tests by using the getResource methods
of java.lang.Class or java.lang.ClassLoader.
The main Scala testing frameworks ( ScalaCheck, ScalaTest, and specs2) provide
an implementation of the common test interface and only need to be added to the
classpath to work with sbt. For example, ScalaCheck may be used by declaring
it as a managed dependency:
Test is the configuration and means that ScalaCheck will only be on the test
classpath and it isn’t needed by the main sources. This is generally good practice
for libraries because your users don’t typically need your test dependencies to
use your library.
With the library dependency defined, you can then add test sources in the
locations listed above and compile and run tests. The tasks for running tests
are test and testOnly. The test task accepts no command line arguments
and runs all tests:
> test
testOnly The testOnly task accepts a whitespace separated list of test names
to run. For example:
testQuick The testQuick task, like testOnly, allows to filter the tests to
run to specific tests or wildcards using the same syntax to indicate the filters.
In addition to the explicit filter, only the tests that satisfy one of the following
conditions are run:
246
Tab completion Tab completion is provided for test names based on the
results of the last test:compile. This means that a new sources aren’t available
for tab completion until they are compiled and deleted sources won’t be removed
from tab completion until a recompile. A new test source can still be manually
written out and run using testOnly.
Other tasks Tasks that are available for main sources are generally available
for test sources, but are prefixed with Test / on the command line and are
referenced in Scala code with Test / as well. These tasks include:
• Test / compile
• Test / console
• Test / consoleQuick
• Test / run
• Test / runMain
Output
By default, logging is buffered for each test source file until all tests for that file
complete. This can be disabled by setting logBuffered:
Test Reports By default, sbt will generate JUnit XML test reports for all
tests in the build, located in the target/test-reports directory for a project.
This can be disabled by disabling the JUnitXmlReportPlugin
Options
To specify test framework arguments as part of the build, add options con-
structed by Tests.Argument:
247
Test / testOptions += Tests.Argument("-verbosity", "1")
Setup and Cleanup Specify setup and cleanup actions using Tests.Setup
and Tests.Cleanup. These accept either a function of type () => Unit or a
function of type ClassLoader => Unit. The variant that accepts a ClassLoader
is passed the class loader that is (or was) used for running the tests. It provides
access to the test classes as well as the test framework classes.
Examples:
Disable Parallel Execution of Tests By default, sbt runs all tasks in par-
allel and within the same JVM as sbt itself. Because each test is mapped to
a task, tests are also run in parallel by default. To make tests within a given
project execute serially: :
Filter classes If you want to only run test classes whose name ends with
“Test”, use Tests.Filter:
248
Forking tests The setting:
specifies that all tests will be executed in a single external JVM. See Forking
for configuring standard options for forking. By default, tests executed in a
forked JVM are executed sequentially. More control over how tests are assigned
to JVMs and what options to pass to those is available with testGrouping key.
For example in build.sbt:
import Tests._
{
def groupByFirst(tests: Seq[TestDefinition]) =
tests groupBy (_.name(0)) map {
case (letter, tests) =>
val options = ForkOptions().withRunJVMOptions(Vector("-Dfirst.letter"+letter))
new Group(letter.toString, tests, SubProcess(options))
} toSeq
The tests in a single group are run sequentially. Control the number of
forked JVMs allowed to run at the same time by setting the limit on
Tags.ForkedTestGroup tag, which is 1 by default. Setup and Cleanup actions
cannot be provided with the actual test class loader when a group is forked.
In addition, forked tests can optionally be run in parallel within the forked
JVM(s), using the following setting:
You can add an additional test configuration to have a separate set of test
sources and associated compilation, packaging, and testing tasks and settings.
The steps are:
249
The following two examples demonstrate this. The first example shows how
to enable integration tests. The second shows how to define a customized test
configuration. This allows you to define multiple types of tests per project.
The standard testing tasks are available, but must be prefixed with it:. For
example,
250
Test / testOptions += ...
then these will be picked up by the Test configuration and in turn by the
IntegrationTest configuration. Options can be added specifically for integra-
tion tests by putting them in the IntegrationTest configuration:
Or, use := to overwrite any existing options, declaring these to be the definitive
integration test options:
The extend(Test) part means to delegate to Test for undefined FunTest set-
tings. The line that adds the tasks and settings for the new test configuration
is:
settings(inConfig(FunTest)(Defaults.testSettings))
251
This says to add test and settings tasks in the FunTest configuration.
We could have done it this way for integration tests as well. In fact,
Defaults.itSettings is a convenience definition: val itSettings =
inConfig(IntegrationTest)(Defaults.testSettings).
The comments in the integration test section hold, except with IntegrationTest
replaced with FunTest and "it" replaced with "fun". For example, test options
can be configured specifically for FunTest:
252
• We filter the tests to be run for each configuration.
To run standard unit tests, run test (or equivalently, Test / test):
> test
To run tests for the added configuration (here, "FunTest"), prefix it with the
configuration name as before:
The tests to run in parallel would be run with test and the ones to run in serial
would be run with serial:test.
JUnit
Support for JUnit is provided by junit-interface. To add JUnit support into your
project, add the junit-interface dependency in your project’s main build.sbt file.
Extensions
This page describes adding support for additional testing libraries and defining
additional test reporters. You do this by implementing sbt interfaces (described
below). If you are the author of the testing framework, you can depend on
the test interface as a provided dependency. Alternatively, anyone can provide
support for a test framework by implementing the interfaces in a separate project
and packaging the project as an sbt Plugin.
253
Custom Test Framework The main Scala testing libraries have built-in sup-
port for sbt. To add support for a different framework, implement the uniform
test interface.
Custom Test Reporters Test frameworks report status and results to test
reporters. You can create a new test reporter by implementing either TestRe-
portListener or TestsListener.
Specify the test reporters you want to use by overriding the testListeners
setting in your project definition.
testListeners += customTestListener
Dependency Management
This part of the documentation has pages documenting particular sbt topics in
detail. Before reading anything in here, you will need the information in the
Getting Started Guide as a foundation.
Artifacts
By default, the published artifacts are the main binary jar, a jar containing the
main sources and resources, and a jar containing the API documentation. You
can add artifacts for the test classes, sources, or API or you can disable some
of the main artifacts.
To add all test artifacts:
254
// enable publishing the jar produced by `test:package`
publishArtifact in (Test, packageBin) := true
255
artifactName := { (sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
artifact.name + "-" + module.revision + "." + artifact.extension
}
myTask := {
val (art, file) = packagedArtifact.in(Compile, packageBin).value
println("Artifact definition: " + art)
println("Packaged file: " + file.getAbsolutePath)
}
In addition to configuring the built-in artifacts, you can declare other artifacts
to publish. Multiple artifacts are allowed when using Ivy metadata, but a Maven
POM file only supports distinguishing artifacts based on classifiers and these
are not recorded in the POM.
Basic Artifact construction look like:
For example:
See the Ivy documentation for more details on artifacts. See the Artifact API
for combining the parameters above and specifying Configurations and extra
attributes.
To declare these artifacts for publishing, map them to the task that generates
the artifact:
256
val myImageTask = taskKey[File](...)
myImageTask := {
val artifact: File = makeArtifact(...)
artifact
}
...
lazy val proj = Project(...)
.settings( addArtifact(...).settings )
...
A common use case for web applications is to publish the .war file instead of
the .jar file.
To specify the artifacts to use from a dependency that has custom or multiple
artifacts, use the artifacts method on your dependencies. For example:
The from and classifer methods (described on the Library Management page)
are actually convenience methods that translate to artifacts:
257
def from(url: String) = artifacts( Artifact(name, new URL(url)) )
def classifier(c: String) = artifacts( Artifact(name, c) )
Background
258
5. Overriding all of the above, skip in update := true will tell sbt to
never perform resolution. Note that this can cause dependent tasks to
fail. For example, compilation may fail if jars have been deleted from the
cache (and so needed classes are missing) or a dependency has been added
(but will not be resolved because skip is true). Also, update itself will
immediately fail if resolution has not been allowed to run since the last
clean.
1. Run update explicitly. This will typically fix problems with out of date
SNAPSHOTs or locally published artifacts.
2. If a file cannot be found, look at the output of update to see where Ivy
is looking for the file. This may help diagnose an incorrectly defined
dependency or a dependency that is actually not present in a repository.
3. last update contains more information about the most recent resolution
and download. The amount of debugging output from Ivy is high, so you
may want to use last-grep (run help last-grep for usage).
4. Run clean and then update. If this works, it could indicate a bug in sbt,
but the problem would need to be reproduced in order to diagnose and fix
it.
5. Before deleting all of the Ivy cache, first try deleting files in
~/.ivy2/cache related to problematic dependencies. For example,
if there are problems with dependency "org.example" % "demo" %
"1.0", delete ~/.ivy2/cache/org.example/demo/1.0/ and retry update.
This avoids needing to redownload all dependencies.
6. Normal sbt usage should not require deleting files from ~/.ivy2/cache,
especially if the first four steps have been followed. If deleting the cache
fixes a dependency management issue, please try to reproduce the issue
and submit a test case.
Plugins
These troubleshooting steps can be run for plugins by changing to the build def-
inition project, running the commands, and then returning to the main project.
For example:
259
Notes
Library Management
There’s now a getting started page about library management, which you may
want to read first.
Documentation Maintenance Note: it would be nice to remove the overlap be-
tween this page and the getting started page, leaving this page with the more
advanced topics such as checksums and external Ivy files.
Introduction
There are two ways for you to manage libraries with sbt: manually or auto-
matically. These two ways can be mixed as well. This page discusses the two
approaches. All configurations shown here are settings that go either directly
in a .sbt file or are appended to the settings of a Project in a .scala file.
Manually managing dependencies involves copying any jars that you want to use
to the lib directory. sbt will put these jars on the classpath during compilation,
testing, running, and when using the interpreter. You are responsible for adding,
removing, updating, and otherwise managing the jars in this directory. No
modifications to your project definition are required to use this method unless
you would like to change the location of the directory you store the jars in.
260
To change the directory jars are stored in, change the unmanagedBase setting
in your project definition. For example, to use custom_lib/:
If you want more control and flexibility, override the unmanagedJars task, which
ultimately provides the manual dependencies to sbt. The default implementa-
tion is roughly:
If you want to add jars from multiple directories in addition to the default
directory, you can do:
sbt uses Apache Ivy to implement dependency management in all three cases.
The default is to use inline declarations, but external configuration can be ex-
plicitly selected. The following sections describe how to use each method of
automatic dependency management.
261
Dependencies Declaring a dependency looks like:
or
If you are using a dependency that was built with sbt, double the first % to be
%%:
This will use the right jar for the dependency built with the version of Scala
that you are currently using. If you get an error while resolving this kind of
dependency, that dependency probably wasn’t published for the version of Scala
you are using. See Cross Build for details.
Ivy can select the latest revision of a module according to constraints you specify.
Instead of a fixed revision like "1.6.1", you specify "latest.integration",
"2.9.+", or "[1.0,)". See the Ivy revisions documentation for details.
For example:
262
sbt can search your local Maven repository if you add it as a repository:
To use the local repository, but not the Maven Central repository:
Override all resolvers for all builds The repositories used to retrieve sbt,
Scala, plugins, and application dependencies can be configured globally and de-
clared to override the resolvers configured in a build or plugin definition. There
are two parts:
[repositories]
local
my-maven-repo: https://example.org/repo
my-ivy-repo: https://example.org/ivy-repo/, [organization]/[module]/[revision]/[type]s/[artif
263
Explicit URL If your project requires a dependency that is not present in a
repository, a direct URL to its jar can be specified as follows:
The URL is only used as a fallback if the dependency cannot be found through
the configured repositories. Also, the explicit URL is not included in published
metadata (that is, the pom or ivy.xml).
Classifiers You can specify the classifier for a dependency using the
classifier method. For example, to get the jdk15 version of TestNG:
libraryDependencies +=
"org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-windows" classifie
transitiveClassifiers := Seq("sources")
libraryDependencies +=
"log4j" % "log4j" % "1.2.15" exclude("javax.jms", "jms")
264
The excludeAll method is more flexible, but because it cannot be represented
in a pom.xml, it should only be used when a pom doesn’t need to be generated.
For example,
libraryDependencies +=
"log4j" % "log4j" % "1.2.15" excludeAll(
ExclusionRule(organization = "com.sun.jdmk"),
ExclusionRule(organization = "com.sun.jmx"),
ExclusionRule(organization = "javax.jms")
)
libraryDependencies +=
"org.apache.felix" % "org.apache.felix.framework" % "1.8.0" withSources() withJavadoc()
Note that this is not transitive. Use the update-*classifiers tasks for that.
265
projectID := {
val previous = projectID.value
previous.extra("color" -> "blue", "component" -> "compiler-interface")
}
Inline Ivy XML sbt additionally supports directly specifying the configura-
tions or dependencies sections of an Ivy configuration file inline. You can mix
this with inline Scala dependency and repository declarations.
For example:
ivyXML :=
<dependencies>
<dependency org="javax.mail" name="mail" rev="1.4.2">
<exclude module="activation"/>
</dependency>
</dependencies>
Ivy Home Directory By default, sbt uses the standard Ivy home direc-
tory location ${user.home}/.ivy2/. This can be configured machine-wide, for
use by both the sbt launcher and by projects, by setting the system property
sbt.ivy.home in the sbt startup script (described in Setup).
For example:
266
Conflict Management The conflict manager decides what to do when de-
pendency resolution brings in different versions of the same library. By default,
the latest revision is selected. This can be changed by setting conflictManager,
which has type ConflictManager. See the Ivy documentation for details on the
different conflict managers. For example, to specify that no conflicts are allowed,
conflictManager := ConflictManager.strict
With this set, any conflicts will generate an error. To resolve a conflict, you
must configure a dependency override, which is explained in a later section.
The default conflict manager will select the newer version of akka-actor, 2.3.7.
This can be confirmed in the output of show update, which shows the newer
version as being selected and the older version as evicted.
[info] com.typesafe.akka:akka-actor_2.10
[info] - 2.3.7
...
[info] - 2.1.4
...
[info] evicted: true
[info] evictedReason: latest-revision
...
[info] callers: org.w3:banana-rdf_2.10:0.4
Furthermore, the binary version compatibility of the akka-actor 2.1.4 and 2.3.7
are not guaranteed since the second segment has bumped up. sbt 0.13.6+ detects
this automatically and prints out the following warning:
267
Since akka-actor 2.1.4 and 2.3.7 are not binary compatible, the only way to fix
this is to downgrade your dependency to akka-actor 2.1.4, or upgrade banana-rdf
to use akka-actor 2.3.
The default conflict manager chooses the latest revision of log4j, 1.2.17:
This will not add a direct dependency on log4j, but will force the revision to be
1.2.16. This is confirmed by the output of show update:
268
sbt 0.13.6+ will try to reconstruct dependencies tree when it fails to resolve a
managed dependency. This is an approximation, but it should help you figure
out where the problematic dependency is coming from. When possible sbt will
display the source position next to the modules:
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: UNRESOLVED DEPENDENCIES ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: foundrylogic.vpp#vpp;2.2.1: not found
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn]
[warn] Note: Unresolved dependencies path:
[warn] foundrylogic.vpp:vpp:2.2.1
[warn] +- org.apache.cayenne:cayenne-tools:3.0.2
[warn] +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2 (/foo/some-test/build.sb
[warn] +- d:d_2.10:0.1-SNAPSHOT
Configurations Ivy configurations are a useful feature for your build when
you need custom groups of dependencies, such as for a plugin. Ivy configurations
are essentially named sets of dependencies. You can read the Ivy documentation
for details.
The built-in use of configurations in sbt is similar to scopes in Maven. sbt adds
dependencies to different classpaths by the configuration that they are defined
in. See the description of Maven Scopes for details.
You put a dependency in a configuration by selecting one or more of its con-
figurations to map to one or more of your project’s configurations. The most
common case is to have one of your configurations A use a dependency’s config-
uration B. The mapping for this looks like "A->B". To apply this mapping to a
dependency, add it to the end of your dependency definition:
This says that your project’s "test" configuration uses ScalaTest’s "compile"
configuration. See the Ivy documentation for more advanced mappings. Most
projects published to Maven repositories will use the "compile" configuration.
A useful application of configurations is to group dependencies that are not used
on normal classpaths. For example, your project might use a "js" configuration
269
to automatically download jQuery and then include it in your jar by modifying
resources. For example:
ivyConfigurations += JS
The config method defines a new configuration with name "js" and makes it
private to the project so that it is not used for publishing. See Update Report
for more information on selecting managed artifacts.
A configuration without a mapping (no "->") is mapped to "default" or
"compile". The -> is only needed when mapping to a different configuration
than those. The ScalaTest dependency above can then be shortened to:
External Maven or Ivy For this method, create the configuration files as you
would for Maven (pom.xml) or Ivy (ivy.xml and optionally ivysettings.xml).
External configuration is selected by using one of the following expressions.
externalIvySettings()
or
externalIvySettings(baseDirectory.value / "custom-settings-name.xml")
or
externalIvySettingsURL(url("your_url_here"))
externalIvyFile()
or
270
externalIvyFile(Def.setting(baseDirectory.value / "custom-name.xml"))
Because Ivy files specify their own configurations, sbt needs to know which con-
figurations to use for the compile, runtime, and test classpaths. For example,
to specify that the Compile classpath should use the ‘default’ configuration:
externalPom()
or
externalPom(Def.setting(baseDirectory.value / "custom-name.xml"))
Full Ivy Example For example, a build.sbt using external Ivy files might
look like:
externalIvySettings()
externalIvyFile(Def.setting(baseDirectory.value / "ivyA.xml"))
Compile / classpathConfiguration := Compile
Test / classpathConfiguration := Test
Runtime / classpathConfiguration := Runtime
271
Known limitations Maven support is dependent on Ivy’s support for Maven
POMs. Known issues with this support:
Proxy Repositories
It’s often the case that users wish to set up a maven/ivy proxy repository in-
side their corporate firewall, and have developer sbt instances resolve artifacts
through such a proxy. Let’s detail what exact changes must be made for this
to work.
Overview
The situation arises when many developers inside an organization are attempting
to resolve artifacts. Each developer’s machine will hit the internet and download
an artifact, regardless of whether or not another on the team has already done so.
Proxy repositories provide a single point of remote download for an organization.
In addition to control and security concerns, Proxy repositories are primarily
important for increased speed across a team.
There are many good proxy repository solutions out there, with the big three
being (in alphabetical order):
• Archiva
• Artifactory
• Nexus
Once you have a proxy repository installed and configured, then it’s time to
configure sbt for your needs. Read the note at the bottom about proxy issues
with ivy repositories.
sbt Configuration
sbt requires configuration in two places to make use of a proxy repository. The
first is the ~/.sbt/repositories file, and the second is the launcher script.
272
Figure 7: image
273
~/.sbt/repositories
The repositories file is an external configuration for the Launcher. The exact
syntax for the configuration file is detailed in the sbt Launcher Configuration.
Here’s an example config:
[repositories]
local
my-ivy-proxy-releases: http://repo.company.com/ivy-releases/, [organization]/[module]/(scal
my-maven-proxy-releases: http://repo.company.com/maven-releases/
In case you need to define credentials to connect to your proxy repository, define
an environment variable SBT_CREDENTIALS that points to the file containing
your credentials:
export SBT_CREDENTIALS="$HOME/.ivy2/.credentials"
274
If the above does not work for your system, then another approach is to explicitly
provide the boot credentials via:
-Dsbt.boot.credentials$HOME/TO/.ivy2/credentials
Launcher Script The sbt launcher supports two configuration options that al-
low the usage of proxy repositories. The first is the sbt.override.build.repos
setting and the second is the sbt.repository.config setting.
-Dsbt.override.build.repos=true
-Dsbt.repository.config=<path-to-your-repo-file>
This is only necessary if users do not already have their own default repository
file.
The most common mistake made when setting up a proxy repository for sbt is
attempting to merge both maven and ivy repositories into the same proxy repos-
itory. While some repository managers will allow this, it’s not recommended to
do so.
Even if your company does not use ivy, sbt uses a custom layout to handle
binary compatibility constraints of its own plugins. To ensure that these are
275
Figure 8: image
276
resolved correctly, simply set up two virtual/proxy repositories, one for maven
and one for ivy.
Here’s an example setup:
NOTE: If using Nexus as the proxy repository, then it is very important
that you set the layout policy to “permissive” for the proxy mapping that you
create to the upstream repository http://repo.scala-sbt.org/scalasbt/sbt-plugin-
releases. If you do not, Nexus will stop short of proxying the original request to
this url and issue a HTTP 404 in its place and the dependency will not resolve.
Publishing
This page describes how to publish your project. Publishing consists of upload-
ing a descriptor, such as an Ivy file or Maven POM, and artifacts, such as a
jar or war, to a repository so that other projects can specify your project as a
dependency.
The publish action is used to publish your project to a remote repository.
To use publishing, you need to specify the repository to publish to and the
credentials to use. Once these are set up, you can run publish.
The publishLocal action is used to publish your project to your Ivy local file
repository, which is usually located at ~/.ivy2/local/. You can then use this
project from other projects on the same machine.
If you’re using Maven repositories you will also have to select the right repository
depending on your artifacts: SNAPSHOT versions go to the /snapshot repos-
itory while other versions go to the /releases repository. Doing this selection
can be done by using the value of the isSnapshot SettingKey:
277
publishTo := {
val nexus = "https://my.artifact.repo.net/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
Credentials
There are two ways to specify credentials for such a repository. The first is to
specify them inline:
The second and better way is to load them from a file, for example:
The credentials file is a properties file with keys realm, host, user, and
password. For example:
Cross-publishing
Published artifacts
By default, the main binary jar, a sources jar, and a API documentation jar
are published. You can declare other types of artifacts to publish and disable
or modify the default artifacts. See the Artifacts page for details.
278
Modifying the generated POM
pomExtra :=
<licenses>
<license>
<name>Apache 2</name>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
makePom adds to the POM any Maven-style repositories you have declared. You
can filter these by modifying pomRepositoryFilter, which by default excludes
local repositories. To instead only include local repositories:
There is also a pomPostProcess setting that can be used to manipulate the final
XML before it is written. It’s type is Node => Node.
Publishing Locally
The publishLocal command will publish to the local Ivy repository. By default,
this is in ${user.home}/.ivy2/local. Other projects on the same machine can
then list the project as a dependency. For example, if the SBT project you are
publishing has configuration parameters like:
organization := "org.me"
version := "0.1-SNAPSHOT"
279
Then another project can depend on it:
The version number you select must end with SNAPSHOT, or you must change
the version number each time you publish. Ivy maintains a cache, and it stores
even local projects in that cache. If Ivy already has a version cached, it will
not check the local repository for updates, unless the version number matches
a changing pattern, and SNAPSHOT is one such pattern.
Skipping Publishing
Resolvers
Maven
resolvers +=
"Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
This is the most common kind of user-defined resolvers. The rest of this page
describes how to define other types of repositories.
Predefined
280
• Resolver.typesafeIvyRepo("releases") (or “snapshots”) This is Type-
safe Ivy Repository at https://repo.typesafe.com/typesafe/ivy-releases
• Resolver.sbtPluginRepo("releases") (or “snapshots”) This is
sbt Community Repository at https://repo.scala-sbt.org/scalasbt/
sbt-plugin-releases
• Resolver.bintrayRepo("owner", "repo") This is the Bintray reposi-
tory at https://dl.bintray.com/{[}owner{]}/{[}repo{]}/
• Resolver.jcenterRepo This is the Bintray JCenter repository at https:
//jcenter.bintray.com/
For example, to use the java.net repository, use the following setting in your
build definition:
resolvers += JavaNet1Repository
Predefined repositories will go under Resolver going forward so they are in one
place:
Resolver.sonatypeRepo("releases") // Or "snapshots"
Custom
sbt provides an interface to the repository types available in Ivy: file, URL, SSH,
and SFTP. A key feature of repositories in Ivy is using patterns to configure
repositories.
Construct a repository definition using the factory in sbt.Resolver for the
desired type. This factory creates a Repository object that can be further
configured. The following table contains links to the Ivy documentation for the
repository type and the API documentation for the factory and repository class.
The SSH and SFTP repositories are configured identically except for the name
of the factory. Use Resolver.ssh for SSH and Resolver.sftp for SFTP.
Type
Factory
Ivy Docs
Factory API
Repository Class API
Filesystem
Resolver.file
Ivy filesystem
281
filesystem factory
FileRepository API
SFTP
Resolver.sftp
Ivy sftp
sftp factory
SftpRepository API
SSH
Resolver.ssh
Ivy ssh
ssh factory
SshRepository API
URL
Resolver.url
Ivy url
url factory
URLRepository API
Basic Examples These are basic examples that use the default Maven-style
repository layout.
or customize the layout pattern described in the Custom Layout section below.
282
SFTP and SSH Repositories The following defines a repository that is
served by SFTP from host "example.org":
Authentication for the repositories returned by sftp and ssh can be configured
by the as methods.
To use password authentication:
resolvers += {
val keyFile: File = ...
Resolver.ssh("my-ssh-repo", "example.org") as("user", keyFile, "keyFilePassword")
}
283
Custom Layout These examples specify custom repository layouts using pat-
terns. The factory methods accept an Patterns instance that defines the pat-
terns to use. The patterns are first resolved against the base file or URL. The
default patterns give the default Maven-style layout. Provide a different Pat-
terns object to use a different layout. For example:
You can specify multiple patterns or patterns for the metadata and artifacts
separately. You can also specify whether the repository should be Maven com-
patible (as defined by Ivy). See the patterns API for the methods to use.
For filesystem and URL repositories, you can specify absolute patterns by omit-
ting the base URL, passing an empty Patterns instance, and using ivys and
artifacts:
Update Report
284
def matching(f: DependencyFilter): Seq[File]
Any argument to select may be omitted, in which case all values are allowed
for the corresponding component. For example, if the ConfigurationFilter
is not specified, all configurations are accepted. The individual filter types are
discussed below.
Filter Basics Configuration, module, and artifact filters are typically built
by applying a NameFilter to each component of a Configuration, ModuleID,
or Artifact. A basic NameFilter is implicitly constructed from a String, with
* interpreted as a wildcard.
import sbt._
// each argument is of type NameFilter
val mf: ModuleFilter = moduleFilter(organization = "*sbt*",
name = "main" | "actions", revision = "1.*" - "1.0")
import sbt._
// here the function value of type String => Boolean is implicitly converted to a NameFilter
val nf: NameFilter = (s: String) => s.startsWith("dispatch-")
285
ConfigurationFilter A configuration filter essentially wraps a NameFilter
and is explicitly constructed by the configurationFilter method:
import sbt._
val a: ConfigurationFilter = Set("compile", "test")
val b: ConfigurationFilter = (c: String) => c.startsWith("r")
val c: ConfigurationFilter = a | b
import sbt._
val a: ModuleFilter = moduleFilter(name = "dispatch-twitter", revision = "0.7.8")
val b: ModuleFilter = moduleFilter(name = "dispatch-*")
val c: ModuleFilter = b - a
286
def artifactFilter(name: NameFilter = ..., `type`: NameFilter = ...,
extension: NameFilter = ..., classifier: NameFilter = ...): ArtifactFilter
import sbt._
val a: ArtifactFilter = artifactFilter(classifier = "javadoc")
val b: ArtifactFilter = artifactFilter(`type` = "jar")
val c: ArtifactFilter = b - a
import sbt._
Here, we used && and || to combine individual component filters into a depen-
dency filter, which can then be provided to the UpdateReport.matches method.
Alternatively, the UpdateReport.select method may be used, which is equiv-
alent to calling matches with its arguments combined with &&.
Cached Resolution
287
Setup
To set up Cached Resolution include the following setting in your project’s build:
updateOptions := updateOptions.value.withCachedResolution(true)
Dependency as a graph
Cached Resolution
288
The actual speedup will vary case by case, but you should see significant speedup
if you have many subprojects. An initial report from a user showed a change
from 260s to 25s. Your mileage may vary.
Cached Resolution is an experimental feature, and you might run into some
issues. When you see them please report to GitHub Issue or sbt-dev list.
First runs The first time you run, Cached Resolution will likely be slow
since it needs to resolve all minigraphs and save the result into the filesystem.
Whenever you add a new node the system has not seen, it will save the minigraph.
The second run onwards should be faster, but comparing full-resolution update
with second run onwards might not be a fair comparison.
Ivy fidelity is not guaranteed Some of the Ivy behavior doesn’t make sense,
especially around Maven emulation. For example, it seems to treat all transitive
dependencies introduced by Maven-published library as force() even when the
original pom.xml doesn’t say to:
289
There are also some issues around multiple dependencies to the same library
with different Maven classifiers. In these cases, reproducing the exact result as
normal update may not make sense or is downright impossible.
updateOptions := updateOptions.value.withLatestSnapshots(false)
Consolidated resolution
updateOptions := updateOptions.value.withConsolidatedResolution(true)
This feature is specifically designed to address Ivy resolution being slow for multi-
module projects. Consolidated resolution aims to fix this issue by artificially
constructing an Ivy dependency graph for the unique managed dependencies.
If two subprojects introduce identical external dependencies, both subprojects
should consolidate to the same graph, and therefore resolve immediately for the
second update.
Motivation
sbt internally uses Apache Ivy to resolve library dependencies. While sbt has
benefited from not having to reinvent its own dependency resolution engine
all these years, we are increasingly seeing scalability challenges especially for
projects with both multiple subprojects and large dependency graph. There are
several factors involved in sbt’s resolution scalability:
290
• Number of transitive nodes (libraries) in the graph
• Exclusion and override rules
• Number of subprojects
• Configurations
• Number of repositories and their availability
• Classifiers (additional sources and docs used by IDE)
Of the above factors, the one that has the most impact is the number of transi-
tive nodes.
1. The more nodes there are the greater the chance of version conflicts. Con-
flicts are resolved typically by picking the latest version within the same
library.
2. The more nodes there are, the more it needs to backtrack to check for
exclusion and override rules.
Exclusion and override rules are applied transitively, so any time a new node is
introduced to the graph it needs to check its parent node’s rules, its grandparent
node’s rules, great-grandparent node’s rules, etc.
sbt treats configurations and subprojects to be independent dependency graph.
This allows us to include arbitrary libraries for different configurations and sub-
projects, but if the dependency resolution is slow, the linear scaling starts to
hurt. There have been prior efforts to cache the result of library dependencies,
but it still resulted in full resolution when libraryDependencies has changed.
This part of the documentation has pages documenting particular sbt topics in
detail. Before reading anything in here, you will need the information in the
Getting Started Guide as a foundation.
Tasks
Tasks and settings are introduced in the getting started guide, which you may
wish to read first. This page has additional details and background and is
intended more as a reference.
Introduction
Both settings and tasks produce values, but there are two major differences
between them:
291
1. Settings are evaluated at project load time. Tasks are executed on demand,
often in response to a command from the user.
2. At the beginning of project loading, settings and their dependencies are
fixed. Tasks can introduce new tasks during execution, however.
Features
1. By integrating with the settings system, tasks can be added, removed, and
modified as easily and flexibly as settings.
2. Input Tasks use parser combinators to define the syntax for their argu-
ments. This allows flexible syntax and tab-completions in the same way
as Commands.
3. Tasks produce values. Other tasks can access a task’s value by calling
value on it within a task definition.
4. Dynamically changing the structure of the task graph is possible. Tasks
can be injected into the execution graph based on the result of another
task.
5. There are ways to handle task failure, similar to try/catch/finally.
6. Each task has access to its own Logger that by default persists the logging
for that task at a more verbose level than is initially printed to the screen.
Defining a Task
Run “sbt hello” from command line to invoke the task. Run “sbt tasks” to see
this task listed.
Define the key To declare a new task, define a lazy val of type TaskKey:
292
The name of the val is used when referring to the task in Scala code and at
the command line. The string passed to the taskKey method is a description
of the task. The type parameter passed to taskKey (here, Int) is the type of
value produced by the task.
We’ll define a couple of other keys for the examples:
Implement the task There are three main parts to implementing a task once
its key is defined:
1. Determine the settings and other tasks needed by the task. They are the
task’s inputs.
2. Define the code that implements the task in terms of these inputs.
3. Determine the scope the task will go in.
These parts are then combined just like the parts of a setting are combined.
intTask := 1 + 2
stringTask := System.getProperty("user.name")
sampleTask := {
val sum = 1 + 2
println("sum: " + sum)
sum
}
293
Tasks with inputs Tasks with other tasks or settings as inputs are also
defined using :=. The values of the inputs are referenced by the value method.
This method is special syntax and can only be called when defining a task, such
as in the argument to :=. The following defines a task that adds one to the
value produced by intTask and returns the result.
sampleTask := intTask.value + 1
Task Scope As with settings, tasks can be defined in a specific scope. For
example, there are separate compile tasks for the compile and test scopes.
The scope of a task is defined the same as for a setting. In the following example,
test:sampleTask uses the result of compile:intTask.
1. Assignment methods have the lowest precedence. These are methods with
names ending in =, except for !=, <=, >=, and names that start with =.
2. Methods starting with a letter have the next highest precedence.
3. Methods with names that start with a symbol and aren’t included in
1. have the highest precedence. (This category is divided further accord-
ing to the specific character it starts with. See the Scala specification
for details.)
294
Separating implementations The implementation of a task can be sepa-
rated from the binding. For example, a basic separate definition looks like:
Note that whenever .value is used, it must be within a task definition, such as
within Def.task above or as an argument to :=.
// initial definition
intTask := 3
Completely override a task by not declaring the previous task as an input. Each
of the definitions in the following example completely overrides the previous one.
That is, when intTask is run, it will only print #3.
intTask := {
println("#1")
3
}
intTask := {
println("#2")
5
}
intTask := {
println("#3")
sampleTask.value - 3
}
295
Getting values from multiple scopes
Introduction The general form of an expression that gets values from multi-
ple scopes is:
<setting-or-task>.all(<scope-filter>).value
Example A common scenario is getting the sources for all subprojects for
processing all at once, such as passing them to scaladoc. The task that we want
to obtain values for is sources and we want to get the values in all non-root
projects and in the Compile configuration. This looks like:
296
Unspecified filters If the task filter is not specified, as in the example above,
the default is to select scopes without a specific task (global). Similarly, an
unspecified configuration filter will select scopes in the global configuration. The
project filter should usually be explicit, but if left unspecified, the current project
context will be used.
For example, the following selects the scope for the Compile and Test configu-
rations of the core project and the global configuration of the util project:
More operations The all method applies to both settings (values of type
Initialize[T]) and tasks (values of type Initialize[Task[T]]). It returns a
setting or task that provides a Seq[T], as shown in this table:
Target
Result
Initialize[T]
Initialize[Seq[T]]
297
Initialize[Task[T]]
Initialize[Task[Seq[T]]]
This means that the all method can be combined with methods that construct
tasks and settings.
Missing values Some scopes might not define a setting or task. The ? and
?? methods can help in this case. They are both defined on settings and tasks
and indicate what to do when a key is undefined.
?
On a setting or task with underlying type T, this accepts no arguments and
returns a setting or task (respectively) of type Option[T]. The result is None if
the setting/task is undefined and Some[T] with the value if it is.
??
On a setting or task with underlying type T, this accepts an argument of type
T and uses this argument if the setting/task is undefined.
The following contrived example sets the maximum errors to be the maximum
of all aggregates of the current project.
maxErrors := {
// select the transitive aggregates for this project, but not the project itself
val filter: ScopeFilter =
ScopeFilter( inAggregates(ThisProject, includeRoot=false) )
// get the configured maximum errors in each selected scope,
// using 0 if not defined in a scope
val allVersions: Seq[Int] =
(maxErrors ?? 0).all(filter).value
allVersions.max
}
Multiple values from multiple scopes The target of all is any task or
setting, including anonymous ones. This means it is possible to get multiple
values at once without defining a new task or setting in each scope. A common
use case is to pair each value obtained with the project, configuration, or full
scope it came from.
298
• configuration: Provides the Configuration for the context (undefined
for the global configuration)
For example, the following defines a task that prints non-Compile configurations
that define sbt plugins. This might be used to identify an incorrectly configured
build (or not, since this is a fairly contrived example):
checkPluginsTask := {
val oddPlugins: Seq[(String, Set[String])] =
pluginsWithConfig.all(filter).value
// Print each configuration that defines sbt plugins
for( (config, plugins) <- oddPlugins if plugins.nonEmpty )
println(s"$config defines sbt plugins: ${plugins.mkString(", ")}")
}
The examples in this section use the task keys defined in the previous section.
Streams: Per-task logging Per-task loggers are part of a more general sys-
tem for task-specific data called Streams. This allows controlling the verbosity
of stack traces and logging individually for tasks as well as recalling the last
logging for a task. Tasks also have access to their own persisted binary or text
data.
To use Streams, get the value of the streams task. This is a special task that
provides an instance of TaskStreams for the defining task. This type provides
access to named binary and text streams, named loggers, and a default logger.
The default Logger, which is the most commonly used aspect, is obtained by
the log method:
299
myTask := {
val s: TaskStreams = streams.value
s.log.debug("Saying hi...")
s.log.info("Hello!")
}
traceLevel in myTask := 5
To obtain the last logging output from a task, use the last command:
$ last myTask
[debug] Saying hi...
[info] Hello!
It can be useful to use the result of a task to determine the next tasks to evaluate.
This is done using Def.taskDyn. The result of taskDyn is called a dynamic task
because it introduces dependencies at runtime. The taskDyn method supports
the same syntax as Def.task and := except that you return a task instead of a
plain value.
For example,
300
Def.task {
intTask.value + 5
}
}
myTask := {
val num = dynamic.value
println(s"Number selected was $num")
}
Using Def.sequential
To call this task type in compilecheck from the shell. If the compilation fails,
compilecheck would stop the execution.
root> compilecheck
[info] Compiling 1 Scala source to /Users/x/proj/target/scala-2.10/classes...
[error] /Users/x/proj/src/main/scala/Foo.scala:3: Unmatched closing brace '}' ignored here
[error] }
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
301
Handling Failure
This section discusses the failure, result, and andFinally methods, which
are used to handle failure of other tasks.
failure The failure method creates a new task that returns the Incomplete
value when the original task fails to complete normally. If the original task
succeeds, the new task fails. Incomplete is an exception with information about
any tasks that caused the failure and any underlying exceptions thrown during
task execution.
For example:
intTask := sys.error("Failed.")
intTask := {
println("Ignoring failure: " + intTask.failure.value)
3
}
This overrides the intTask so that the original exception is printed and the
constant 3 is returned.
failure does not prevent other tasks that depend on the target from failing.
Consider the following example:
The following table lists the results of each task depending on the initially in-
voked task:
invoked task
intTask result
aTask result
bTask result
cTask result
302
overall result
intTask
failure
not run
not run
not run
failure
aTask
failure
success
not run
not run
success
bTask
failure
not run
failure
not run
failure
cTask
failure
success
failure
failure
failure
intTask
success
not run
not run
not run
success
303
aTask
success
failure
not run
not run
failure
bTask
success
not run
success
not run
success
cTask
success
failure
success
failure
failure
The overall result is always the same as the root task (the directly invoked task).
A failure turns a success into a failure, and a failure into an Incomplete. A
normal task definition fails when any of its inputs fail and computes its value
otherwise.
result The result method creates a new task that returns the full
Result[T] value for the original task. Result has the same structure as
Either[Incomplete, T] for a task result of type T. That is, it has two
subtypes:
Thus, the task created by result executes whether or not the original task
succeeds or fails.
For example:
304
intTask := sys.error("Failed.")
This overrides the original intTask definition so that if the original task fails,
the exception is printed and the constant 3 is returned. If it succeeds, the value
is printed and returned.
andFinally The andFinally method defines a new task that runs the original
task and evaluates a side effect regardless of whether the original task succeeded.
The result of the task is the result of the original task. For example:
intTask := intTaskImpl.value
This modifies the original intTask to always print “andFinally” even if the task
fails.
Note that andFinally constructs a new task. This means that the new task has
to be invoked in order for the extra block to run. This is important when calling
andFinally on another task instead of overriding a task like in the previous
example. For example, consider this code:
otherIntTask := intTaskImpl.value
305
def otherIntTask(): Int =
try { intTask() }
finally { println("finally") }
intTask()
It is obvious here that calling intTask() will never result in “finally” being
printed.
Input Tasks
Input Tasks parse user input and produce a task to run. Parsing Input describes
how to use the parser combinators that define the input syntax and tab comple-
tion. This page describes how to hook those parser combinators into the input
task system.
Input Keys
A key for an input task is of type InputKey and represents the input task like a
SettingKey represents a setting or a TaskKey represents a task. Define a new
input task key using the inputKey.apply factory method:
The definition of an input task is similar to that of a normal task, but it can
also use the result of a
Parser applied to 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.
306
import complete.DefaultParsers._
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
}
The Parser provided by the spaceDelimited method does not provide any flex-
ibility in defining the input syntax. Using a custom parser is just a matter of
defining your own Parser as described on the Parsing Input page.
Constructing the Parser The first step is to construct the actual Parser
by defining a value of one of the following types:
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 project’s Scala and sbt version set-
tings as well as the state. To use these settings, we need to wrap the Parser
construction in Def.setting and get the setting values with the special value
method:
import complete.DefaultParsers._
import complete.Parser
307
This Parser definition will produce a value of type (String,String). The input
syntax defined isn’t very flexible; it is just a demonstration. It will produce one
of the following values for a successful parse (assuming the current Scala version
is 2.12.7, the current sbt version is 1.2.7, and there are 3 commands left to run):
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.
Constructing the Task Next, we construct the actual task to execute from
the result of the Parser. For this, we define a task as usual, but we can access
the result of parsing via the special parsed method on Parser.
The following contrived example uses the previous example’s output (of type
(String,String)) and the result of the package task to print some information
to the screen.
demo := {
val (tpe, value) = parser.parsed
println("Type: " + tpe)
println("Value: " + value)
println("Packaged: " + packageBin.value.getAbsolutePath)
}
1. You can use other settings (via Initialize) to construct an input task.
2. You can use the current State to construct the parser.
3. The parser accepts user input and provides tab completion.
4. The parser produces the task to run.
So, you can use settings or State to construct the parser that defines an input
task’s command line syntax. This was described in the previous section. You
can then use settings, State, or user input to construct the task to run. This
is implicit in the input task syntax.
308
Using other input tasks
run2 := {
val one = (run in Compile).evaluated
val sep = separator.parsed
val two = (run in Compile).evaluated
}
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
309
Preapplying input
• 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
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:
• 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.
Note: if the input derives from settings you need to use, for example,
Def.taskDyn { ... }.value
// The argument string for the first run task is ' <name> <version>'
lazy val firstInput: Initialize[String] =
Def.setting(s" ${name.value} ${version.value}")
// Make the first arguments to the second run task ' red blue'
lazy val secondInput: String = " red blue"
run2 := {
val one = (run in Compile).fullInput(firstInput.value).evaluated
val two = (run in Compile).partialInput(secondInput).evaluated
}
For a main class Demo that echoes its arguments, this looks like:
310
$ sbt
> run2 green
[info] Running Demo demo 1.0
[info] Running Demo red blue green
demo
1.0
red
blue
green
The previous section showed how to derive a new InputTask by applying in-
put. 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`")
runFixed2 := {
311
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!
Commands
What is a “command”?
Introduction
312
3. Help provided to the user
In sbt, the syntax part, including tab completion, is specified with parser com-
binators. If you are familiar with the parser combinators in Scala’s standard
library, these are very similar. The action part is a function (State, T) =>
State, where T is the data structure produced by the parser. See the Parsing
Input page for how to use the parser combinators.
State provides access to the build state, such as all registered Commands, the
remaining commands to execute, and all project-related information. See States
and Actions for details on State.
Finally, basic help information may be provided that is used by the help com-
mand to display command help.
Defining a Command
313
Multi-argument command There is a convenience method for constructing
commands that accept multiple arguments separated by spaces.
Full Example
Here’s build.sbt:
import CommandExample._
Here’s project/CommandExample.scala:
import sbt._
import Keys._
object CommandExample {
// A simple, no-argument command that prints "Hi",
// leaving the current state unchanged.
314
def hello = Command.command("hello") { state =>
println("Hi!")
state
}
315
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
}
import sbt._
import complete.DefaultParsers._
Basic parsers
// A parser that succeeds if the input is 'x', returning the Char 'x'
// and failing otherwise
val singleChar: Parser[Char] = 'x'
// A parser that succeeds if the input is "blue", returning the String "blue"
// and failing otherwise
val litString: Parser[String] = "blue"
316
In these examples, implicit conversions produce a literal Parser from a Char
or String. Other basic parser constructors are the charClass, success and
failure methods:
// A parser that succeeds if the character is a digit, returning the matched Char
// The second argument, "digit", describes the parser and is used in error messages
val digit: Parser[Char] = charClass( (c: Char) => c.isDigit, "digit")
// A parser that produces the value 3 for an empty input string, fails otherwise
val alwaysSucceed: Parser[Int] = success( 3 )
Built-in parsers
Combining parsers
// A parser that matches "fg" or "bg", a space, and then the color, returning the matched va
317
// ~ is an alias for Tuple2.
val setColor: Parser[String ~ Char ~ String] =
select ~ ' ' ~ color
// Often, we don't care about the value matched by a parser, such as the space above
// For this, we can use ~> or <~, which keep the result of
// the parser on the right or left, respectively
val setColor2: Parser[String ~ String] = select ~ (' ' ~> color)
Transforming results
A key aspect of parser combinators is transforming results along the way into
more useful data structures. The fundamental methods for this are map and
flatMap. Here are examples of map and some convenience methods implemented
on top of map.
// Apply the `digits` parser and apply the provided function to the matched
// character sequence
val num: Parser[Int] = digits map { (chars: Seq[Char]) => chars.mkString.toInt }
// Match a digit character, returning the matched character or return '0' if the input is no
val digitWithDefault: Parser[Char] = charClass(_.isDigit, "digit") ?? '0'
318
Controlling tab completion
Most parsers have reasonable default tab completion behavior. For example,
the string and character literal parsers will suggest the underlying literal for
an empty input string. However, it is impractical to determine the valid com-
pletions for charClass, since it accepts an arbitrary predicate. The examples
method defines explicit completions for such a parser:
Tab completion will use the examples as suggestions. The other method con-
trolling tab completion is token. The main purpose of token is to determine
the boundaries for suggestions. For example, if your parser is:
then the potential completions on empty input are: console fg green fg blue
bg green bg blue
Typically, you want to suggest smaller segments or the number of suggestions
becomes unmanageable. A better parser is:
Dependent parsers
Sometimes a parser must analyze some data and then more data needs to be
parsed, and it is dependent on the previous one.
The key for obtaining this behaviour is to use the flatMap function.
As an example, it will shown how to select several items from a list of valid ones
with completion, but no duplicates are possible. A space is used to separate the
different items.
319
select1(items).flatMap { v �
val remaining = items filter { _ != v }
if (remaining.size == 0)
success(v :: Nil)
else
selectSome(remaining).?.map(v +: _.getOrElse(Seq()))
}
As you can see, the flatMap function provides the previous value. With this
info, a new parser is constructed for the remaining items. The map combinator
is also used in order to transform the output of the parser.
The parser is called recursively, until it is found the trivial case of no possible
choices.
State is the entry point to all available information in sbt. The key methods
are:
The action part of a command performs work and transforms State. The follow-
ing sections discuss State => State transformations. As mentioned previously,
a command will typically handle a parsed value as well: (State, T) => State.
Command-related data
320
This takes the current commands, appends new commands, and drops dupli-
cates. Alternatively, State has a convenience method for doing the above:
The first adds a command that will run after all currently specified commands
run. The second inserts a command that will run next. The remaining com-
mands will run after the inserted command completes.
To indicate that a command has failed and execution should not continue, return
state.fail.
Project-related data
Extracted provides:
321
Project data
Here, a SettingKey[T] is typically obtained from Keys and is the same type
that is used to define settings in .sbt files, for example. Scope selects the scope
the key is obtained for. There are convenience overloads of in that can be used
to specify only the required scope axes. See Structure.scala for where in and
other parts of the settings interface are defined. Some examples:
import Keys._
val extracted: Extracted
import extracted._
// get the package options for the `test:packageSrc` task or Nil if none are defined
val pkgOpts: Seq[PackageOption] = packageOptions in (currentRef, Test, packageSrc) get struc
A URI identifies a build and root identifies the initial build loaded. Load-
edBuildUnit provides information about a single build. The key members of
LoadedBuildUnit are:
322
Classpaths
Running tasks
It can be useful to run a specific project task from a command (not from another
task) and get its result. For example, an IDE-related command might want to get
the classpath from a project or a task might analyze the results of a compilation.
The relevant method is Project.runTask, which has the following signature:
For example,
// This selects the main 'compile' task for the current project.
// The value produced by 'compile' is of type inc.Analysis,
// which contains information about the compiled code.
val taskKey = Keys.compile in Compile
323
For getting the test classpath of a specific project, use this key:
To access the current State from a task, use the state task as an input. For
example,
Tasks/Settings: Motivation
This page motivates the task and settings system. You should already know
how to use tasks and settings, which are described in the getting started guide
and on the Tasks page.
An important aspect of the task system is to combine two common, related
steps in a build:
1. Dependency declarations
2. Some form of shared state
324
makeFoo()
doSomething(foo)
This example is rather exaggerated in its badness, but I claim it is nearly the
same situation as our two step task definitions. Particular reasons this is bad
include:
The first point is like declaring a task dependency, the second is like two tasks
modifying the same state (either project variables or files), and the third is a
consequence of unsynchronized, shared state.
In Scala, we have the built-in functionality to easily fix this: lazy val.
doSomething(foo)
Here, lazy val gives us thread safety, guaranteed initialization before access,
and immutability all in one, DRY construct. The task system in sbt does the
same thing for tasks (and more, but we won’t go into that here) that lazy val
did for our bad example.
A task definition must declare its inputs and the type of its output. sbt will
ensure that the input tasks have run and will then provide their results to the
function that implements the task, which will generate its own result. Other
tasks can use this result and be assured that the task has run (once) and be
thread-safe and typesafe in the process.
The general form of a task definition looks like:
myTask := {
val a: A = aTask.value
val b: B = bTask.value
... do something with a, b and generate a result ...
}
(This is only intended to be a discussion of the ideas behind tasks, so see the sbt
Tasks page for details on usage.) Here, aTask is assumed to produce a result of
type A and bTask is assumed to produce a result of type B.
325
Application
As an example, consider generating a zip file containing the binary jar, source jar,
and documentation jar for your project. First, determine what tasks produce the
jars. In this case, the input tasks are packageBin, packageSrc, and packageDoc
in the main Compile scope. The result of each of these tasks is the File for the
jar that they generated. Our zip file task is defined by mapping these package
tasks and including their outputs in a zip file. As good practice, we then return
the File for this zip so that other tasks can map on the zip task.
zip := {
val bin: File = (packageBin in Compile).value
val src: File = (packageSrc in Compile).value
val doc: File = (packageDoc in Compile).value
val out: File = zipPath.value
val inputs: Seq[(File,String)] = Seq(bin, src, doc) x Path.flat
IO.zip(inputs, out)
out
}
The val inputs line defines how the input files are mapped to paths in the
zip. See Mapping Files for details. The explicit types are not required, but are
included for clarity.
The zipPath input would be a custom task to define the location of the zip file.
For example:
Anything that is necessary for building the project should go in project/. This
includes things like the web plugin. ~/.sbt/ should contain local customizations
and commands for working with a build, but are not necessary. An example is
an IDE plugin.
326
Local settings
There are two options for settings that are specific to a user. An example of
such a setting is inserting the local Maven repository at the beginning of the
resolvers list:
resolvers := {
val localMaven = "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/re
localMaven +: resolvers.value
}
.sbtrc
Put commands to be executed when sbt starts up in a .sbtrc file, one per
line. These commands run before a project is loaded and are useful for defining
aliases, for example. sbt executes commands in $HOME/.sbtrc (if it exists) and
then <project>/.sbtrc (if it exists).
Generated files
Don’t hard code constants, like the output directory target/. This is especially
important for plugins. A user might change the target setting to point to
build/, for example, and the plugin needs to respect that. Instead, use the
setting, like:
327
Don’t “mutate” files
This arrangement is not always possible, but it should be the rule and not the
exception.
file("/home/user/A.scala")
base / "A.scala"
328
This is related to the no hard coding best practice because the proper way
involves referencing the baseDirectory setting. For example, the following
defines the myPath setting to be the <base>/licenses/ directory.
In Java (and thus in Scala), a relative File is relative to the current working di-
rectory. The working directory is not always the same as the build root directory
for a number of reasons.
The only exception to this rule is when specifying the base directory for a Project.
Here, sbt will resolve a relative File against the build root directory for you for
convenience.
Parser combinators
Plugins
There’s a getting started page focused on using existing plugins, which you may
want to read first.
A plugin is a way to use external code in a build definition. A plugin can
be a library used to implement a task (you might use Knockoff to write a
markdown processing task). A plugin can define a sequence of sbt settings that
are automatically added to all projects or that are explicitly declared for selected
projects. For example, a plugin might add a proguard task and associated
329
(overridable) settings. Finally, a plugin can define new commands (via the
commands setting).
sbt 0.13.5 introduces auto plugins, with improved dependency management
among the plugins and explicitly scoped auto importing. Going forward, our
recommendation is to migrate to the auto plugins. The Plugins Best Practices
page describes the currently evolving guidelines to writing sbt plugins. See also
the general best practices.
Alternatively, you can create project/plugins.sbt with all of the desired sbt
plugins, any general dependencies, and any necessary repositories:
// plain library (not an sbt plugin) for use in the build definition
libraryDependencies += "org.example" % "utilities" % "1.3"
Many of the auto plugins automatically add settings into projects, however,
some may require explicit enablement. Here’s an example:
See using plugins in the Getting Started guide for more details on using plugins.
330
By Description
• Automatically import selective names to .sbt files and the eval and set
commands.
• Specify plugin dependencies to other auto plugins.
• Automatically activate itself when all dependencies are present.
• Specify projectSettings, buildSettings, and globalSettings as ap-
propriate.
Plugin dependencies
331
1. add the setting sequence from the dependency as part of its own setting
sequence, or
2. tell the build users to include them in the right order.
This will pull in the right setting sequence from the plugins in the right order.
The key notion here is you declare the plugins you want, and sbt can fill in the
gap.
A plugin implementation is not required to produce an auto plugin, however. It
is a convenience for plugin consumers and because of the automatic nature, it
is not always appropriate.
A minimal sbt plugin is a Scala library that is built against the version of
Scala that sbt runs (currently, 2.12.7) or a Java library. Nothing special needs
to be done for this type of library. A more typical plugin will provide sbt
tasks, commands, or settings. This kind of plugin may provide these settings
automatically or make them available for the user to explicitly integrate.
To make an auto plugin, create a project and enable SbtPlugin.
332
Then, write the plugin code and publish your project to a repository. The plugin
can be used as described in the previous section.
First, in an appropriate namespace, define your auto plugin object by extending
sbt.AutoPlugin.
package sbthello
import sbt._
import Keys._
333
Implementing plugin dependencies Next step is to define the plugin de-
pendencies.
package sbtless
import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
override def requires = SbtJsTaskPlugin
override lazy val projectSettings = ...
}
The requires method returns a value of type Plugins, which is a DSL for
constructing the dependency list. The requires method typically contains one
of the following values:
Root plugins and triggered plugins Some plugins should always be ex-
plicitly enabled on projects. we call these root plugins, i.e. plugins that are
“root” nodes in the plugin dependency graph. An auto plugin is by default a
root plugin.
Auto plugins also provide a way for plugins to automatically attach themselves
to projects if their dependencies are met. We call these triggered plugins, and
they are created by overriding the trigger method.
For example, we might want to create a triggered plugin that can append com-
mands automatically to the build. To do this, set the requires method to
return empty, and override the trigger method with allRequirements.
package sbthello
import sbt._
import Keys._
334
}
}
The build user still needs to include this plugin in project/plugins.sbt, but
it is no longer needed to be included in build.sbt. This becomes more in-
teresting when you do specify a plugin with requirements. Let’s modify the
SbtLessPlugin so that it depends on another plugin:
package sbtless
import sbt._
import Keys._
object SbtLessPlugin extends AutoPlugin {
override def trigger = allRequirements
override def requires = SbtJsTaskPlugin
override lazy val projectSettings = ...
}
As it turns out, PlayScala plugin (in case you didn’t know, the Play framework
is an sbt plugin) lists SbtJsTaskPlugin as one of it required plugins. So, if we
define a build.sbt with:
package sbthello
import sbt._
import Keys._
335
object autoImport {
val greeting = settingKey[String]("greeting")
val hello = taskKey[Unit]("say hello")
}
import autoImport._
override def trigger = allRequirements
override lazy val buildSettings = Seq(
greeting := "Hi!",
hello := helloTask.value)
lazy val helloTask =
Def.task {
println(greeting.value)
}
}
sbtPlugin := true
name := "sbt-obfuscate"
organization := "org.example"
ObfuscatePlugin.scala:
package sbtobfuscate
import sbt._
import sbt.Keys._
336
Obfuscate(sources.value, (obfuscateLiterals in obfuscate).value)
},
obfuscateLiterals in obfuscate := false
)
}
import autoImport._
override def requires = sbt.plugins.JvmPlugin
object Obfuscate {
def apply(sources: Seq[File], obfuscateLiterals: Boolean): Seq[File] = {
// TODO obfuscate stuff!
sources
}
}
Usage example A build definition that uses the plugin might look like.
obfuscate.sbt:
This plugin will be available for every sbt project for the current user.
In addition:
337
• A Plugin may be directly defined in Scala source files in ~/.sbt/1.0/plugins/,
such as ~/.sbt/1.0/plugins/MyPlugin.scala. ~/.sbt/1.0/plugins//build.sbt
should contain sbtPlugin := true. This can be used for quicker
turnaround when developing a plugin initially:
$ sbt
> reload plugins
[info] Set current project to default (in build file:/Users/sbt/demo2/project/)
>
338
Then, we can add dependencies like usual and save them to project/plugins.sbt.
It is useful, but not required, to run update to verify that the dependencies are
correct.
1d) Project dependency This variant shows how to use sbt’s external
project support to declare a source dependency on a plugin. This means that
the plugin will be built from source and used on the classpath.
Edit project/plugins.sbt
One caveat to using this method is that the local sbt will try to run the remote
plugin’s build. It is quite possible that the plugin’s own build uses a different
sbt version, as many plugins cross-publish for several sbt versions. As such, it
is recommended to stick with binary artifacts when possible.
339
2) Use the library Grizzled Scala is ready to be used in build definitions.
This includes the eval and set commands and .sbt and project/*.scala
files.
In a build.sbt file:
import grizzled.sys._
import OperatingSystem._
libraryDependencies ++=
if(os == Windows)
Seq("org.example" % "windows-only" % "1.0")
else
Seq.empty
Best Practices
If you’re a plugin writer, please consult the Plugins Best Practices page; it
contains a set of guidelines to help you ensure that your plugin is consistent and
plays well with other plugins.
This page is intended primarily for sbt plugin authors. This page assumes you’ve
read using plugins and Plugins.
A plugin developer should strive for consistency and ease of use. Specifically:
• Plugins should play well with other plugins. Avoiding namespace clashes
(in both sbt and Scala) is paramount.
• Plugins should follow consistent conventions. The experiences of an sbt
user should be consistent, no matter what plugins are pulled in.
340
Key naming convention: Use prefix
Sometimes, you need a new key, because there is no existing sbt key. In this
case, use a plugin-specific prefix.
package sbtassembly
import autoImport._
....
}
In this approach, every val starts with assembly. A user of the plugin would
refer to the settings like this in build.sbt:
Inside sbt shell, the user can refer to the setting in the same way:
Avoid sbt 0.12 style key names where the key’s Scala identifier and shell uses
kebab-casing:
Because there’s a single namespace for keys both in build.sbt and in sbt
shell, if different plugins use generic sounding key names like jarName and
excludedFiles they will cause name conflict.
341
Artifact naming convention
Use the sbt-$projectname scheme to name your library and artifact. A plugin
ecosystem with a consistent naming convention makes it easier for users to tell
whether a project or dependency is an SBT plugin.
If the project’s name is foobar the following holds:
• BAD: foobar
• BAD: foobar-sbt
• BAD: sbt-foobar-plugin
• GOOD: sbt-foobar
Users who have their build files in some package will not be able to use your
plugin if it’s defined in default (no-name) package.
Make sure people can find your plugin. Here are some of the recommended
steps:
sbt has a number of predefined keys. Where possible, reuse them in your plugin.
For instance, don’t define:
342
Use settings and tasks. Avoid commands.
Your plugin should fit in naturally with the rest of the sbt ecosystem. The first
thing you can do is to avoid defining commands, and use settings and tasks
and task-scoping instead (see below for more on task-scoping). Most of the
interesting things in sbt like compile, test and publish are provided using
tasks. Tasks can take advantage of duplication reduction and parallel execution
by the task engine. With features like ScopeFilter, many of the features that
previously required commands are now possible using tasks.
Settings can be composed from other settings and tasks. Tasks can be composed
from other tasks and input tasks. Commands, on the other hand, cannot be
composed from any of the above. In general, use the minimal thing that you
need. One legitimate use of commands may be using plugin to access the build
definition itself not the code. sbt-inspectr was implemented using a command
before it became inspect tree.
Configuration advices
If your plugin introduces either a new set of source code or its own library
dependencies, only then you want your own configuration.
package sbtwhatever
343
object autoImport {
// BAD sample
lazy val Whatever = config("whatever") extend(Compile)
lazy val specificKey = settingKey[String]("A plugin specific key")
}
import autoImport._
override lazy val projectSettings = Seq(
specificKey in Whatever := "another opinion" // DON'T DO THIS
)
}
package sbtfuzz
object autoImport {
lazy val Fuzz = config("fuzz") extend(Compile)
}
import autoImport._
344
)
override lazy val projectSettings = inConfig(Fuzz)(baseFuzzSettings)
}
Provide raw settings and configured settings Split your settings by the
configuration axis like so:
package sbtobfuscate
object autoImport {
lazy val obfuscate = taskKey[Seq[File]]("obfuscate the source")
lazy val obfuscateStylesheet = settingKey[File]("obfuscate stylesheet")
}
import autoImport._
lazy val baseObfuscateSettings: Seq[Def.Setting[_]] = Seq(
obfuscate := Obfuscate((sources in obfuscate).value),
sources in obfuscate := sources.value
)
override lazy val projectSettings = inConfig(Compile)(baseObfuscateSettings)
}
345
sources
}
}
import sbtobfuscate.ObfuscatePlugin
Scoping advices
In general, if a plugin provides keys (settings and tasks) with the widest scoping,
and refer to them with the narrowest scoping, it will give the maximum flexibility
to the build users.
package sbtobfuscate
object autoImport {
lazy val obfuscate = taskKey[Seq[File]]("obfuscate the source")
lazy val obfuscateOption = settingKey[ObfuscateOption]("options to configure obfuscate")
}
import autoImport._
override lazy val globalSettings = Seq(
obfuscateOption := ObfuscateOption()
346
)
Giving keys default values in global scope requires knowing that every key (if
any) used to define that key must also be defined in global scope, otherwise it
will fail at load time.
Using a “main” task scope for settings Sometimes you want to define
some settings for a particular “main” task in your plugin. In this instance, you
can scope your settings using the task itself. See the baseObfuscateSettings:
In the above example, sources in obfuscate is scoped under the main task,
obfuscate.
347
Rewiring existing keys in globalSettings There may be times when you
need to rewire an existing key in globalSettings. The general rule is be careful
what you touch.
Care should be taken to ensure previous settings from other plugins are not
ignored. e.g. when creating a new onLoad handler, ensure that the previous
onLoad handler is not removed.
package sbtsomething
Set project/build.properties
Continuous integration is a great way of checking that your code works out-
side of your machine. If you haven’t created one already, make sure to create
project/build.properties and explicitly set the sbt.version number:
sbt.version=1.2.7
A treasure trove of Travis tricks can be found in the Travis’s official documen-
tation. Use this guide as an inspiration, but consult the official source for more
details.
348
Basic setup
language: scala
jdk: oraclejdk8
scala:
- 2.10.4
- 2.12.7
language: scala
jdk: oraclejdk8
scala:
- 2.10.4
- 2.12.7
script:
- sbt ++$TRAVIS_SCALA_VERSION test
For sbt plugins, there is no need for cross building on Scala, so the following is
all you need:
language: scala
jdk: oraclejdk8
script:
- sbt scripted
349
Another source of good information is to read the output by Travis CI itself
to learn about how the virtual environment is set up. For example, from the
following output we learn that it is using JVM_OPTS environment variable to pass
in the JVM options.
$ export JVM_OPTS=@/etc/sbt/jvmopts
$ export SBT_OPTS=@/etc/sbt/sbtopts
The default sbt and JVM options are set by Travis CI people, and it should work
for most cases. If you do decide to customize it, read what they currently use
as the defaults first. Because Travis is already using the environment variable
JVM_OPTS, we can instead create a file travis/jvmopts:
-Dfile.encoding=UTF8
-Xms2048M
-Xmx2048M
-Xss6M
-XX:ReservedCodeCacheSize=256M
and then write out the script section with -jvm-opts option:
script:
- sbt ++$TRAVIS_SCALA_VERSION -jvm-opts travis/jvmopts test
After making the change, confirm on the Travis log to see if the flags are taking
effect:
350
script:
- sbt ++$TRAVIS_SCALA_VERSION -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M -J-Xms10
Note: This duplicates the -Xms flag as intended, which might not the best thing
to do.
Caching
1. start up faster
2. allow the use of caches for public repositories
3. disallow the use of sudo, setuid and setgid executables
351
# These directories are cached to S3 at the end of the build
cache:
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt
before_cache:
# Cleanup the cached directories to avoid unnecessary cache updates
- rm -fv $HOME/.ivy2/.sbt.ivy.lock
- find $HOME/.ivy2/cache -name "ivydata-*.properties" -print -delete
- find $HOME/.sbt -name "*.lock" -print -delete
With the above changes combined Travis CI will tar up the cached directories
and uploads them to Amazon S3. Overall, the use of the new infrastructure and
caching seems to shave off a few minutes of build time per job.
Note: The Travis documentation states caching features are still experimental.
Build matrix
language: scala
jdk: oraclejdk8
scala:
- 2.10.4
- 2.12.7
script:
- sbt ++$TRAVIS_SCALA_VERSION test
env:
global:
- SOME_VAR="1"
352
- TEST_COMMAND="scripted merging/* caching/*"
script:
- sbt "$TEST_COMMAND"
Now two jobs will be created to build this sbt plugin, simultaneously running
different integration tests. This technique is described in Parallelizing your
builds across virtual machines.
Notification
The default behavior looks reasonable, but if you want, we can override the
notifications section to email you on successful builds too, or to use some
other channel of communication like IRC.
This might also be a good time to read up on encryption using the command
line travis tool.
For builds that are more prone to flaky network or tests, Travis CI has created
some tricks described in the page My builds is timing out.
Starting your command with travis_retry retries the command three times if
the return code is non-zero. With caching, hopefully the effect of flaky network
is reduced, but it’s an interesting one nonetheless. Here are some cautionary
words from the documentation:
353
We recommend careful use of travis_retry, as overusing it can
extend your build time when there could be a deeper underlying
issue.
Our builds have a global timeout and a timeout that’s based on the
output. If no output is received from a build for 10 minutes, it’s
assumed to have stalled for unknown reasons and is subsequently
killed.
More things
There are more thing you can do, such as set up databases, installing Ubuntu
packages, and deploy continuously.
Travis offers the ability to run tests in parallel, and also imposes time limits on
builds. If you have an especially long-running suite of scripted tests for your
plugin, you can run a subset of scripted tests in a directory, for example:
- TEST_COMMAND="scripted tests/*1of3"
- TEST_COMMAND="scripted tests/*2of3"
- TEST_COMMAND="scripted tests/*3of3"
Will create three chunks and run each of the chunks separately for the directory
tests.
Sample setting
Here’s a sample that puts them all together. Remember, most of the sections
are optional.
language: scala
jdk: oraclejdk8
354
directories:
- $HOME/.ivy2/cache
- $HOME/.sbt/boot/
jdk: oraclejdk8
env:
# This splits the build into two parts
matrix:
- TEST_COMMAND="scripted sbt-assembly/*"
- TEST_COMMAND="scripted merging/* caching/*"
script:
- sbt -Dfile.encoding=UTF8 -J-XX:ReservedCodeCacheSize=256M "$TEST_COMMAND"
before_cache:
# Tricks to avoid unnecessary cache updates
- find $HOME/.sbt -name "*.lock" | xargs rm
- find $HOME/.ivy2 -name "ivydata-*.properties" | xargs rm
- rm -f $HOME/.ivy2/.sbt.ivy.lock
Let’s talk about testing. Once you write a plugin, it turns into a long-term
thing. To keep adding new features (or to keep fixing bugs), writing tests makes
sense.
sbt comes with scripted test framework, which lets you script a build scenario.
It was written to test sbt itself on complex scenarios – such as change detection
and partial compilation:
355
The scripted test framework is used to verify that sbt handles cases
such as that described above.
The framework is made available via scripted-plugin. The rest of this page
explains how to include the scripted-plugin into your plugin.
step 1: snapshot
Before you start, set your version to a -SNAPSHOT one because scripted-
plugin will publish your plugin locally. If you don’t use SNAPSHOT, you could
get into a horrible inconsistent state of you and the rest of the world seeing
different artifacts.
step 2: SbtPlugin
Note: You must use sbt 1.2.1 and above to use SbtPlugin.
step 3: src/sbt-test
356
lazy val root = (project in file("."))
.settings(
version := "0.1",
scalaVersion := "2.10.6",
assemblyJarName in assembly := "foo.jar"
)
In project/plugins.sbt:
sys.props.get("plugin.version") match {
case Some(x) => addSbtPlugin("com.eed3si9n" % "sbt-assembly" % x)
case _ => sys.error("""|The system property 'plugin.version' is not defined.
Specify this property using the scriptedLaunchOpts -D.""".stripMargin)
}
Now, write a script to describe your scenario in a file called test located at the
root dir of your test project.
357
• touch path+ creates or updates the timestamp on the files
• delete path+ deletes the files
• exists path+ checks if the files exist
• mkdir path+ creates dirs
• absent path+ checks if the files don’t exist
• newer source target checks if source is newer
• must-mirror source target checks if source is identical
• pause pauses until enter is pressed
• sleep time sleeps (in milliseconds)
• exec command args* runs the command in another process
• copy-file fromPath toPath copies the file
• copy fromPath+ toDir copies the paths to toDir preserving relative struc-
ture
• copy-flat fromPath+ toDir copies the paths to toDir flat
So my script will run assembly task, and checks if foo.jar gets created. We’ll
cover more complex tests later.
> scripted
This will copy your test build into a temporary dir, and executes the test script.
If everything works out, you’d see publishLocal running, then:
The file commands are great, but not nearly enough because none of them test
the actual contents. An easy way to test the contents is to implement a custom
task in your test build.
For my hello project, I’d like to check if the resulting jar prints out “hello”. I
can take advantage of scala.sys.process.Process to run the jar. To express
a failure, just throw an error. Here’s build.sbt:
import scala.sys.process.Process
358
lazy val root = (project in file("."))
.settings(
version := "0.1",
scalaVersion := "2.10.6",
assemblyJarName in assembly := "foo.jar",
TaskKey[Unit]("check") := {
val process = Process("java", Seq("-jar", (crossTarget.value / "foo.jar").toString))
val out = (process!!)
if (out.trim != "bye") sys.error("unexpected output: " + out)
()
}
)
Until you get the hang of it, it might take a while for the test itself to behave
correctly. There are several techniques that may come in handy.
First place to start is turning off the log buffering.
This for example should print out the location of the temporary dir:
359
[info] [info] Set current project to default-c6500b (in build file:/private/var/folders/Ab/AbC1
...
Add the following line to your test script to suspend the test until you hit the
enter key:
$ pause
There are literally 100+ scripted tests under sbt project itself. Browse around
to get inspirations.
For example, here’s the one called by-name.
> compile
# Both A.scala and B.scala need to be recompiled because the type has changed
-> compile
First, you need sbt’s launcher version 0.13.13 or above. Normally the exact
version for the sbt launcher does not matter because it will use the version
specified by sbt.version in project/build.properties; however for new
sbt’s launcher 0.13.13 or above is required as the command functions without a
project/build.properties present.
Next, run:
360
$ sbt new scala/scala-seed.g8
....
name [hello]:
This ran the template scala/scala-seed.g8 using Giter8, prompted for values
for “name” (which has a default value of “hello”, which we accepted hitting
[Enter]), and created a build under ./hello.
scala-seed is the official template for a “minimal” Scala project, but it’s defi-
nitely not the only one out there.
Giter8 support
For more, see Giter8 templates on the Giter8 wiki. sbt provides out-of-the-box
support for Giter8 templates by shipping with a template resolver for Giter8.
361
Giter8 parameters You can append Giter8 parameters to the end of the
command, so for example to specify a particular branch you can use:
How to create a Giter8 template See Making your own templates for the
details on how to create a new Giter8 template.
License
-------
Written in <YEAR> by <AUTHOR NAME> <AUTHOR E-MAIL ADDRESS>
[other author/contributor lines as appropriate]
To the extent possible under law, the author(s) have dedicated all copyright and related and neig
You should have received a copy of the CC0 Public Domain Dedication along with this software. If n
The rest of this page explains how to extend the sbt new command to provide
support for something other than Giter8 templates. You can skip this section
if you’re not interested in extending new.
362
To create your own template resolver, create a library that has template-resolver
as a dependency:
package sbt.template;
import Def.Setting
import Keys._
/** An experimental plugin that adds the ability for Giter8 templates to be resolved
*/
object Giter8TemplatePlugin extends AutoPlugin {
override def requires = CorePlugin
override def trigger = allRequirements
363
Cross building plugins
Like we are able to cross build against multiple Scala versions, we can cross
build sbt 1.0 plugins while staying on sbt 0.13. This is useful because we can
port one plugin at a time.
1. If the plugin depends on libraries, make sure there are Scala 2.12 artifacts
for them.
2. Use the latest sbt 0.13.16.
3. Append the following settings to your plugin project (and any other sub-
projects that it depends):
.settings(
scalaVersion := "2.12.7",
sbtVersion in Global := "1.2.7",
scalaCompilerBridgeSource := {
val sv = appConfiguration.value.provider.id.version
("org.scala-sbt" % "compiler-interface" % sv % "component").sources
}
)
How to…
See Detailed Table of Contents for the list of all the how-tos.
Classpaths
The classpathTypes setting controls the types of managed artifacts that are
included on the classpath by default. To add a new type, such as mar,
classpathTypes += "mar"
See the default types included by running show classpathTypes at the sbt
prompt.
364
The dependencyClasspath task scoped to Compile provides the classpath to use
for compilation. Its type is Seq[Attributed[File]], which means that each
entry carries additional metadata. The files method provides just the raw
Seq[File] for the classpath. For example, to use the files for the compilation
classpath in another task, :
example := {
val cp: Seq[File] = (dependencyClasspath in Compile).value.files
...
}
Note: This classpath does not include the class directory, which
may be necessary for compilation in some situations.
example := {
val cp: Seq[File] = (fullClasspath in Runtime).value.files
...
}
Get the test classpath, including the project’s compiled test classes
example := {
val cp: Seq[File] = (fullClasspath in Test).value.files
365
...
}
exportJars := true
This will use the result of packageBin on the classpath instead of the class
directory.
The result of the update task has type UpdateReport, which contains the results
of dependency resolution. This can be used to extract the files for specific types
of artifacts in a specific configuration. For example, to get the jars and zips of
dependencies in the Compile configuration, :
example := {
val artifactTypes = Set("jar", "zip")
val files =
Classpaths.managedJars(Compile, artifactTypes, update.value)
...
}
A classpath has type Seq[Attributed[File]], which means that each entry car-
ries additional metadata. The files method provides just the raw Seq[File]
for the classpath. For example, :
366
Get the module and artifact that produced a classpath entry
A classpath has type Seq[Attributed[File]], which means that each entry car-
ries additional metadata. This metadata is in the form of an AttributeMap. Use-
ful keys for entries in the map are artifact.key, moduleID.key, and analysis.
For example,
Note: Entries may not have some or all metadata. Only entries from
source dependencies, such as internal projects, have an incremental
compilation Analysis. Only entries for managed dependencies have
an Artifact and ModuleID.
Customizing paths
This page describes how to modify the default source, resource, and library
directories and what files get included from them.
The directory that contains the main Scala sources is by default src/main/scala.
For test Scala sources, it is src/test/scala. To change this, modify
scalaSource in the Compile (for main sources) or Test (for test sources). For
example,
Note: The Scala source directory can be the same as the Java source
directory.
367
Change the default Java source directory
The directory that contains the main Java sources is by default src/main/java.
For test Java sources, it is src/test/java. To change this, modify javaSource
in the Compile (for main sources) or Test (for test sources).
For example,
Note: The Scala source directory can be the same as the Java source
directory.
When set for Compile, Runtime, or Test, unmanagedBase is the directory con-
taining libraries for that configuration, overriding the default. For example,
the following declares lib/main/ to contain jars only for Compile and not for
running or testing:
368
Disable using the project’s base directory as a source directory
By default, sbt includes .scala files from the project’s base directory as main
source files. To disable this, configure sourcesInBase:
sourcesInBase := false
369
excludeFilter in unmanagedSources := HiddenFileFilter || "*impl*"
To have different filters for main and test libraries, configure Compile and Test
separately:
To have different filters for main and test libraries, configure Compile and Test
separately:
Note: By default, sbt includes all files that are not hidden.
370
To have different filters for main and test libraries, configure Compile and Test
separately:
Note: By default, sbt includes jars, zips, and native dynamic li-
braries, excluding hidden files.
Generating files
sbt provides standard hooks for adding source and resource generation tasks.
Generate sources
371
sourceGenerators in Compile += Def.task {
val file = (sourceManaged in Compile).value / "demo" / "Test.scala"
IO.write(file, """object Test extends App { println("Hi") }""")
Seq(file)
}.taskValue
> run
[info] Running Test
Hi
Generate resources
372
Executing run (or package, not compile) will add a file demo to resourceManaged,
which is target/scala-*/resource_managed". By default, generated re-
sources are not included in the packaged source artifact. To do so, add them
as you would other mappings. See Adding files to a package.
As a specific example, the following generates a properties file myapp.properties
containing the application name and version:
The help command is used to show available commands and search the help for
commands, tasks, or settings. If run without arguments, help lists the available
commands.
> help
373
List available tasks
The tasks command, without arguments, lists the most commonly used tasks.
It can take a regular expression to search task names and descriptions. The
verbosity can be increased to show or search less commonly used tasks. See
help tasks for details.
The settings command, without arguments, lists the most commonly used set-
tings. It can take a regular expression to search setting names and descriptions.
The verbosity can be increased to show or search less commonly used settings.
See help settings for details.
374
[info] | +-cleanKeepFiles = Vector(<project>/target/.history)
[info] | | +-history = Some(<project>/target/.history)
[info] | | +-target = target
[info] | | +-baseDirectory =
...
For each task, inspect tree show the type of the value generated by the task.
For a setting, the toString of the setting is displayed. See the Inspecting
Settings page for details on the inspect command.
While the help, settings, and tasks commands display a description of a task,
the inspect command also shows the type of a setting or task and the value of
a setting. For example:
375
See the Inspecting Settings page for details.
The projects command displays the currently loaded projects. The projects
are grouped by their enclosing build and the current project is indicated by an
asterisk. For example,
> projects
[info] In file:/home/user/demo/
[info] * parent
[info] sub
[info] In file:/home/user/dep/
[info] sample
session list displays the settings that have been added at the command line
for the current project. For example,
session list-all displays the settings added for all projects. For details, see
help session.
> about
[info] This is sbt 1.1.5
[info] The current project is {file:~/code/sbt.github.com/}default
[info] The current project is built against Scala 2.12.6
[info] Available Plugins: sbt.plugins.IvyPlugin, sbt.plugins.JvmPlugin, sbt.plugins.CorePlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.12.6
The inspect command shows the value of a setting as part of its output, but
the show command is dedicated to this job. It shows the output of the setting
provided as an argument. For example,
376
> show organization
[info] com.github.sbt
The show command will execute the task provided as an argument and then
print the result. Note that this is different from the behavior of the inspect
command (described in other sections), which does not execute a task and thus
can only display its type and not its generated value.
sbt detects the classes with public, static main methods for use by the run
method and to tab-complete the runMain method. The discoveredMainClasses
task does this discovery and provides as its result the list of class names. For
example, the following shows the main classes discovered in the main sources:
377
Show the test classes detected in a project
Interactive mode
> tes<TAB>
> test
> test<TAB>
testFrameworks testListeners testLoader testOnly testOptions test:
Now, there is more than one possibility for the next character, so sbt prints the
available options. We will select testOnly and get more suggestions by entering
the rest of the command and hitting tab twice:
> testOnly<TAB><TAB>
-- sbt.DagSpecification sbt.EmptyRelationTest sbt.KeyTest sbt.Relati
378
The first tab inserts an unambiguous space and the second suggests names of
tests to run. The suggestion of -- is for the separator between test names and
options provided to the test framework. The other suggestions are names of
test classes for one of sbt’s modules. Test name suggestions require tests to be
compiled first. If tests have been added, renamed, or removed since the last
test compilation, the completions will be out of date until another successful
compile.
Some commands have different levels of completion. Hitting tab multiple times
increases the verbosity of completions. (Presently, this feature is only used by
the set command.)
JLine, used by both Scala and sbt, uses a configuration file for many of its
keybindings. The location of this file can be changed with the system property
jline.keybindings. The default keybindings file is included in the sbt launcher
and may be used as a starting point for customization.
By default, sbt only displays > to prompt for a command. This can be changed
through the shellPrompt setting, which has type State => String. State
contains all state for sbt and thus provides access to all build information for
use in the prompt string.
Examples:
// set the prompt (for this build) to include the project id.
shellPrompt in ThisBuild := { state => Project.extract(state).currentRef.project + "> " }
// set the prompt (for the current project) to include the username
shellPrompt := { state => System.getProperty("user.name") + "> " }
Use history
Interactive mode remembers history even if you exit sbt and restart it. The
simplest way to access history is to press the up arrow key to cycle through
previously entered commands. Use Ctrl+r to incrementally search history back-
wards. The following commands are supported:
379
• ! Show history command help.
• !! Execute the previous command again.
• !: Show all previous commands.
• !:n Show the last n commands.
• !n Execute the command with index n, as shown by the !: command.
• !-n Execute the nth command before this one.
• !string Execute the most recent command starting with ‘string’
• !?string Execute the most recent command containing ‘string’
By default, interactive history is stored in the target/ directory for the current
project (but is not removed by a clean). History is thus separate for each
subproject. The location can be changed with the historyPath setting, which
has type Option[File]. For example, history can be stored in the root directory
for the project instead of the output directory:
The history path needs to be set for each project, since sbt will use the value of
historyPath for the current project (as selected by the project command).
The previous section describes how to configure the location of the history file.
This setting can be used to share the interactive history among all projects in a
build instead of using a different history for each project. The way this is done
is to set historyPath to be the same file, such as a file in the root project’s
target/ directory:
historyPath :=
Some( (target in LocalRootProject).value / ".history")
The in LocalRootProject part means to get the output directory for the root
project for the build.
If, for whatever reason, you want to disable history, set historyPath to None
in each project it should be disabled in:
380
Run commands before entering interactive mode
This runs clean and then compile before entering the interactive prompt. If
either clean or compile fails, sbt will exit without going to the prompt. To enter
the prompt whether or not these initial commands succeed, prepend "onFailure
shell", which means to run shell if any command fails. For example,
When a command is run, more detailed logging output is sent to a file than
to the screen (by default). This output can be recalled for the command just
executed by running last.
For example, the output of run when the sources are uptodate is:
> run
[info] Running A
Hi!
[success] Total time: 0 s, completed Feb 25, 2012 1:00:00 PM
> last
[debug] Running task... Cancelable: false, max worker threads: 4, check cycles: false
[debug]
[debug] Initial source changes:
[debug] removed:Set()
[debug] added: Set()
[debug] modified: Set()
[debug] Removed products: Set()
[debug] Modified external sources: Set()
[debug] Modified binary dependencies: Set()
[debug] Initial directly invalidated sources: Set()
381
[debug]
[debug] Sources indirectly invalidated by:
[debug] product: Set()
[debug] binary dep: Set()
[debug] external source: Set()
[debug] Initially invalidated: Set()
[debug] Copy resource mappings:
[debug]
[info] Running A
[debug] Starting sandboxed run...
[debug] Waiting for threads to exit or System.exit to be called.
[debug] Classpath:
[debug] /tmp/e/target/scala-2.9.2/classes
[debug] /tmp/e/.sbt/0.12.0/boot/scala-2.9.2/lib/scala-library.jar
[debug] Waiting for thread runMain to exit
[debug] Thread runMain exited.
[debug] Interrupting remaining threads (should be all daemons).
[debug] Sandboxed run complete..
[debug] Exited with code 0
[success] Total time: 0 s, completed Jan 1, 2012 1:00:00 PM
Configuration of the logging level for the console and for the backing file are
described in following sections.
When a task is run, more detailed logging output is sent to a file than to the
screen (by default). This output can be recalled for a specific task by running
last <task>. For example, the first time compile is run, output might look
like:
> compile
[info] Updating {file:/.../demo/}example...
[info] Resolving org.scala-lang#scala-library;2.9.2 ...
[info] Done updating.
[info] Compiling 1 Scala source to .../demo/target/scala-2.9.2/classes...
[success] Total time: 0 s, completed Jun 1, 2012 1:11:11 PM
The output indicates that both dependency resolution and compilation were
performed. The detailed output of each of these may be recalled individually.
For example,
382
[debug] Initial source changes:
[debug] removed:Set()
[debug] added: Set(/home/mark/tmp/a/b/A.scala)
[debug] modified: Set()
...
and:
The Scala compiler does not print the full details of warnings by default. Com-
piling code that uses the deprecated error method from Predef might generate
the following output:
> compile
[info] Compiling 1 Scala source to <...>/classes...
[warn] there were 1 deprecation warnings; re-run with -deprecation for details
[warn] one warning found
> printWarnings
[warn] A.scala:2: method error in object Predef is deprecated: Use sys.error(message) instead
[warn] def x = error("Failed.")
[warn] ^
The quickest way to change logging levels is by using the error, warn, info, or
debug commands. These set the default logging level for commands and tasks.
For example,
383
> warn
will by default show only warnings and errors. To set the logging level before
any commands are executed on startup, use -- before the logging level. For
example,
$ sbt --warn
> compile
[warn] there were 2 feature warning(s); re-run with -feature for details
[warn] one warning found
[success] Total time: 4 s, completed ...
>
The amount of logging is controlled by the logLevel setting, which takes values
from the Level enumeration. Valid values are Error, Warn, Info, and Debug
in order of increasing verbosity. The logging level may be configured globally,
as described in the previous section, or it may be applied to a specific project,
configuration, or task. For example, to change the logging level for compilation
to only show warnings and errors:
A common scenario is that after running a task, you notice that you need
more information than was shown by default. A logLevel based solution typ-
ically requires changing the logging level and running a task again. However,
there are two cases where this is unnecessary. First, warnings from a previous
compilation may be displayed using printWarnings for the main sources or
test:printWarnings for test sources. Second, output from the previous execu-
tion is available either for a single task or for in its entirety. See the section on
printWarnings and the sections on previous output.
384
Configure printing of stack traces
By default, sbt hides the stack trace of most exceptions thrown during execution.
It prints a message that indicates how to display the exception. However, you
may want to show more of stack traces by default.
The setting to configure is traceLevel, which is a setting with an Int value.
When traceLevel is set to a negative value, no stack traces are shown. When
it is zero, the stack trace is displayed up to the first sbt stack frame. When
positive, the stack trace is shown up to that many stack frames.
For example, the following configures sbt to show stack traces up to the first
sbt frame:
The every part means to override the setting in all scopes. To change the trace
printing behavior for a single project, configuration, or task, scope traceLevel
appropriately:
By default, sbt buffers the logging output of a test until the whole class finishes.
This is so that output does not get mixed up when executing in parallel. To
disable buffering, set the logBuffered setting to false:
logBuffered := false
The setting extraLoggers can be used to add custom loggers. A custom logger
should implement [AbstractLogger]. extraLoggers is a function ScopedKey[_]
=> Seq[AbstractLogger]. This means that it can provide different logging
based on the task that requests the logger.
extraLoggers := {
val currentFunction = extraLoggers.value
(key: ScopedKey[_]) => {
myCustomLogger(key) +: currentFunction(key)
}
}
385
Here, we take the current function currentFunction for the setting and provide
a new function. The new function prepends our custom logger to the ones
provided by the old function.
The special task streams provides per-task logging and I/O via a Streams in-
stance. To log, a task uses the log member from the streams task. Calling log
provides a Logger.
:
myTask := {
val log = streams.value.log
log.warn("A warning.")
}
Since settings cannot reference tasks, the special task streams cannot be used
to provide logging during setting initialization. The recommended way is to use
sLog. Calling sLog.value provides a Logger.
mySetting := {
val log = sLog.value
log.warn("A warning.")
}
Project metadata
A project should define name and version. These will be used in various parts of
the build, such as the names of generated artifacts. Projects that are published
to a repository should also override organization.
386
Set the project version
version := "1.0"
organization := "org.example"
By convention, this is a reverse domain name that you own, typically one specific
to your project. It is used as a namespace for projects.
A full/formal name can be defined in the organizationName setting. This is
used in the generated pom.xml. If the organization has a web site, it may be
set in the organizationHomepage setting. For example:
organizationHomepage := Some(url("http://example.org"))
homepage := Some(url("https://www.scala-sbt.org"))
startYear := Some(2008)
Configure packaging
exportJars := true
The jar will be used by run, test, console, and other tasks that use the full
classpath.
387
Add manifest attributes
By default, sbt constructs a manifest for the binary package from settings such
as organization and mainClass. Additional attributes may be added to the
packageOptions setting scoped by the configuration and package task.
Main attributes may be added with Package.ManifestAttributes. There are
two variants of this method, once that accepts repeated arguments that map
an attribute of type java.util.jar.Attributes.Name to a String value and
other that maps attribute names (type String) to the String value.
For example,
The artifactName setting controls the name of generated packages. See the
Artifacts page for details.
388
mappings in (Compile, packageBin) += {
(baseDirectory.value / "in" / "example.txt") -> "out/example.txt"
}
Note that mappings is scoped by the configuration and the specific package
task. For example, the mappings for the test source package are defined by the
mappings in (Test, packageSrc) task.
Running commands
> ~ ;clean;compile
The < command reads commands from the files provided to it as arguments.
Run help < at the sbt prompt for details.
The alias command defines, removes, and displays aliases for commands. Run
help alias at the sbt prompt for details.
Example usage:
389
> alias a=about
> alias
a = about
> a
[info] This is sbt ...
> alias a=
> alias
> a
[error] Not a valid command: a ...
The eval command compiles and runs the Scala expression passed to it as an
argument. The result is printed along with its type. For example,
The scalaVersion configures the version of Scala used for compilation. By de-
fault, sbt also adds a dependency on the Scala library with this version. See the
next section for how to disable this automatic dependency. If the Scala version
is not specified, the version sbt was built against is used. It is recommended to
explicitly specify the version of Scala.
For example, to set the Scala version to “2.11.1”,
scalaVersion := "2.11.1"
sbt adds a dependency on the Scala standard library by default. To disable this
behavior, set the autoScalaLibrary setting to false.
autoScalaLibrary := false
390
Temporarily switch to a different Scala version
To set the Scala version in all scopes to a specific value, use the ++ command.
For example, to temporarily use Scala 2.10.4, run:
> ++ 2.10.4
Defining the scalaHome setting with the path to the Scala home directory will
use that Scala installation. sbt still requires scalaVersion to be set when a
local Scala version is used. For example,
scalaVersion := "2.10.0-local"
scalaHome := Some(file("/path/to/scala/home/"))
The consoleQuick action retrieves dependencies and puts them on the classpath
of the Scala REPL. The project’s sources are not compiled, but sources of any
source dependencies are compiled. To enter the REPL with test dependencies
on the classpath but without compiling test sources, run test:consoleQuick.
This will force compilation of main sources.
The console action retrieves dependencies and compiles sources and puts them
on the classpath of the Scala REPL. To enter the REPL with test dependencies
and compiled test sources on the classpath, run test:console.
Enter the Scala REPL with plugins and the build definition on the
classpath
> consoleProject
391
Define the initial commands evaluated when entering the Scala REPL
sbt runs tests in the same JVM as sbt itself and Scala classes are not in the
same class loader as the application classes. This is also the case in console
and when run is not forked. Therefore, when using the Scala interpreter, it is
important to set it up properly to avoid an error message like:
392
The key is to initialize the Settings for the interpreter using embeddedDefaults.
For example:
sbt will run javadoc if there are only Java sources in the project. If there are
any Scala sources, sbt will run scaladoc. (This situation results from scaladoc
not processing Javadoc comments in Java sources nor linking to Javadoc.)
393
Set the options used for generating javadoc independently of compi-
lation
Set autoAPIMappings := true for sbt to tell scaladoc where it can find the
API documentation for managed dependencies. This requires that dependencies
have this information in its metadata and you are using scaladoc for Scala
2.10.2 or later.
apiMappings += (
(unmanagedBase.value / "a-library.jar") ->
url("https://example.org/api/")
)
394
Define the location of API documentation for a library
Set apiURL to define the base URL for the Scaladocs for your library. This will
enable clients of your library to automatically link against the API documenta-
tion using autoAPIMappings. (This only works for Scala 2.10.2 and later.) For
example,
apiURL := Some(url("https://example.org/api/"))
This information will get included in a property of the published pom.xml, where
it can be automatically consumed by sbt.
Triggered execution
You can make a command run when certain files change by prefixing the com-
mand with ~. Monitoring is terminated when enter is pressed. This triggered
execution is configured by the watch setting, but typically the basic settings
watchSources and pollInterval are modified as described in later sections.
The original use-case for triggered execution was continuous compilation:
> ~ test:compile
> ~ compile
You can use the triggered execution feature to run any command or task, how-
ever. The following will poll for changes to your source code (main or test) and
run testOnly for the specified test.
> ~ ;a ;b
395
Configure the sources that are checked for changes
• watchSources defines the files for a single project that are monitored
for changes. By default, a project watches resources and Scala and Java
sources.
• watchTransitiveSources then combines the watchSources for the cur-
rent project and all execution and classpath dependencies (see .scala build
definition for details on inter-project dependencies).
import scala.concurrent.duration._
pollInterval := 1.second
myTestTask := {
(test in (core, Test)).value
(test in (tools, Test)).value
}
396
How to take an action on startup
A global setting onLoad is of type State => State and is executed once, after
all projects are built and loaded. There is a similar hook onUnload for when a
project is unloaded.
Project unloading typically occurs as a result of a reload command or a set
command. Because the onLoad and onUnload hooks are global, modifying this
setting typically involves composing a new function with the previous value.
The following example shows the basic structure of defining onLoad.
Suppose you want to run a task named dependencyUpdates on start up. Here’s
what you can do:
// This prepends the String you would type into the shell
lazy val startupTransition: State => State = { s: State =>
"dependencyUpdates" :: s
}
You can use this technique to switch the startup subproject too.
Sequencing
397
tasks. So ideally, what you should do is define task Y yourself, and depend on
the task X.
taskY := {
val x = taskX.value
x + 1
}
This is more constrained compared to the imperative style plain Scala code with
side effects such as the follows:
project/build.properties
sbt.version=1.2.7
398
project/style.sbt
build.sbt
To call this task type in compilecheck from the shell. If the compilation fails,
compilecheck would stop the execution.
root> compilecheck
[info] Compiling 1 Scala source to /Users/x/proj/target/scala-2.10/classes...
[error] /Users/x/proj/src/main/scala/Foo.scala:3: Unmatched closing brace '}' ignored here
[error] }
[error] ^
[error] one error found
[error] (compile:compileIncremental) Compilation failed
If sequential task is not enough, another step up is the dynamic task. Unlike
Def.task which expects you to return pure value A, with a Def.taskDyn you
return a task sbt.Def.Initialize[sbt.Task[A]] which the task engine can
continue the rest of the computation with.
Let’s try implementing a custom task called compilecheck that runs compile
in Compile and then scalastyle in Compile task added by scalastyle-sbt-
plugin.
project/build.properties
sbt.version=1.2.7
399
project/style.sbt
build.sbt v1
Now we have the same thing as the sequential task, except we can now return
the result c from the first task.
Now we can actually call compile in Compile from the shell and make it do
what we want it to do.
Thus far we’ve mostly looked at tasks. There’s another kind of tasks called
input tasks that accepts user input from the shell. A typical example for this is
400
the run in Compile task. The scalastyle task is actually an input task too.
See input task for the details of the input tasks.
Now suppose we want to call run in Compile task and then open the browser
for testing purposes.
src/main/scala/Greeting.scala
build.sbt v1
Here, I’m faking the browser opening using println as the side effect. We can
now call this task from the shell:
build.sbt v2 We can actually remove runopen key, by rewriting the new input
task to run in Compile:
401
Defining a dynamic input task with Def.inputTaskDyn
Let’s suppose that there’s a task already that does the bowser opening called
openbrowser because of a plugin. Here’s how we can sequence a task after an
input tasks.
build.sbt v1
402
}).evaluated,
actualRun in Compile := Defaults.runTask(
fullClasspath in Runtime,
mainClass in (Compile, run),
runner in (Compile, run)
).evaluated,
openbrowser := {
println("open browser!")
}
)
* Note that some tasks (ie. testOnly) will fail with trailing spaces, so a right
trim (.replaceAll("\s+$", "")) of the string built for toTask might be needed
to handle empty args.
The actualRun in Compile’s implementation was copy-pasted from run task’s
implementation in Defaults.scala.
Now we can call run foo from the shell and it will evaluate actualRun in
Compile with the passed in argument, and then evaluate the openbrowser task.
If all you care about is the side effects, and you really just want to emulate
humans typing in one command after another, a custom command might be
just want you need. This comes in handy for release procedures.
Here’s from the build script of sbt itself:
Examples
This section of the documentation has example sbt build definitions and code.
Contributions are welcome!
You may want to read the Getting Started Guide as a foundation for under-
standing the examples.
403
.sbt build examples
Note: As of sbt 0.13.7 blank lines are no longer used to delimit build.sbt files.
The following example requires sbt 0.13.7+.
Listed here are some examples of settings (each setting is independent). See
.sbt build definition for details.
import scala.concurrent.duration._
// increase the time between polling for file changes when using continuous execution
pollInterval := 1.second,
404
// append several options to the list of options passed to the Java compiler
javacOptions ++= Seq("-source", "1.5", "-target", "1.5"),
// set the initial commands when entering 'console' or 'consoleQuick', but not 'consoleP
initialCommands in console := "import myproject._",
// set the prompt (for this build) to include the project id.
405
shellPrompt in ThisBuild := { state => Project.extract(state).currentRef.project + "> "
// set the prompt (for the current project) to include the username
shellPrompt := { state => System.getProperty("user.name") + "> " },
// set the location of the JDK to use for compiling Java code.
// if 'fork' is true, this is used for 'run' as well
javaHome := Some(file("/usr/lib/jvm/sun-jdk-1.6")),
// Use Scala from a directory on the filesystem instead of retrieving from a repository
scalaHome := Some(file("/home/user/scala/trunk/")),
406
// only show warnings and errors on the screen for compilations.
// this applies to both test:compile and compile and is Info by default
logLevel in compile := Level.Warn,
// only show warnings and errors on the screen for all tasks (the default is Info)
// individual tasks can then be more verbose using the previous setting
logLevel := Level.Warn,
407
// Exclude transitive dependencies, e.g., include log4j without including logging via jd
libraryDependencies +=
"log4j" % "log4j" % "1.2.15" excludeAll(
ExclusionRule(organization = "com.sun.jdmk"),
ExclusionRule(organization = "com.sun.jmx"),
ExclusionRule(organization = "javax.jms")
)
)
.sbt builds can be supplemented with project/*.scala files. When the build
file gets large enough, the first thing to factor out are resolvers and dependencies.
project/Resolvers.scala
import sbt._
import Keys._
object Resolvers {
val sunrepo = "Sun Maven2 Repo" at "http://download.java.net/maven/2"
val sunrepoGF = "Sun GF Maven2 Repo" at "http://download.java.net/maven/glassfish"
val oraclerepo = "Oracle Maven2 Repo" at "http://download.oracle.com/maven"
project/Dependencies.scala
import sbt._
import Keys._
object Dependencies {
val logbackVersion = "0.9.16"
val grizzlyVersion = "1.9.19"
408
val grizzlyrcm = "com.sun.grizzly" % "grizzly-rcm" % grizzlyVersion
val grizzlyutils = "com.sun.grizzly" % "grizzly-utils" % grizzlyVersion
val grizzlyportunif = "com.sun.grizzly" % "grizzly-portunif" % grizzlyVersion
project/ShellPromptPlugin.scala
When you want to implement custom commands or tasks, you can organize your
build by defining an one-off auto plugin.
import sbt._
import Keys._
import scala.sys.process._
// Shell prompt which show the current project and git branch
object ShellPromptPlugin extends AutoPlugin {
override def trigger = allRequirements
override lazy val projectSettings = Seq(
shellPrompt := buildShellPrompt
)
val devnull: ProcessLogger = new ProcessLogger {
def out(s: => String): Unit = {}
def err(s: => String): Unit = {}
def buffer[T] (f: => T): T = f
}
def currBranch =
("git status -sb" lineStream_! devnull headOption)
.getOrElse("-").stripPrefix("## ")
val buildShellPrompt: State => String = {
case (state: State) =>
val currProject = Project.extract (state).currentProject.id
s"""$currProject:$currBranch> """
}
}
This auto plugin will display the current project name and the git branch.
409
build.sbt
import Resolvers._
import Dependencies._
410
libraryDependencies ++= commonDeps
)
// Custom configurations
lazy val Common = config("common") describedAs("Dependencies required in all configurations.
411
lazy val Scalate = config("scalate") extend(Common) describedAs("Dependencies for using Scal
lazy val Saxon = config("saxon") extend(Common) describedAs("Dependencies for using Saxon ut
// We want our Common sources to have access to all of the dependencies on the classpa
// for compile and test, but when depended on, it should only require dependencies i
classpathConfiguration in Common := CustomCompile,
// Modify the default Ivy configurations.
// 'overrideConfigs' ensures that Compile is replaced by CustomCompile
ivyConfigurations := overrideConfigs(Scalate, Saxon, Common, CustomCompile)(ivyConfigura
// Put all dependencies without an explicit configuration into Common (optional)
defaultConfiguration := Some(Common),
// Declare dependencies in the appropriate configurations
libraryDependencies ++= Seq(
"org.fusesource.scalate" % "scalate-core" % "1.5.0" % Scalate,
"org.squeryl" %% "squeryl" % "0.9.5-6" % Scalate,
"net.sf.saxon" % "saxon" % "8.7" % Saxon
)
)
412
Advanced command example
This is an advanced example showing some of the power of the new settings
system. It shows how to temporarily modify all declared dependencies in the
build, regardless of where they are defined. It directly operates on the final
Seq[Setting[_]] produced from every setting involved in the build.
The modifications are applied by running canonicalize. A reload or using set
reverts the modifications, requiring canonicalize to be run again.
This particular example shows how to transform all declared dependencies on
ScalaCheck to use version 1.8. As an exercise, you might try transforming other
dependencies, the repositories used, or the scalac options used. It is possible to
add or remove settings as well.
This kind of transformation is possible directly on the settings of Project, but it
would not include settings automatically added from plugins or build.sbt files.
What this example shows is doing it unconditionally on all settings in all projects
in all builds, including external builds.
import sbt._
import Keys._
// Define the command. This takes the existing settings (including any session settings)
// and applies 'f' to each Setting[_]
def canonicalize = Command.command("canonicalize") { (state: State) =>
val extracted = Project.extract(state)
import extracted._
val transformed = session.mergeSettings map ( s => f(s) )
appendWithSession(transformed, state)
}
// Transforms a Setting[_].
def f(s: Setting[_]): Setting[_] = s.key.key match {
// transform all settings that modify libraryDependencies
case Keys.libraryDependencies.key =>
// hey scalac. T == Seq[ModuleID]
s.asInstanceOf[Setting[Seq[ModuleID]]].mapInit(mapLibraryDependencies)
// preserve other settings
case _ => s
}
// This must be idempotent because it gets applied after every transformation.
// That is, if the user does:
413
// libraryDependencies += a
// libraryDependencies += b
// then this method will be called for Seq(a) and Seq(a,b)
def mapLibraryDependencies(key: ScopedKey[Seq[ModuleID]], value: Seq[ModuleID]): Seq[Modul
value map mapSingle
Project Information
What does the name “sbt” stand for, and why shouldn’t it be written
“SBT”? TL;DR the name sbt doesn’t stand for anything, it’s just “sbt”, and
it should be written that way.
When Mark Harrah ([@harrah][]) first created the project he called it “Simple
Build Tool”, but in his first public announcement of it he already referred to
it as just “sbt”. Over time some have re-defined sbt to stand for “Scala Build
Tool”, but we believe that isn’t accurate either given it can be used to build
Java-only projects.
Nowadays we just call sbt “sbt”, and to reinforce that the name is no longer an
initialism we always write it in all lowercase letters. However, we are cool with
�� (subuta) as a nickname.
• See Support
414
Usage
and it will display the full output from the last run of the update command.
How do I disable ansi codes in the output? Sometimes sbt doesn’t detect
that ansi codes aren’t supported and you get output that looks like:
or ansi codes are supported but you want to disable colored output. To com-
pletely disable ansi codes, pass -no-colors option:
$ sbt -no-colors
How can I start a Scala interpreter (REPL) with sbt project configu-
ration (dependencies, etc.)? In sbt’s shell run console.
Build definitions
What are the :=, +=, and ++= methods? These are methods on keys used
to construct a Setting or a Task. The Getting Started Guide covers all these
methods, see .sbt build definition, task graph, and appending values for example.
What is the % method? It’s used to create a ModuleID from strings, when
specifying managed dependencies. Read the Getting Started Guide about li-
brary dependencies.
415
How do I add files to a jar package? The files included in an artifact are
configured by default by a task mappings that is scoped by the relevant package
task. The mappings task returns a sequence Seq[(File,String)] of mappings
from the file to include to the path within the jar. See mapping files for details
on creating these mappings.
For example, to add generated sources to the packaged source artifact:
This takes sources from the managedSources task and relativizes them against
the managedSource base directory, falling back to a flattened mapping. If a
source generation task doesn’t write the sources to the managedSource directory,
the mapping function would have to be adjusted to try relativizing against
additional directories or something more appropriate for the generator.
How can a task avoid redoing work if the input files are unchanged?
There is basic support for only doing work when input files have changed or
when the outputs haven’t been generated yet. This support is primitive and
subject to change.
The relevant methods are two overloaded methods called FileFunction.cached.
Each requires a directory in which to store cached data. Sample usage is:
416
There are two additional arguments for the first parameter list that allow the file
tracking style to be explicitly specified. By default, the input tracking style is
FilesInfo.lastModified, based on a file’s last modified time, and the output
tracking style is FilesInfo.exists, based only on whether the file exists. The
other available style is FilesInfo.hash, which tracks a file based on a hash of
its contents. See the FilesInfo API for details.
A more advanced version of FileFunction.cached passes a data structure of
type ChangeReport describing the changes to input and output files since the
last evaluation. This version of cached also expects the set of files generated as
output to be the result of the evaluated function.
Extending sbt
This uses the main options as base options because of +=. Use := to ignore the
main options:
The example adds all of the usual compilation related settings and tasks to
samples:
samples:run
samples:runMain
samples:compile
samples:console
samples:consoleQuick
samples:scalacOptions
samples:fullClasspath
samples:package
samples:packageSrc
...
417
How do I add a test configuration? See the Additional test configurations
section of Testing.
How can I create a custom run task, in addition to run? This answer
is extracted from a mailing list discussion.
Read the Getting Started Guide up to custom settings for background.
A basic run task is created by:
As an example, consider a proguard task. This task needs the ProGuard jars
in order to run the tool. First, define and add the new configuration:
ivyConfigurations += ProguardConfig
418
Then,
Defining the intermediate classpath is optional, but it can be useful for debug-
ging or if it needs to be used by multiple tasks. It is also possible to specify
artifact types inline. This alternative proguard task would look like:
proguard := {
val artifactTypes = Set("jar")
val cp =
Classpaths.managedJars(proguardConfig, artifactTypes, update.value)
// ... do something with , which includes proguard ...
}
419
generally intentionally set sbt up this way, so error recovery is not typically
necessary (just a short error message explaining the situation.)
How can I take action when the project is loaded or unloaded? See
How to take an action on startup.
{
// the key for the current count
val key = AttributeKey[Int]("loadCount")
// the State transformer
val f = (s: State) => {
val previous = s get key getOrElse 0
println("Project load count: " + previous)
s.put(key, previous + 1)
}
onLoad in Global := {
val previous = (onLoad in Global).value
f compose previous
}
}
420
Errors
disablePlugins(plugins.IvyPlugin)
A more subtle variation of this error occurs when using scoped settings.
This setting varies between the test and compile scopes. The solution is use
the scoped setting, both as the input to the initializer, and the setting that we
update.
Dependency Management
How do I resolve a checksum error? This error occurs when the published
checksum, such as a sha1 or md5 hash, differs from the checksum computed for
a downloaded artifact, such as a jar or pom.xml. An example of such an error
is:
421
The invalid checksum should generally be reported to the repository owner (as
was done for the above error). In the meantime, you can temporarily disable
checking with the following setting:
Miscellaneous
Where can I find plugins for 1.2.7? See Community Plugins for a list of
currently available plugins.
Index
This is an index of common methods, types, and values you might find in an
sbt build definition. For command names, see Running. For available plugins,
see the plugins list.
Dependency Management
422
• ModuleID is the type of a dependency definition. See Library Manage-
ment.
• Artifact represents a single artifact (such as a jar or a pom) to be built
and published. See Library Management and Artifacts.
• A Resolver can resolve and retrieve dependencies. Many types of Resolvers
can publish dependencies as well. A repository is a closely linked idea that
typically refers to the actual location of the dependencies. However, sbt is
not very consistent with this terminology and repository and resolver are
occasionally used interchangeably.
• A ModuleConfiguration defines a specific resolver to use for a group of
dependencies.
• A Configuration is a useful Ivy construct for grouping dependencies. See
ivy-configurations. It is also used for scoping settings.
• Compile, Test, Runtime, Provided, and Optional are predefined config-
urations.
Build Structure
423
• State contains the full state for a build. It is mainly used by Commands
and sometimes Input Tasks. See also State and Actions.
Methods
Settings and Tasks See the Getting Started Guide for details.
• :=, +=, ++= These construct a Setting, which is the fundamental type in
the settings system.
• value This uses the value of another setting or task in the definition of
a new setting or task. This method is special (it is a macro) and cannot
be used except in the argument of one of the setting definition methods
above (:=, …) or in the standalone construction methods Def.setting and
Def.task. See Task-Graph for details.
• in specifies the Scope or part of the Scope of a setting being referenced.
See scopes.
File and IO See RichFile, PathFinder, and Paths for the full documentation.
• / When called on a single File, this is new File(x,y). For Seq[File], this
is applied for each member of the sequence..
• * and ** are methods for selecting children (*) or descendants (**) of a
File or Seq[File] that match a filter.
• |, ||, &&, &, -, and -- are methods for combining filters, which are often
used for selecting Files. See NameFilter and FileFilter. Note that methods
with these names also exist for other types, such as collections (like Seq)
and Parser (see Parsing Input).
• pair Used to construct mappings from a File to another File or to a
String. See Mapping Files.
• get forces a PathFinder (a call-by-name data structure) to a strict
Seq[File] representation. This is a common name in Scala, used by
types like Option.
424
• intransitive Marks a dependency or Configuration as being intransitive.
• hide Marks a Configuration as internal and not to be included in the
published metadata.
Parsing These methods are used to build up Parsers from smaller Parsers.
They closely follow the names of the standard library’s parser combinators. See
Parsing Input for the full documentation. These are used for Input Tasks and
Commands.
This is the set of documentation about the future architecture of sbt. The target
audience of this document is the sbt plugin authors and sbt developers. See also
How can I help?
On 2008-12-18, Mark Harrah announced sbt 0.3.2 as the initial release of sbt.
Mark remained the primary author of sbt until sbt 0.13.1 (2013-12-11). In 2014,
sbt project was handed over to the authors of this document Josh Suereth and
Eugene Yokota.
As we move towards sbt 1.0, we wish to stabilize what’s already stable and
innovate where it matters. There are several levels of stability:
425
• conceptual stability
• source compatibility of the build definition
• binary compatibility of the plugins
The only thing that we plan to change is the last point. In sbt 1.0, we will
replace the interactive shell with sbt server that’s accessible via JSON API and
a text-based client.
Modularization
The process we aim to take for sbt 1.0 is to disassemble sbt into smaller modules
and layers. To be clear, sbt 0.13’s codebase already does consist of numerous
subprojects.
Layers are more coarse-grained sets of subproject(s) that can be used indepen-
dently. Another purpose of the modularization is to distinguish between public
API and internal implementation. Reducing the surface area of the sbt code
base has several benefits:
426
• It makes it easier for the build users and the plugin authors to learn the
APIs.
• It makes it easier for us to maintain binary and semantic compatibilities.
• It encourages the reuse of the modules.
Module summary
This diagram is arranged such that each layer depends only on the layers un-
derneath it.
IO API (sbt/io) IO API is a low level API to deal with files and directories.
427
Util APIs (sbt/util) Util APIs provide commonly used features like logging
and internal datatypes used by sbt.
Build API (tbd) This is the part that’s exposed to build.sbt. The respon-
sibility of the module is to load the build files and plugins, and provide a way
for commands to be executed on the state.
This might remain at sbt/sbt.
This page discusses the coding style and other guidelines for sbt 1.0.
General goal
sbt 1.0 will primarily target Scala 2.12. We will cross-build against Scala 2.10.
428
Clean up old deprecation Before 1.0 is release, we should clean up depre-
cations.
Documentation
All newly introduced public traits and classes and, to a lesser extent, functions
and methods, should have Scaladoc. A significant amount of existing sbt code
lacks documentation and we need to repair this situation over time. If you see
an opportunity to add some documentation, or improve existing documentation
then this will also help.
Package level documentation is a great place to describe how various components
interact, so please consider adding/enhancing that where possible.
For more information on good Scaladoc style, please refer to the Scaladoc Style
Guide
Modular design
Aim small The fewer methods we can expose to the build user, the easier sbt
becomes to maintain.
Hide external classes Avoid exposing external classes in the API, except for
standard Scala and Java classes.
429
Hide internal modules A module may be declared internal if it has no use
to the public.
Compiler flags
-encoding utf8
-deprecation
-feature
-unchecked
-Xlint
-language:higherKinds
-language:implicitConversions
-Xfuture
-Yinline-warnings
-Yno-adapted-args
-Ywarn-dead-code
-Ywarn-numeric-widen
-Ywarn-value-discard
-Xfatal-warnings
Package name and organization name Use the package name appended
with the layer name, such as sbt.io for IO layer. The organization name for
published artifacts should remain org.scala-sbt.
Binary resiliency
A good overview on the topic of binary resiliency is Josh’s 2012 talk on Binary
resiliency. The guideline here applies mostly to publicly exposed APIs.
Abstract classes are also useful To trait, or not to trait?. Abstract classes
are less flexible than traits, but traits pose more problems for binary compati-
bility. Abstract classes also have better Java interoperability.
430
Seal traits and abstract classes If there’s no need to keep a class open,
seal it.
Finalize the leaf classes If there’s no need to keep a class open, finalize it.
Typeclass and subclass inheritance The typeclass pattern with pure traits
might ease maintaining binary compatibility more so than subclassing.
Avoid case classes, use sbt-datatype Case classes involve code generation
that makes it harder to maintain binary compatibility over time.
Avoid overuse of def apply def apply should be reserved for factory meth-
ods in a companion object that returns type T.
Avoid calling toSeq on Map Same as above. This will introduce non-
determinism.
431
Avoid functions and tuples in the signature, if Java interoperability is
needed Instead of functions and tuples, turn them into a trait. This applies
where interoperability is a concern, like implementing incremental compilation.
Style matters
Use scalafmt sbt-houserules comes with scalafmt for formatting source code
consistently.
package sbt.io
object syntax {
implicit def uriToRichURI(uri: URI): RichURI = new RichURI(uri)
}
When all the layers are available, the sbt package should also define an object
called syntax which forwards implicit conversions from all the layers:
package sbt
object syntax {
implicit def uriToRichURI(uri: URI): io.RichURI = io.syntax.uriToRichURI(uri)
....
}
432
sbt-datatype
To enable the plugin for your build, put the following line in project/datatype.sbt:
Datatype schema
1. Records
2. Interfaces
3. Enums
433
Records Records are mapped to Java or Scala classes, corresponding to the
standard case classes in Scala.
{
"types": [
{
"name": "Person",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "name",
"type": "String"
},
{
"name": "age",
"type": "int"
}
]
}
]
}
434
copy(age = age)
}
}
object Person {
def apply(name: String, age: Int): Person = new Person(name, age)
}
Or the following Java code (after changing the target property to "Java"):
435
{
"types": [
{
"name": "Greeting",
"namespace": "com.example",
"target": "Scala",
"type": "interface",
"fields": [
{
"name": "message",
"type": "String"
}
],
"types": [
{
"name": "SimpleGreeting",
"namespace": "com.example",
"target": "Scala",
"type": "record"
}
]
}
]
}
This generates abstract class named Greeting and a class named SimpleGreeting
that extends Greeting.
In addition, interfaces can define messages, which generates abstract method
declarations.
{
"types": [
{
"name": "FooService",
"target": "Scala",
"type": "interface",
"messages": [
{
"name": "doSomething",
"response": "int*",
"request": [
{
"name": "arg0",
"type": "int*",
"doc": [
436
"The first argument of the message.",
]
}
]
}
]
}
]
}
{
"types": [
{
"name": "Weekdays",
"type": "enum",
"target": "Java",
"symbols": [
"Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday", "Sunday"
]
}
]
}
Or the following Scala code (after changing the target property to):
437
case object Wednesday extends Weekdays
case object Thursday extends Weekdays
case object Friday extends Weekdays
case object Saturday extends Weekdays
case object Sunday extends Weekdays
}
{
"types": [
{
"name": "Greeting",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "message",
"type": "String"
}
]
}
]
}
The generated code could be used in a Scala program using the following code:
Imagine now that you would like to extend your datatype to include a date to
the Greetings. The datatype can be modified accordingly:
{
"types": [
{
"name": "Greeting",
"type": "record",
"target": "Scala",
438
"fields": [
{
"name": "message",
"type": "String"
},
{
"name": "date",
"type": "java.util.Date"
}
]
}
]
}
Unfortunately, the code that used Greeting would no longer compile, and
classes that have been compiled against the previous version of the datatype
would crash with a NoSuchMethodError.
To circumvent this problem and allow you to grow your datatypes, it is possible
to indicate the version since the field exists and a default value in the datatype
definition:
{
"types": [
{
"name": "Greeting",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "message",
"type": "String"
},
{
"name": "date",
"type": "java.util.Date",
"since": "0.2.0",
"default": "new java.util.Date()"
}
]
}
]
}
Now the code that was compiled against previous definitions of the datatype
will still run.
439
JSON codec generation
{
"codecNamespace": "com.example.codec",
"fullCodec": "CustomJsonProtocol",
"types": [
{
"name": "Person",
"namespace": "com.example",
"type": "record",
"target": "Scala",
"fields": [
{
"name": "name",
"type": "String"
},
{
"name": "age",
"type": "int"
}
]
}
]
}
440
scala> val p = Person("Bob", 20)
p: com.example.Person = Person(Bob, 20)
scala> assert(p == q)
All the elements of the schema definition accept a number of parameters that
will influence the generated code. These parameters are not available for every
node of the schema. Please refer to the syntax summary to see whether a
parameters can be defined for a node.
name
This parameter defines the name of a field, record, field, etc.
target
This parameter determines whether the code will be generated in Java or Scala.
namespace
This parameter exists only for Definitions. It determines the package in which
the code will be generated.
doc
The Javadoc that will accompany the generated element.
fields
For a protocol or a record only, it describes all the fields that compose the
generated entity.
types
For a protocol, it defines the child protocols and records that extend it.
For an enumeration, it defines the values of the enumeration.
441
since
This parameter exists for fields only. It indicates the version in which the field
has been added to its parent protocol or record.
When this parameter is defined, default must also be defined.
default
This parameter exists for fields only. It indicates what the default value should
be for this field, in case it is used by a class that has been compiled against an
earlier version of this datatype.
It must contain an expression which is valid in the target language of the parent
protocol or record.
type for fields
It indicates what is the underlying type of the field.
Always use the type that you want to see in Scala. For instance, if your field
will contain an integer value, use Int rather than Java’s int. datatype will
automatically use Java’s primitive types if they are available.
For non-primitive types, it is recommended to write the fully-qualified type.
type for other definitions
It simply indicates the kind of entity that you want to generate: protocol,
record or enumeration.
Settings
This location can be changed by setting a new location in your build definition:
442
Syntax summary
Symbol := ID
{ "name": ID
(, "doc": string constant)? }
443
Request := { "name": ID,
"type": ID
(, "doc": string constant)? }
Compiler Interface
The compiler interface is the communication link between sbt and the Scala
compiler.
It is used to get information from the Scala compiler, and must therefore be
compiled against the Scala version in use for the configured projects.
The code for this project can be found in the directory internal/compiler-bridge.
Because the compiler interface is recompiled against each Scala version in use
in your project, its source must stay compatible with all the Scala versions that
sbt supports (from Scala 2.8 to the latest version of Scala).
This comes at great cost for both the sbt maintainers and the Scala compiler
authors:
1. The compiler authors cannot remove old and deprecated public APIs from
the Scala compiler.
2. sbt cannot use new APIs defined in the Scala compiler.
3. sbt must implement all kinds of hackery to remain source-compatible with
all versions of the Scala compiler and support new features.
To circumvent this problem, a new mechanism that allows sbt to fetch the
version of the sources for the compiler interface that are the most specific for
the Scala version in use has been implemented in sbt.
For instance, for a project that is compiled using Scala 2.11.8-M2, sbt will look
for the following version of the sources for the compiler interface, in this order:
1. 2.11.8-M2
2. 2.11.8
3. 2.11
4. The default sources.
This new mechanism allows both the Scala compiler and sbt to move forward
and enjoy new APIs while being certain than users of older versions of Scala
will still be able to use sbt.
444
Finally, another advantage of this technique is that it relies on Ivy to retrieve
the sources of the compiler bridge, but can be easily ported for use with Maven,
which is the distribution mechanism that the sbt maintainers would like to use
to distribute sbt’s modules.
sbt Launcher
The sbt launcher provides a generic container that can load and run programs
resolved using the Ivy dependency manager. Sbt uses this as its own deployment
mechanism.
The code is hosted at sbt/launcher.
The sbt launcher component is a self-contained jar that boots a Scala application
or server without Scala or the application already existing on the system. The
only prerequisites are the launcher jar itself, an optional configuration file, and
a Java runtime version 1.6 or greater.
Overview
A user downloads the launcher jar and creates a script to run it. In this docu-
mentation, the script will be assumed to be called launch. For Unix, the script
would look like: java -jar sbt-launcher.jar "$@"
The user can now launch servers and applications which provide sbt launcher
configuration.
Alternatively, you can repackage the launcher with a launcher configuration
file. For example, sbt/sbt pulls in the raw JAR and injects the appropriate
boot.properties files for sbt.
445
The user can then launch the application using myapp arg1 arg2 ...
More on launcher configuration can be found at Launcher Configuration
Servers The sbt launcher can be used to launch and discover running servers
on the system. The launcher can be used to launch servers similarly to applica-
tions. However, if desired, the launcher can also be used to ensure that only one
instance of a server is running at time. This is done by having clients always
use the launcher as a service locator.
To discover where a server is running (or launch it if it is not run-
ning), the user downloads the configuration file for the server (call it
my.server.configuration) and creates a script to discover the server (call it
find-myserver):
This command will print out one string, the URI at which to reach the server,
e.g. sbt://127.0.0.1:65501. Clients should use the IP/port to connect to to
the server and initiate their connection.
When using the locate feature, the sbt launcher makes the following restrictions
to servers:
• The Server must have a starting class that extends the xsbti.ServerMain
class
• The Server must have an entry point (URI) that clients can use to detect
the server
• The server must have defined a lock file which the launcher can use to
ensure that only one instance is running at a time
• The filesystem on which the lock file resides must support locking.
• The server must allow the launcher to open a socket against the port
without sending any data. This is used to check if a previous server is still
alive.
446
about how it was called: command line arguments, current working directory,
Scala version, and application ID (organization, name, version). In addition,
the application can ask the launcher to perform operations such as obtaining
the Scala jars and a ClassLoader for any version of Scala retrievable from the
repositories specified in the configuration file. It can request that other appli-
cations be downloaded and run. When the application completes, it can tell
the launcher to exit with a specific exit code or to reload the application with
a different version of Scala, a different version of the application, or different
arguments.
There are some other options for setup, such as putting the configuration file
inside the launcher jar and distributing that as a single download. The rest
of this documentation describes the details of configuring, writing, distributing,
and running the application.
resolvers += sbtResolver.value
package com.acme.launcherapp
// demonstrate the ability to reboot the application into different versions of Scal
447
// and how to return the code to exit with
scalaVersion match
{
case "2.10.6" =>
new xsbti.Reboot {
def arguments = configuration.arguments
def baseDirectory = configuration.baseDirectory
def scalaVersion = "2.11.8"
def app = configuration.provider.id
}
case "2.11.8" => new Exit(1)
case _ => new Exit(0)
}
}
class Exit(val code: Int) extends xsbti.Exit
}
Next, define a configuration file for the launcher. For the above class, it might
look like:
[scala]
version: 2.11.8
[app]
org: com.acme
name: launcherapp
version: 0.0.1
class: com.acme.launcherapp.Main
cross-versioned: true
[repositories]
local
maven-central
[boot]
directory: ${user.home}/.myapp/boot
448
• The user downloads the launcher jar and you provide the configuration
file.
– The user needs to run java -Dsbt.boot.properties=your.boot.properties
-jar launcher.jar.
– The user already has a script to run the launcher (call it ‘launch’).
The user needs to run launch @your.boot.properties your-arg-1
your-arg-2
Execution Let’s review what’s happening when the launcher starts your ap-
plication.
On startup, the launcher searches for its configuration and then parses it. Once
the final configuration is resolved, the launcher proceeds to obtain the necessary
jars to launch the application. The boot.directory property is used as a base
directory to retrieve jars to. Locking is done on the directory, so it can be shared
system-wide. The launcher retrieves the requested version of Scala to
${boot.directory}/${scala.version}/lib/
If this directory already exists, the launcher takes a shortcut for startup perfor-
mance and assumes that the jars have already been downloaded. If the directory
does not exist, the launcher uses Apache Ivy to resolve and retrieve the jars. A
similar process occurs for the application itself. It and its dependencies are
retrieved to
${boot.directory}/${scala.version}/${app.org}/${app.name}/.
Once all required code is downloaded, the class loaders are set up. The launcher
creates a class loader for the requested version of Scala. It then creates a child
class loader containing the jars for the requested app.components and with the
paths specified in app.resources. An application that does not use components
will have all of its jars in this class loader.
The main class for the application is then instantiated. It must be a public class
with a public no-argument constructor and must conform to xsbti.AppMain.
The run method is invoked and execution passes to the application. The argu-
ment to the ‘run’ method provides configuration information and a callback to
obtain a class loader for any version of Scala that can be obtained from a repos-
itory in [repositories]. The return value of the run method determines what
is done after the application executes. It can specify that the launcher should
restart the application or that it should exit with the provided exit code.
449
Sbt Launcher Architecture
The sbt launcher is a mechanism whereby modules can be loaded from Ivy and
executed within a JVM. It abstracts the mechanism of grabbing and caching
jars, allowing users to focus on what application they want, and control its
versions.
The launcher’s primary goal is to take configuration for applications— mostly
Ivy coordinates and a main class—and start the application. The launcher
resolves the Ivy module, caches the required runtime jars, and starts the appli-
cation.
The sbt launcher provides the application with the means to load a different
application when it completes, exit normally, or load additional applications
from inside another.
The sbt launcher provides these core functions:
• Module Resolution
• Classloader Caching and Isolation
• File Locking
• Service Discovery and Isolation
Module Resolution
The primary purpose of the sbt launcher is to resolve applications and run them.
This is done through the [app] configuration section. See launcher configuration
for more information on how to configure module resolution.
Module resolution is performed using the Ivy dependency management library.
This library supports loading artifacts from Maven repositories as well.
The sbt launcher’s classloading structure is different than just starting an ap-
plication in the standard Java mechanism. Every application loaded by the
launcher is given its own classloader. This classloader is a child of the Scala
classloader used by the application. The Scala classloader can see all of the
xsbti.* classes from the launcher itself.
Here’s an example classloader layout from an sbt-launched application.
In this diagram, three different applications were loaded. Two of these use the
same version of Scala (2.9.2). In this case, sbt can share the same classloader for
these applications. This has the benefit that any JIT optimisations performed
on Scala classes can be re-used between applications thanks to the shared class-
loader.
450
Figure 11: image
451
Caching
The sbt launcher creates a secondary cache on top of Ivy’s own cache. This helps
isolate applications from errors resulting from unstable revisions, like -SNAPSHOT.
For any launched application, the launcher creates a directory to store all its
jars. Here’s an example layout.
Locking
This feature requires a filesystem which supports locking. It is exposed via the
xsbti.GlobalLock interface.
Note: This is both a thread and file lock. Not only are we limiting access to a
single process, but also a single thread within that process.
The launcher also provides a mechanism to ensure that only one instance of a
server is running, while dynamically starting it when a client requests. This is
done through the --locate flag on the launcher. When the launcher is started
with the --locate flag it will do the following:
The configured server.lock file is thus used to prevent multiple servers from
running. sbt itself uses this to prevent more than one server running on any given
project directory by configuring server.lock to be ${user.dir}/.sbtserver.
452
sbt Launcher Configuration
The launcher may be configured in one of the following ways in increasing order
of precedence:
Example
[scala]
version: ${sbt.scala.version-auto}
[app]
org: ${sbt.organization-org.scala-sbt}
name: sbt
version: ${sbt.version-read(sbt.version)[0.13.5]}
class: ${sbt.main.class-sbt.xMain}
components: xsbti,extra
cross-versioned: ${sbt.cross.versioned-false}
[repositories]
local
typesafe-ivy-releases: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[mod
maven-central
sonatype-snapshots: https://oss.sonatype.org/content/repositories/snapshots
[boot]
directory: ${sbt.boot.directory-${sbt.global.base-${user.home}/.sbt}/boot/}
[ivy]
ivy-home: ${sbt.ivy.home-${user.home}/.ivy2/}
453
checksums: ${sbt.checksums-sha1,md5}
override-build-repos: ${sbt.override.build.repos-false}
repository-config: ${sbt.repository.config-${sbt.global.base-${user.home}/.sbt}/repositorie
454
3. Repositories Section The [repositories] section configures where and
how Ivy will look for your application. Each line denotes a repository where Ivy
will look.
Note: This section configured the default location where Ivy will look, but this
can be overridden via user configuration.
There are several built-in strings that can be used for common repositories:
Besides built in repositories, other repositories can be configured using the fol-
lowing syntax:
The name property is an identifier which Ivy uses to cache modules resolved
from this location. The name should be unique across all repositories.
The url property is the base url where Ivy should look for modules.
The pattern property is an optional specification of how Ivy should look for
modules. By default, the launcher assumes repositories are in the maven style
format.
The skipConsistencyCheck string is used to tell Ivy not to validate checksums
and signatures of files it resolves.
4. The Boot section The [boot] section is used to configure where the sbt
launcher will store its cache and configuration information. It consists of the
following properties:
• directory - The directory defined here is used to store all cached JARs
resolved launcher.
• properties - (optional) A properties file to use for any read variables.
5. The Ivy section The [ivy] section is used to configure the Ivy depen-
dency manager for resolving applications. It consists of the following properties:
• ivy-home - The home directory for Ivy. This determines where the ivy-
local repository is located, and also where the Ivy cache is stored. Defaults
to ~/.ivy2
455
• checksums - The comma-separated list of checksums that Ivy should use
to verify artifacts have correctly resolved, e.g. md5 or sha1.
• override-build-repos - If this is set, then the isOverrideRepositories
method on xsbti.Launcher interface will return its value. The use of
this method is application-specific, but in the case of sbt denotes that the
configuration of repositories in the launcher should override those used by
any build. Applications should respect this convention if they can.
• repository-config - This specifies a configuration location where Ivy
repositories can also be configured. If this file exists, then its contents
override the [repositories] section.
6. The Server Section When using the --locate feature of the launcher,
this section configures how a server is started. It consists of the following prop-
erties:
• lock - The file that controls access to the running server. This file will
contain the active port used by a server and must be located on a filesystem
that supports locking.
• jvmargs - A file that contains line-separated JVM arguments that were
used when starting the server.
• jvmprops - The location of a properties file that will define override prop-
erties in the server. All properties defined in this file will be set as -D Java
properties.
Variable Substitution
• ${variable.name}
• ${variable.name-default}
read(property.name)[default]
This will look in the file configured by boot.properties for a value. If there
is no boot.properties file configured, or the property does not exist, then the
default value is chosen.
456
Syntax
The configuration file is line-based, read as UTF-8 encoded, and defined by the
following grammar. 'nl' is a newline or end of file and 'text' is plain text
without newlines or the surrounding delimiters (such as parentheses or square
brackets):
457
nl: "\r\n" | "\n" | "\r"
path: text
propertyName: text
label: text
default: text
checksum: text
ivyPattern: text
artifactPattern: text
url: text
component: text
Notes
Here are some more docs that used to be part of Developer Guide.
Core Principles
This document details the core principles overarching sbt’s design and code style.
sbt’s core principles can be stated quite simply:
With these principles in mind, let’s walk through the core design of sbt.
This is the first piece you hit when starting sbt. sbt’s command engine is the
means by which it processes user requests using the build state. The command
engine is essentially a means of applying state transformations on the build
state, to execute user requests.
In sbt, commands are functions that take the current build state (sbt.State)
and produce the next state. In other words, they are essentially functions of
sbt.State => sbt.State. However, in reality, Commands are actually string
processors which take some string input and act on it, returning the next build
state.
So, the entirety of sbt is driven off the sbt.State class. Since this class needs
to be resilient in the face of custom code and plugins, it needs a mechanism
to store the state from any potential client. In dynamic languages, this can be
done directly on objects.
458
A naive approach in Scala is to use a Map<String,Any>. However, this violates
tenant #1: Everything should have a Type. So, sbt defines a new type of map
called an AttributeMap. An AttributeMap is a key-value storage mechanism
where keys are both strings and expected Types for their value.
Here is what the type-safe AttributeKey key looks like :
These keys store both a label (string) and some runtime type information
(manifest). To put or get something on the AttributeMap, we first need to con-
struct one of these keys. Let’s look at the basic definition of the AttributeMap:
trait AttributeMap {
/** Gets the value of type ``T`` associated with the key ``k`` or ``None`` if no value is
* If a key with the same label but a different type is defined, this method will return ``
def get[T](k: AttributeKey[T]): Option[T]
/** Adds the mapping ``k -> value`` to this map, replacing any existing mapping for ``k``.
* Any mappings for keys with the same label but different types are unaffected. */
def put[T](k: AttributeKey[T], value: T): AttributeMap
}
Now that there’s a definition of what build state is, there needs to be a way to
dynamically construct it. In sbt, this is done through the Setting[_] sequence.
Settings Architecture
459
the AttributeMap. This means overwriting an existing value at a key is as easy
as appending a Setting[_] to the end of the sequence which does so.
Where it gets interesting is that Initialize[T] can depend on other
AttributeKey[_]s in the build state. Each Initialize[_] can pull values
from any AttributeKey[_] in the build state’s AttributeMap to compute its
value. sbt ensures a few things when it comes to Initialize[_] dependencies:
normalizedName := normalize(name.value)
460
This represents the core mechanism of how to construct sbt’s build state. Con-
ceptually, at some point we have a graph of dependencies and initialization
functions which we can use to construct the first build state. Once this is com-
pleted, we can then start to process user requests.
Task Architecture
The next layer in sbt is around these user requests, or tasks. When a user
configures a build, they are defining a set of repeatable tasks that they can
run on their project. Things like compile or test. These tasks also have a
dependency graph, where e.g. the test task requires that compile has run
before it can successfully execute.
sbt defines a class Task[T]. The T type parameter represents the type of data
returned by a task. Remember the tenets of sbt? “All things have types” and
“Dependencies are explicit” both hold true for tasks. sbt promotes a style of
task dependencies that is closer to functional programming: return data for
your users rather than using shared mutable state.
Most build tools communicate over the filesystem, and indeed by necessity sbt
does some of this. However, for stable parallelization it is far better to keep
tasks isolated on the filesystem and communicate directly through types.
Similarly to how a Setting[_] stores both dependencies and an initialization
function, a Task[_] stores both its Task[_]dependencies and its behavior (a
function).
TODO - More on Task[_]
TODO - Transition into InputTask[_], rehash Command
TODO - Transition into Scope.
Settings Core
This page describes the core settings engine a bit. This may be useful for using it
outside of sbt. It may also be useful for understanding how sbt works internally.
The documentation is comprised of two parts. The first part shows an example
settings system built on top of the settings engine. The second part comments
on how sbt’s settings system is built on top of the settings engine. This may help
illuminate what exactly the core settings engine provides and what is needed to
build something like the sbt settings system.
Example
Setting up To run this example, first create a new project with the following
build.sbt file:
461
libraryDependencies += "org.scala-sbt" %% "collections" % sbtVersion.value
resolvers += sbtResolver.value
Example Settings System The first part of the example defines the custom
settings system. There are three main parts:
There is also a fourth, but its usage is likely to be specific to sbt at this time.
The example uses a trivial implementation for this part.
SettingsExample.scala:
import sbt._
462
}
// These three functions + a scope (here, Scope) are sufficient for defining our setting
}
Example Usage This part shows how to use the system we just defined. The
end result is a Settings[Scope] value. This type is basically a mapping Scope
-> AttributeKey[T] -> Option[T]. See the Settings API documentation for
details.
SettingsUsage.scala:
import sbt._
import SettingsExample._
import Types._
object SettingsUsage {
val b4 = ScopedKey(Scope(4), b)
463
// Show results.
for(i <- 0 to 5; k <- Seq(a, b)) {
println( k.label + i + " = " + applied.get( Scope(i), k) )
}
}
a0 = None
b0 = None
a1 = None
b1 = None
a2 = None
b2 = None
a3 = Some(3)
b3 = None
a4 = Some(3)
b4 = Some(9)
a5 = Some(4)
b5 = Some(9)
• For the None results, we never defined the value and there was no value
to delegate to.
• For a3, we explicitly defined it to be 3.
• a4 wasn’t defined, so it delegates to a3 according to our delegates func-
tion.
• b4 gets the value for a4 (which delegates to a3, so it is 3) and multiplies
by 3
• a5 is defined as the previous value of a5 + 1 and since no previous value
of a5 was defined, it delegates to a4, resulting in 3+1=4.
• b5 isn’t defined explicitly, so it delegates to b4 and is therefore equal to 9
as well
Scopes sbt defines a more complicated scope than the one shown here for the
standard usage of settings in a build. This scope has four components: the
project axis, the configuration axis, the task axis, and the extra axis. Each
component may be Zero (no specific value), This (current context), or Select
(containing a specific value). sbt resolves This_ to either Zero or Select depend-
ing on the context.
For example, in a project, a This project axis becomes a Select referring to the
defining project. All other axes that are This are translated to Zero. Functions
464
like inConfig and inTask transform This into a Select for a specific value. For
example, inConfig(Compile)(someSettings) translates the configuration axis
for all settings in someSettings to be Select(Compile) if the axis value is This.
So, from the example and from sbt’s scopes, you can see that the core settings
engine does not impose much on the structure of a scope. All it requires is a
delegates function Scope => Seq[Scope] and a display function. You can
choose a scope type that makes sense for your situation.
Constructing settings The app, value, update, and related methods are the
core methods for constructing settings. This example obviously looks rather dif-
ferent from sbt’s interface because these methods are not typically used directly,
but are wrapped in a higher-level abstraction.
With the core settings engine, you work with HLists to access other settings.
In sbt’s higher-level system, there are wrappers around HList for TupleN and
FunctionN for N = 1-9 (except Tuple1 isn’t actually used). When working
with arbitrary arity, it is useful to make these wrappers at the highest level
possible. This is because once wrappers are defined, code must be duplicated
for every N. By making the wrappers at the top-level, this requires only one
level of duplication.
Additionally, sbt uniformly integrates its task engine into the settings system.
The underlying settings engine has no notion of tasks. This is why sbt uses a
SettingKey type and a TaskKey type. Methods on an underlying TaskKey[T]
are basically translated to operating on an underlying SettingKey[Task[T]]
(and they both wrap an underlying AttributeKey).
For example, a := 3 for a SettingKey a will very roughly translate to setting(a,
value(3)). For a TaskKey a, it will roughly translate to setting(a, value(
task { 3 } ) ). See main/Structure.scala for details.
Setting Initialization
This page outlines the mechanisms by which sbt loads settings for a particular
build, including the hooks where users can control the ordering of everything.
As stated elsewhere, sbt constructs its initialization graph and task graph via
Setting[_] objects. A setting is something which can take the values stored at
465
other Keys in the build state, and generates a new value for a particular build
key. sbt converts all registered Setting[_] objects into a giant linear sequence
and compiles them into a task graph. This task graph is then used to execute
your build.
All of sbt’s loading semantics are contained within the Load.scala file. It is
approximately the following:
The blue circles represent actions happening when sbt loads a project. We can
see that sbt performs the following actions in load:
466
3. Load project *.sbt files (build.sbt and friends).
Each of these loads defines several sequences of settings. The diagram shows
the two most important:
or in a build.sbt file:
Controlling Initialization
The order which sbt uses to load settings is configurable at a project level.
This means that we can’t control the order of settings added to Build/Global
namespace, but we can control how each project loads, e.g. plugins and .sbt
files. To do so, use the AddSettings class:
467
import sbt._
import Keys._
import AddSettings._
The AddSettings object provides the following “groups” of settings you can use
for ordering:
• autoPlugins All the ordered settings of plugins after they’ve gone through
dependency resolution
• buildScalaFiles The full sequence of settings defined directly in
project/*.scala builds.
• sbtFiles(*) Specifies the exact setting DSL files to include (files must
use the .sbt file format)
• userSettings All the settings defined in the user directory ~/.sbt/<version>/.
• defaultSbtFiles Include all local *.sbt file settings.
For example, let’s see what happens if we move the build.sbt files before the
buildScalaFile.
Let’s create an example project the following definition. project/build.scala:
468
version := scalaBinaryVersion.value match {
case "2.10" => "1.0-SNAPSHOT"
case v => "1.0-for-${v}-SNAPSHOT"
}
)
}
This build defines a version string which appends the Scala version if the current
Scala version is not the in the 2.10.x series. Now, when issuing a release we
want to lock down the version. Most tools assume this can happen by writing
a version.sbt file. version.sbt:
version := "1.0.0"
However, when we load this new build, we find that the version in version.sbt
has been overridden by the one defined in project/Build.scala because of
the order we defined for settings, so the new version.sbt file has no effect.
Build Loaders
Build loaders are the means by which sbt resolves, builds, and transforms build
definitions. Each aspect of loading may be customized for special applications.
Customizations are specified by overriding the buildLoaders methods of your
build definition’s Build object. These customizations apply to external projects
loaded by the build, but not the (already loaded) Build in which they are defined.
Also documented on this page is how to manipulate inter-project dependencies
from a setting.
Custom Resolver
The resolver should return None if it cannot handle the URI or Some containing
a function that will retrieve the build. The ResolveInfo provides a staging
directory that can be used or the resolver can determine its own target directory.
Whichever is used, it should be returned by the loading function. A resolver is
registered by passing it to BuildLoader.resolve and overriding Build.buildLoaders
with the result:
469
...
object Demo extends Build {
...
override def buildLoaders =
BuildLoader.resolve(demoResolver) ::
Nil
• ResolveInfo
• BuildLoader
Full Example
import sbt._
import Keys._
470
// and so identical URIs will resolve to the same directory (as desired).
val base = RetrieveUnit.temporary(info.staging, info.uri)
// Construct a sample project on the fly with the name specified in the URI.
def resolveDemo(base: File, ssp: String): File =
{
// Only create the project if it hasn't already been created.
if(!base.exists)
IO.write(base / "build.sbt", template.format(ssp))
base
}
version := "1.0"
"""
}
Custom Builder
A builder returns None if it does not want to handle the build identified by
the BuildInfo. Otherwise, it provides a function that will load the build when
evaluated. Register a builder by passing it to BuildLoader.build and overriding
Build.buildLoaders with the result:
...
object Demo extends Build {
...
override def buildLoaders =
BuildLoader.build(demoBuilder) ::
Nil
471
API Documentation Relevant API documentation for custom builders:
• BuildInfo
• BuildLoader
• BuildUnit
val n = Project.normalizeProjectID(model.getName)
val base = Option(model.getProjectDirectory) getOrElse info.base
val root = Project(n, base) settings( pomSettings(model) )
val build = new Build { override def projects = Seq(root) }
val loader = this.getClass.getClassLoader
val definitions = new LoadedDefinitions(info.base, Nil, loader, build :: Nil, Nil)
val plugins = new LoadedPlugins(info.base / "project", Nil, loader, Nil, Nil)
new BuildUnit(info.uri, info.base, definitions, plugins)
}
472
Custom Transformer
...
object Demo extends Build {
...
override def buildLoaders =
BuildLoader.transform(demoTransformer) ::
Nil
• TransformInfo
• BuildLoader
• BuildUnit
473
classpath dependencies, a dependency has type ClasspathDep[ProjectRef],
which combines a ProjectRef with a configuration (see ClasspathDep and
ProjectRef). For aggregate dependencies, the type of a dependency is just
ProjectRef.
The API for BuildDependencies is not extensive, covering only a little more
than the minimum required, and related APIs have more of an internal, unpol-
ished feel. Most manipulations consist of modifying the relevant map (classpath
or aggregate) manually and creating a new BuildDependencies instance.
buildDependencies in Global := {
val deps = (buildDependencies in Global).value
val oldURI = uri("...") // the URI to replace
val newURI = uri("...") // the URI replacing oldURI
def substitute(dep: ClasspathDep[ProjectRef]): ClasspathDep[ProjectRef] =
if(dep.project.build == oldURI)
ResolvedClasspathDependency(ProjectRef(newURI, dep.project.project), dep.configuration
else
dep
val newcp =
for( (proj, deps) <- deps.cp) yield
(proj, deps map substitute)
BuildDependencies(newcp, deps.aggregate)
}
474
Hello World Example
1. build.sbt
2. Main.scala
3. hello.build.properties
Like for sbt itself, you can specify commands from the command line (batch
mode) or run them at an prompt (interactive mode).
Build Definition: build.sbt The build.sbt file should define the standard
settings: name, version, and organization. To use the sbt command system, a
dependency on the command module is needed. To use the task system, add a
dependency on the task-system module as well.
organization := "org.example"
name := "hello"
version := "0.1-SNAPSHOT"
1. Provide command definitions. These are the commands that are available
for users to run.
2. Define initial commands. These are the commands that are initially sched-
uled to run. For example, an application will typically add anything speci-
fied on the command line (what sbt calls batch mode) and if no commands
are defined, enter interactive mode by running the ‘shell’ command.
3. Set up logging. The default setup in the example rotates the log file after
each user interaction and sends brief logging to the console and verbose
logging to the log file.
475
package org.example
import sbt._
import java.io.{File, PrintWriter}
/** Sets up the application by constructing an initial State instance with the supported
* and initial commands to run. See the State API documentation for details. */
def initialState(configuration: xsbti.AppConfiguration): State =
{
val commandDefinitions = hello +: BasicCommands.allBasicCommands
val commandsToRun = Hello +: "iflast shell" +: configuration.arguments.map(_.trim)
State( configuration, commandDefinitions, Set.empty, None, commandsToRun, State.newHis
AttributeMap.empty, initialGlobalLogging, State.Continue )
}
/** Configures logging to log to a temporary backing file as well as to the console.
* An application would need to do more here to customize the logging level and
* provide access to the backing file (like sbt's last command and logLevel setting).*/
def initialGlobalLogging: GlobalLogging =
GlobalLogging.initial(MainLogging.globalDefault _, File.createTempFile("hello", "log")
}
[scala]
version: 2.9.1
[app]
476
org: org.example
name: hello
version: 0.1-SNAPSHOT
class: org.example.Main
components: xsbti
cross-versioned: true
[repositories]
local
maven-central
typesafe-ivy-releases: http://repo.typesafe.com/typesafe/ivy-releases/, [organization]/[mod
Nightly Builds
The latest development versions of 1.2.7 are available as nightly builds on Type-
safe Snapshots.
To use a nightly build, the instructions are the same for normal manual setup
except:
out: Archive.html
477
Archived pages
Hello, World
If you’re using sbt 0.13.13 or later, you can use sbt new command to quickly
setup a simple Hello world build. Type the following command to the terminal.
Now from inside the hello directory, start sbt and type run at the sbt shell.
On Linux or OS X the commands might look like this:
$ cd hello
$ sbt
...
> run
...
[info] Compiling 1 Scala source to /xxx/hello/target/scala-2.12/classes...
[info] Running example.Hello
hello
To leave sbt shell, type exit or use Ctrl+D (Unix) or Ctrl+Z (Windows).
> exit
478
Build definition
The build definition goes in a file called build.sbt, located in the project’s base
directory. You can take a look at the file, but don’t worry if the details of this
build file aren’t clear yet. In .sbt build definition you’ll learn more about how
to write a build.sbt file.
479