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:
lib
directory
Most people use managed dependencies instead of unmanaged. But unmanaged can be simpler when starting out.
Unmanaged dependencies work like this: add jars to lib
and they will be
placed on the project classpath. Not much else to it!
You can place test jars such as
ScalaCheck,
Specs2, and
ScalaTest in lib
as well.
Dependencies in lib
go on all the classpaths (for compile
, test
, run
,
and console
). If you wanted to change the classpath for just one of
those, you would adjust Compile / dependencyClasspath
or
Runtime / dependencyClasspath
for example.
There’s nothing to add to build.sbt
to use unmanaged dependencies,
though you could change the unmanagedBase
key if you’d like to use a
different directory rather than lib
.
To use custom_lib
instead of lib
:
unmanagedBase := baseDirectory.value / "custom_lib"
baseDirectory
is the project’s root directory, so here you’re changing
unmanagedBase
depending on baseDirectory
using the special value
method
as explained in task graph.
There’s also an unmanagedJars
task which lists the jars from the
unmanagedBase
directory. If you wanted to use multiple directories or do
something else complex, you might need to replace the whole
unmanagedJars
task with one that does something else, e.g. empty the list for
Compile
configuration regardless of the files in lib
directory:
Compile / unmanagedJars := Seq.empty[sbt.Attributed[java.io.File]]
sbt uses Coursier to implement managed dependencies, so if you’re familiar with Coursier, Apache Ivy or Maven, you won’t have much trouble.
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 dependencies, 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:
libraryDependencies += groupID % artifactID % revision
or like this, where configuration
can be a string or a Configuration
value (such as Test
):
libraryDependencies += groupID % artifactID % revision % configuration
libraryDependencies
is declared in
Keys like
this:
val libraryDependencies = settingKey[Seq[ModuleID]]("Declares managed dependencies.")
The %
methods create ModuleID
objects from strings, then you add those
ModuleID
to libraryDependencies
.
Of course, sbt (via Coursier) 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:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3"
If you type that in build.sbt
and then update
, sbt should download Derby
to the Coursier cache. (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:
libraryDependencies ++= Seq(
groupID % artifactID % revision,
groupID % otherID % otherRevision
)
In rare cases you might find reasons to use :=
with libraryDependencies
as well.
%%
If you use organization %% moduleName % version
rather than
organization % moduleName % version
(the difference is the double %%
after
the organization
), sbt will add your project’s binary Scala version to the artifact
name. This is just a shortcut. You could write this without the %%
:
libraryDependencies += "org.scala-stm" % "scala-stm_2.13" % "0.9.1"
Assuming the scalaVersion
for your build is 2.13.12
, the following is
identical (note the double %%
after "org.scala-stm"
):
libraryDependencies += "org.scala-stm" %% "scala-stm" % "0.9.1"
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 compatibility.
See Cross Building for some more detail on this.
The version
in organization % moduleName % version
does not have to be a
single fixed version. 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.
Occasionally a Maven “version range” is used to specify a dependency
(transitive or otherwise), such as [1.3.0,)
. If a specific version
of the dependency is declared in the build, and it satisfies the
range, then sbt will use the specified version. Otherwise, Coursier could
go out to the Internet to find the latest version. This would result
to a surprising behavior where the effective version keeps changing
over time, even though there’s a specified version of the library that
satisfies the range condition.
Maven version ranges will be replaced with its lower bound if the
build so that when a satisfactory version is found in the dependency
graph it will be used. You can disable this behavior using the JVM
flag -Dsbt.modversionrange=false
.
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
resolvers += name at location
with the special at
between two strings.
For example:
resolvers += "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"
The resolvers
key is defined in
Keys like this:
val resolvers = settingKey[Seq[Resolver]]("The user-defined additional resolvers for automatically managed dependencies.")
The at
method creates a Resolver
object from two strings.
sbt can search your local Maven repository if you add it as a repository:
resolvers += "Local Maven Repository" at "file://"+Path.userHome.absolutePath+"/.m2/repository"
or, for convenience:
resolvers += Resolver.mavenLocal
See Resolvers for details on defining other types of repositories.
resolvers
does not contain the default resolvers; 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
.
Often a dependency is used by your test code (in src/test/scala
, which
is compiled by the Test
configuration) but not your main code.
If you want a dependency to show up in the classpath only for the Test
configuration and not the Compile
configuration, add % "test"
like this:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % "test"
You may also use the type-safe version of Test
configuration as follows:
libraryDependencies += "org.apache.derby" % "derby" % "10.4.1.3" % Test
Now, if you type show Compile/dependencyClasspath
at the sbt interactive
prompt, you should not see the derby jar. But if you type
show Test/dependencyClasspath
, you should see the derby jar in the list.
Typically, test-related dependencies such as
ScalaCheck,
Specs2, and
ScalaTest would be defined with % "test"
.
There are more details and tips-and-tricks related to library dependencies on this page.