Excluding Components from an Aggregate SBT Build

Posted by Matt Farmer on March 07, 2015 · 4 mins read

While working on upgrading Databinder Dispatch’s liftjson module to use Lift 2.6 I ran into an interesting challenge that Nathan, the project’s maintainer, hadn’t found a good solution to yet. Specifically, here’s the situation: Dispatch is built for a bunch of Scala versions. The liftjson module won’t build on Scala 2.9.3 because we (the Lift Committers) never issued a build of Lift for 2.9.3.

First, a bit of background. Dispatch is actually a collection of a few different projects and uses sbt’s aggregate feature to group them all together. This is nice because any command you issue to the aggregate project (named “dispatch-all” in this case) is executed on all the projects that make up the aggregate. This works great because you can do things like type “+publish” and have all versions of all modules published. Until you’re in the very situation that I was in where one component of the aggregate won’t build for certain versions.

So, I started wondering what sbt might have to offer me if I need to disable building a component of a larger aggregate only for specific versions of Scala. Lo and behold, sbt does have a skip property that allows you to bail on the compile phase. As with most things involving sbt this took a bit of beating my head against the problem before I came up with this Magical Incantation of Building™ that allows us to skip the compile phase only for Scala 2.9.3!

Overjoyed I decided to start tinkering around and realized pretty quickly that something was wrong.

It seems that while trying to build the other modules for 2.9.3, the liftjson project was not being exempted from the update phase where sbt tries to resolve all of the dependencies of the project. This was, of course, an issue since there was no 2.9.3 build to find. And, as I found out the hard way, you can’t skip the update phase or sbt just bails on the entire task with an error. So what are we to do?


After some finagling, I settled on this:

The above tells sbt that when you’re trying to find a lift-json build, if you’re looking for 2.9.3 just use the build for 2.9.1. Now, 2.9.3 and 2.9.1 are not binary compatible. You should never do this if you’re actually going to use the library in question. But as you can see in the comment I made above the case statement, the entire purpose of this is to just make sbt happy. It will never actually get used in an runtime environment so we can completely ignore the fact that this probably wouldn’t work.

And verily, I had a version of Dispatch’s Lift-backed JSON module compiling and the tests would pass. (There was some other work around converting the tests to Mockito that happened, too, but that’s for a different blog post.) Belated, I decided to do what every victory lap of sbt involves: publishLocal to see it in action.

Then tears followed.

Despite my instructions not to compile the module for 2.9.3 – sbt was still publishing the module for 2.9.3. Insert rage flipping of tables here. That said, this fixed proved to be much easier to come by than the first. We just need to disable the publishArtifact property when the Scala version is set to 2.9.3. So, we do that like so:

And with that we no longer get the publishing for 2.9.3, but it continues to publish as expected for all the other Scala versions that this module will support. So, all together now, here’s what the combination of instructions to skip a build of something based on a particular version (in this case 2.9.3) looks like:

Hopefully this will save someone a lot of time in the future. ? If you’re interested you can view the entire PR on Dispatch where this took place!

Until next time folks.