How to share version values between project/plugins.sbt and project/Build.scala?

I would like to share a common version variable between an sbtPlugin and the rest of the build

Here is what I am trying:

in project/Build.scala:

object Versions {
  scalaJs = "0.5.0-M3"
}
object MyBuild extends Build {
  //Use version number
}

in plugins.sbt:

addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % Versions.scalaJs)

results in

plugins.sbt:15: error: not found: value Versions
addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % Versions.scalaJs)

Is there a way to share the version number specification between plugins.sbt and the rest of the build, e.g. project/Build.scala?


sbt-buildinfo

If you need to share version number between build.sbt and hello.scala, what would you normally do? I don't know about you, but I would use sbt-buildinfo that I wrote.

This can be configured using buildInfoKeys setting to expose arbitrary key values like version or some custom String value. I understand this is not exactly what you're asking but bear with me.

meta-build (turtles all the way down)

As Jacek noted and stated in Getting Started Guide, the build in sbt is a project defined in the build located in project directory one level down. To distinguish the builds, let's define the normal build as the proper build, and the build that defines the proper build as meta-build. For example, we can say that an sbt plugin is a library of the root project in the meta build.

Now let's get back to your question. How can we share info between project/Build.scala and project/plugins.sbt?

using sbt-buildinfo for meta-build

We can just define another level of build by creating project/project and add sbt-buildinfo to the (meta-)meta-build.

Here are the files.

In project/project/buildinfo.sbt:

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

In project/project/Dependencies.scala:

package metabuild

object Dependencies {
  def scalaJsVersion = "0.5.0-M2"
}

In project/build.properties:

sbt.version=0.13.5

In project/buildinfo.sbt:

import metabuild.Dependencies._

buildInfoSettings

sourceGenerators in Compile <+= buildInfo

buildInfoKeys := Seq[BuildInfoKey]("scalaJsVersion" -> scalaJsVersion)

buildInfoPackage := "metabuild"

In project/scalajs.sbt:

import metabuild.Dependencies._

addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % scalaJsVersion)

In project/Build.scala:

import sbt._
import Keys._
import metabuild.BuildInfo._

object Builds extends Build {
  println(s"test: $scalaJsVersion")
}

So there's a bit of a boilerplate in project/buildinfo.sbt, but the version info is shared across the build definition and the plugin declaration.

If you're curious where BuildInfo is defined, peek into project/target/scala-2.10/sbt-0.13/src_managed/.


For the project/plugins.sbt file you'd have to have another project under project with the Versions.scala file. That would make the definition of Versions.scalaJs visible.

The reason for doing it is that *.sbt files belong to a project build definition at the current level with *.scala files under project to expand on it. And it's...turtles all the way down, i.e. sbt is recursive.

I'm not sure how much the following can help, but it might be worth to try out - to share versions between projects - plugins and the main one - you'd have to use ProjectRef as described in the answer to RootProject and ProjectRef:

When you want to include other, separate builds directly instead of using their published binaries, you use "source dependencies". This is what RootProject and ProjectRef declare. ProjectRef is the most general: you specify the location of the build (a URI) and the ID of the project in the build (a String) that you want to depend on. RootProject is a convenience that selects the root project for the build at the URI you specify.


My proposal is to hack. For example, in build.sbt you can add a task:

val readPluginSbt = taskKey[String]("Read plugins.sbt file.")

readPluginSbt := {
        val lineIterator = scala.io.Source.fromFile(new java.io.File("project","plugins.sbt")).getLines
            val linesWithValIterator = lineIterator.filter(line => line.contains("scalaxbVersion"))
            val versionString =  linesWithValIterator.mkString("\n").split("=")(1).trim
            val version = versionString.split("\n")(0) // only val declaration
        println(version)
        version
    }

When you call readPluginSbt you will see the contents of plugins.sbt. You can parse this file and extract the variable.

For example:

resolvers += Resolver.sonatypeRepo("public")

val scalaxbVersion = "1.1.2"

addSbtPlugin("org.scalaxb" % "sbt-scalaxb" % scalaxbVersion)

addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.5.1")

You can extract scalaxbVersion with regular expressions/split:

scala> val line = """val scalaxbVersion = "1.1.2""""
line: String = val scalaxbVersion = "1.1.2"

scala>  line.split("=")(1).trim
res1: String = "1.1.2"