sbt 0.13.11 by default suppresses most stack traces and debugging
information. It has the nice side effect of giving you less noise on
screen, but as a newcomer it can leave you lost for explanation. To see
the previous output of a command at a higher verbosity, type
last <task>
where <task>
is the task that failed or that you want to
view detailed output for. For example, if you find that your update
fails to load all the dependencies as you expect you can enter:
> last update
and it will display the full output from the last run of the update
command.
Sometimes sbt doesn’t detect that ansi codes aren’t supported and you get output that looks like:
[0m[ [0minfo [0m] [0mSet current project to root
or ansi codes are supported but you want to disable colored output. To
completely disable ansi codes, set the sbt.log.format
system property
to false
. For example,
You may run sbt console
.
:=
, +=
, 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 and
more kinds of setting for
example.
%
method? It’s used to create a ModuleID
from strings, when specifying managed
dependencies. Read the Getting Started Guide about
library dependencies.
ModuleID
, Project
, …? To figure out an unknown type or method, have a look at the Getting Started Guide if you have not. Also try the index of commonly used methods, values, and types, the API Documentation and the hyperlinked sources.
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:
mappings in (Compile, packageSrc) ++= {
import Path.{flat, relativeTo}
val base = (sourceManaged in Compile).value
val srcs = (managedSources in Compile).value
srcs x (relativeTo(base) | flat)
}
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.
See Generating Files.
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:
// define a task that takes some inputs
// and generates files in an output directory
myTask := {
// wraps a function taskImpl in an uptodate check
// taskImpl takes the input files, the output directory,
// generates the output files and returns the set of generated files
val cachedFun = FileFunction.cached(cacheDirectory.value / "my-task") { (in: Set[File]) =>
taskImpl(in, target.value) : Set[File]
}
// Applies the cached function to the inputs files
cachedFun(inputs.value)
}
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.
The following example demonstrates adding a new set of compilation
settings and tasks to a new configuration called samples
. The sources
for this configuration go in src/samples/scala/
. Unspecified settings
delegate to those defined for the compile
configuration. For example,
if scalacOptions
are not overridden for samples
, the options for the
main sources are used.
Options specific to samples
may be declared like:
scalacOptions in Samples += "-deprecation"
This uses the main options as base options because of +=
. Use :=
to
ignore the main options:
scalacOptions in Samples := "-deprecation" :: Nil
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
...
See the Additional test configurations section of Testing.
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:
lazy val myRunTask = taskKey[Unit]("A custom run task.")
// this can go either in a `build.sbt` or the settings member
// of a Project in a full configuration
fullRunTask(myRunTask, Test, "foo.Foo", "arg1", "arg2")
If you want to be able to supply arguments on the command line, replace
TaskKey
with InputKey
and fullRunTask
with fullRunInputTask
. The
Test
part can be replaced with another configuration, such as
Compile
, to use that configuration’s classpath.
This run task can be configured individually by specifying the task key in the scope. For example:
fork in myRunTask := true
javaOptions in myRunTask += "-Xmx6144m"
Tool dependencies are used to implement a task and are not needed by project source code. These dependencies can be declared in their own configuration and classpaths. These are the steps:
update
.
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:
val ProguardConfig = config("proguard") hide
ivyConfigurations += ProguardConfig
Then,
// Add proguard as a dependency in the custom configuration.
// This keeps it separate from project dependencies.
libraryDependencies +=
"net.sf.proguard" % "proguard" % "4.4" % ProguardConfig.name
// Extract the dependencies from the UpdateReport.
managedClasspath in proguard := {
// these are the types of artifacts to include
val artifactTypes: Set[String] = (classpathTypes in proguard).value
Classpaths.managedJars(proguardConfig, artifactTypes, update.value)
}
// Use the dependencies in a task, typically by putting them
// in a ClassLoader and reflectively calling an appropriate
// method.
proguard := {
val cp: Seq[File] = (managedClasspath in proguard).value
// ... do something with , which includes proguard ...
}
Defining the intermediate classpath is optional, but it can be useful
for debugging 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: Seq[File] =
Classpaths.managedJars(proguardConfig, artifactTypes, update.value)
// ... do something with , which includes proguard ...
}
It is possible to register additional jars that will be placed on sbt’s
classpath (since version 0.10.1). Through
State, it is possible to obtain a
xsbti.ComponentProvider, which
manages application components. Components are groups of files in the
~/.sbt/boot/
directory and, in this case, the application is sbt. In
addition to the base classpath, components in the “extra” component are
included on sbt’s classpath.
(Note: the additional components on an application’s classpath are
declared by the components
property in the [main]
section of the
launcher configuration file boot.properties
.)
Because these components are added to the ~/.sbt/boot/
directory and
~/.sbt/boot/
may be read-only, this can fail. In this case, the user
has generally intentionally set sbt up this way, so error recovery is
not typically necessary (just a short error message explaining the
situation.)
The following code can be used where a State => State
is required,
such as in the onLoad
setting (described below) or in a
command. It adds some files to the “extra”
component and reloads sbt if they were not already added. Note that
reloading will drop the user’s session state.
def augment(extra: Seq[File])(s: State): State = {
// Get the component provider
val cs: xsbti.ComponentProvider = s.configuration.provider.components()
// Adds the files in 'extra' to the "extra" component
// under an exclusive machine-wide lock.
// The returned value is 'true' if files were actually copied and 'false'
// if the target files already exists (based on name only).
val copied: Boolean = s.locked(cs.lockFile, cs.addToComponent("extra", extra.toArray))
// If files were copied, reload so that we use the new classpath.
if(copied) s.reload else s
}
The single, global setting onLoad
is of type State => State
(see
State and Actions) 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
:
// Compose our new function 'f' with the existing transformation.
{
val f: State => State = ...
onLoad in Global := {
val previous = (onLoad in Global).value
f compose previous
}
}
The following example maintains a count of the number of times a project has been loaded and prints that number:
{
// 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
}
}
Setting initializers are executed in order. If the initialization of a setting depends on other settings that has not been initialized, sbt will stop loading.
In this example, we try to append a library to libraryDependencies
before it is initialized with an empty sequence.
object MyBuild extends Build {
val root = Project(id = "root", base = file("."),
settings = Seq(
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
)
).disablePlugins(plugins.IvyModule)
}
To correct this, include the IvyModule plugin settings, which includes
libraryDependencies := Seq()
. So, we just drop the explicit disabling.
object MyBuild extends Build {
val root = Project(id = "root", base = file("."),
settings = Seq(
libraryDependencies += "commons-io" % "commons-io" % "1.4" % "test"
)
)
}
A more subtle variation of this error occurs when using scoped settings.
// error: Reference to uninitialized setting
settings = Seq(
libraryDependencies += "commons-io" % "commons-io" % "1.2" % "test",
fullClasspath := fullClasspath.value.filterNot(_.data.name.contains("commons-io"))
)
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.
fullClasspath in Compile := (fullClasspath in Compile).value.filterNot(_.data.name.contains("commons-io"))
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:
[warn] problem while downloading module descriptor:
https://repo1.maven.org/maven2/commons-fileupload/commons-fileupload/1.2.2/commons-fileupload-1.2.2.pom:
invalid sha1: expected=ad3fda4adc95eb0d061341228cc94845ddb9a6fe computed=0ce5d4a03b07c8b00ab60252e5cacdc708a4e6d8 (1070ms)
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:
checksums in update := Nil
See library management for details.
This problem crops up frequently. Plugins are only published for the Scala version that sbt uses (currently, 2.9.1). You can still use plugins during cross-compilation, because sbt only looks for a 2.9.1 version of the plugin.
… unless you specify the plugin in the wrong place!
A typical mistake is to put global plugin definitions in
~/.sbt/plugins.sbt
. THIS IS WRONG. .sbt
files in ~/.sbt
are
loaded for each build—that is, for each cross-compilation. So, if
you build for Scala 2.9.0, sbt will try to find a version of the plugin
that’s compiled for 2.9.0—and it usually won’t. That’s because it
doesn’t know the dependency is a plugin.
To tell sbt that the dependency is an sbt plugin, make sure you define
your global plugins in a .sbt
file in ~/.sbt/plugins/
. sbt knows
that files in ~/.sbt/plugins
are only to be used by sbt itself, not as
part of the general build definition. If you define your plugins in a
file under that directory, they won’t foul up your cross-compilations.
Any file name ending in .sbt
will do, but most people use
~/.sbt/plugins/build.sbt
or ~/.sbt/plugins/plugins.sbt
.
sbt runs tests in the same JVM as sbt itself and Scala classes are not in the same class loader as the application classes. Therefore, when using the Scala interpreter, it is important to set it up properly to avoid an error message like:
Failed to initialize compiler: class scala.runtime.VolatileBooleanRef not found.
** Note that as of 2.8 scala does not assume use of the java classpath.
** For the old behavior pass -usejavacp to scala, or if using a Settings
** object programmatically, settings.usejavacp.value = true.
The key is to initialize the Settings for the interpreter using embeddedDefaults. For example:
val settings = new Settings
settings.embeddedDefaults[MyType]
val interpreter = new Interpreter(settings, ...)
Here, MyType is a representative class that should be included on the interpreter’s classpath and in its application class loader. For more background, see the original proposal that resulted in embeddedDefaults being added.
Similarly, use a representative class as the type argument when using the break and breakIf methods of ILoop, as in the following example:
def x(a: Int, b: Int) = {
import scala.tools.nsc.interpreter.ILoop
ILoop.breakIf[MyType](a != b, "a" -> a, "b" -> b )
}
See the migration page first and then the following questions.
lib_managed
gone? By default, sbt 0.13.11 loads managed libraries from your ivy cache
without copying them to a lib_managed
directory. This fixes some bugs
with the previous solution and keeps your project directory small. If
you want to insulate your builds from the ivy cache being cleared, set
retrieveManaged := true
and the dependencies will be copied to
lib_managed
as a build-local cache (while avoiding the issues of
lib_managed
in 0.7.x).
This does mean that existing solutions for sharing libraries with your favoured IDE may not work. Refer to Community Plugins page for a list of currently available plugins for your IDE.
For a list of commands, run help
. For details on a specific command,
run help <command>
. To view a list of tasks defined on the current
project, run tasks
. Alternatively, see the
Running page in the Getting Started Guide
for descriptions of common commands and tasks.
If in doubt start by just trying the old command as it may just work. The built in TAB completion will also assist you, so you can just press TAB at the beginning of a line and see what you get.
The following commands work pretty much as in 0.7 out of the box:
reload
update
compile
test
testOnly
publishLocal
exit
sbt 0.10 fixes a flaw in how dependencies get resolved in multi-module projects. This change ensures that only one version of a library appears on a classpath.
Use last update
to view the debugging output for the last update
run. Use show update
to view a summary of files comprising managed
classpaths.
Be aware that compilation and tests run in parallel by default in sbt
0.13.11. If your test code isn’t thread-safe then you may want to
change this behaviour by adding one of the following to your
build.sbt
:
// Execute tests in the current project serially.
// Tests from other projects may still run concurrently.
parallelExecution in Test := false
// Execute everything serially (including compilation and tests)
parallelExecution := false
Web application support was split out into a plugin. See the xsbt-web-plugin project.
For an early version of an xsbt Web Start plugin, visit the xsbt-webstart project.
In 0.13.11, there are three types of project dependencies (classpath, execution, and configuration) and they are independently defined. These were combined in a single dependency type in 0.7.x. A declaration like:
lazy val a = project("a", "A")
lazy val b = project("b", "B", a)
meant that the B
project had a classpath and execution dependency on
A
and A
had a configuration dependency on B
. Specifically, in
0.7.x:
A
were available on the appropriate
classpath for B.
B
would be executed on A
first.
In 0.13.11, declare the specific type of dependency you want. Read about multi-project builds in the Getting Started Guide for details.
0.7 | 0.13.11 |
---|---|
FileUtilities | IO |
Path class and object | Path object, File, RichFile |
PathFinder class | Seq[File], PathFinder class, PathFinder object |
See Community Plugins for a list of currently available plugins.