<img height="1" width="1" src="https://www.facebook.com/tr?id=1076094119157733&amp;ev=PageView &amp;noscript=1">

Managing Library Dependencies in SBT Builds

Posted by Carl Pulley on Tue, Jul 11, 2017

In this post we look at how we can use SBT's source generators to centrally manage library dependencies across a recursive SBT build. Our aim is to have a single location where library dependencies can be managed and to then have SBT plugin code, build code and source code be able to access the centrally managed ModuleIDs.

Let us imagine that we have a single module SBT project. Clearly we wish to ensure that code within this project is written and layed out to a consistent standard. Likewise, we may wish to ensure that a variety of common Scala style issues are avoided and so wish to include static analysis via Scala compiler options, Wartremover or similar.

In SBT projects we often achieve this by adding in project plugins and enabling them. These plugins are specified using an SBT ModuleID in a project/plugins.sbt file.

The source code that our SBT project manages and builds will itself require the use of libraries. These are typically specified using SBT ModuleID's in the libraryDependencies settings key of a build.sbt file.

As projects grow, library dependencies can easily become spread over multiple files - and so the maintenance nightmare starts!

Centralisation is the key to such problems. However, SBT is a recursive build tool and so the library dependencies present in project/plugins.sbt are present at a different build stage to those present in build.sbt - and so any efforts to centrally manage all dependencies are impeded!

When developing the SBT code behind sbt-cake, we hit this issue and needed to solve it. As sbt-cake is an SBT auto plugin, we found that we needed to be able to access library dependencies (i.e. ModuleIDs) within 3 contexts:

  • project/plugin.sbt
  • build.sbt
  • and within the src/ directory - we wanted to provide platform groupings of library dependencies with standard exclusions applied (e.g. Netty and Slf4j messes disentangled).

Library Dependency Objects

As a first step, we first factored all library dependencies into a common Scala object named CakePlatformDependencies and copy and pasted this file into three key directories:

  • project/project/CakePlatformDependencies.scala - allowing project/plugins.sbt to be able to use this file in the first build stage
  • project/CakePlatformDependencies.scala - allowing build.sbt to be able to use this file in the second build stage
  • src/main/scala/CakePlatformDependencies.scala - allowing src/ code to be able to use this file in the runtime.

At first blush, this looks like we've made the situation worse - and we have! However, in doing this, we now have the beginings of a strategy for solving this problem. Well, we will do if we can work out how to ensure that SBT can copy the project/project/CakePlatformDependencies.scala file during each of its recursive build steps!

Source Managed Library Dependency Objects

The trick to getting SBT to copy the project/project/CakePlatformDependencies.scala file across recursive build steps is to leverage the power of Scala source generators. For example, to copy the file from the plugin to the build stages, we can use the following SBT code (placed in project/plugins.sbt):

sourceGenerators in Compile += Def.task {
  val deps = (baseDirectory in Compile).value / "project" / "CakePlatformDependencies.scala"
  val projectDeps = (sourceManaged in Compile).value / "CakePlatformDependencies.scala"

  IO.copyFile(deps, projectDeps)

  Seq(projectDeps)
}.taskValue

In the build.sbt file, the members of CakePlatformDependencies can now be accessed via the following import statement:

import net.cakesolutions.CakePlatformDependencies._

In a similar manner, we can ensure that CakePlatformDependencies is made available to src/ project code by using a similar Scala source generator in the build.sbt file.

Conclusion

In this post, we have seen how we may centrally manage library dependencies across the different stages of a recursive SBT build using Scala source generators. If you are interested in seeing the source code behind all this, please have a look the following sbt-cake files:

Topics: Scala, Sbt

Posts by Topic

see all

Subscribe to Email Updates