The Power of Programmatic Builds

Using a build tool that provides the full power of a real programming language makes many tasks much easier than build tools that use a declarative markup language. To cut to the chase, I’m referring to sbt vs Maven (or Ant). Just wanted to write a quick post that shows how we have customized our sbt builds in two important ways: using different versions of dependencies based on which version of Scala we’re using, and publishing to different snapshot and release repositories depending on the project’s current version.

In the land of Scala, it’s common to publish library jars compiled with multiple versions of Scala (long story). So sbt supports cross-building. In this project’s build.properties file, we specify to compile it with both Scala 2.9.1 and 2.8.1:

build.scala.versions=2.9.1 2.8.1

That’s great and all, but one of the dependencies we want to use, Specs, only goes up to version 1.6.8 with Scala 2.8.1, then switches to version 1.6.9 for Scala 2.9.1. Might seem like a problem, but because we define our build in Scala, we just throw in an if-statement:

class Project(info: ProjectInfo) extends DefaultProject(info) {
  //specs version depends on Scala version
  val specsVersion = if ("2.9.1" == buildScalaVersion) "1.6.9" else "1.6.8"
  val specs = "org.scala-tools.testing" %% "specs" % specsVersion % "test"
}

Run +update on that and you’ll get Specs 1.6.8 for Scala 2.8.1 and Specs 1.6.9 for Scala 2.9.1.

It’s standard practice to use separate Maven repositories for snapshot versions and release versions. Case in point: scala-tools snapshot & release repos. When your project is at version 0.9-SNAPSHOT you should publish its jars to the snapshot repo. Then when version 0.9 is released, publish to the release repo.

Again, because we’re defining the build in Scala, we just do some programming to set this up exactly how we want it:

class Project(info: ProjectInfo) extends DefaultProject(info) {
  //publish destination depends on build version
  override def managedStyle = ManagedStyle.Maven
  def suffix = if (version.toString endsWith "-SNAPSHOT") "snapshots/" else "releases/"
  val publishTo = "Scala Tools Nexus" at "http://nexus.scala-tools.org/content/repositories/" + suffix
  Credentials(Path.userHome / ".ivy2" / ".scala_tools_credentials", log)
}

Pretty simple examples, but the point is that we’re not locked in to the XML elements & attributes that some Maven plugin author exposed to us. With sbt we can do basically whatever we need to in the build file to customize our build to meet project requirements.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s