Apr 22

Auto generate class file with build info for play framework project

Tapping into your build system to automatically generate source code that includes build information (version, date, dependencies) is not only smart, it should be the standard. For Maven-based Java projects, I've long had a technique I use in every project by defining it in a standard parent pom.xml file used in every project. For Play framework's SBT-based build system, there fortunately is a plugin (sbt-buildinfo) which can accomplish the same result with just a few simple tweaks.

Add the sbt-buildinfo plugin to your project/play.plugins file:

addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.2.5")

Add this import statement to the top of your project/Build.scala file:

import sbtbuildinfo.Plugin._
Important Failing to include the import statement will result in an SBT error such as: [error] /project/Build.scala:24: not found: value buildInfoSettings: _*

At this point I start to diverge from the standard instructions for sbt-buildinfo. Their instructions are mainly targeted at SBT users while mine are targeted at Play framework users. In a standard project/Build.scala file, this is usually what the assignment of val main looks like after creating a new play project:

val main = play.Project(appName, appVersion, appDependencies).settings(
  // settings go here
)

I prefer to make two key changes to trigger auto generation of a build info class that contains the application version, build date, scala version, and build time in milliseconds. First, we'll merge sbt-buildinfo's settings. Second, we'll override them with our settings. The new assignment of val main now looks like:

val main = play.Project(appName, appVersion, appDependencies).settings(
  buildInfoSettings: _*
).settings(
  sourceGenerators in Compile <+= buildInfo,
  buildInfoPackage := "com.mfizz.sample",
  buildInfoKeys ++= Seq[BuildInfoKey] (
    "builtAt" -> {
      val dtf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
      dtf.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
      dtf.format(new java.util.Date())
    },
    "builtAtMillis" -> { System.currentTimeMillis() }
  )
)

Go ahead and run play compile to test if your setup works. Once compilation successfully completes, based on the example above, a BuildInfo.scala file should exist here:

target/scala-2.10/src_managed/main/sbt-buildinfo/BuildInfo.scala

If you modify appVersion in project/Build.scala and run play compile another time, you'll notice that BuildInfo.scala will reflect that change. You can now use that class in the rest of your project. For example, you can now access the version in Java like so:

String ver = com.mfizz.sample.BuildInfo.version;

All of the class properties are accessed like static properties from Java. Our custom tweaks also expose a builtAt property that is the build compilation date in UTC "2013-04-21 01:07:33" and builtAtMillis which is the saved long from System.currentTimeMillis() at compile time.

The BuildInfo.scala will look like this:

package com.mfizz.sample

case object BuildInfo {
  val name = "play-sample"
  val version = "1.0-SNAPSHOT"
  val scalaVersion = "2.10.0"
  val sbtVersion = "0.12.2"
  val builtAt = "2013-04-13 01:07:33"
  val builtAtMillis = 1373677653249L
}
Don't use the sbt-buildinfo BuildInfoKey.action() feature with Play projects. It'll create a never-ending loop during development runs where play thinks it needs to keep re-compiling and restarting itself as the BuildInfo.scala file automatically changes.

Your published jars or tarballs will now contain a compiled class that will forever contain the version and build date. These can be useful to access for numerous reasons -- they have come in handy in countless situations over the years.

Updates? Need assistance?

Follow @fizzed_inc on Twitter for future updates and latest news.

If you have specific issues, questions, or problems, please contact us with your inquiry or consulting request.

Tags

Archives