Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SBT Scala cross versions, with aggregation and dependencies

I am struggling with how crossScalaVersions works with subprojects.

I have a project that compiles with 2.10 (foo) and a project that compiles with 2.11 (bar). They share a cross compiled project (common).

How can I compile projects foo and bar?


build.sbt

lazy val root = (project in file(".")).aggregate(foo, bar).settings(
  crossScalaVersions := Seq("2.10.4", "2.11.4")
)

lazy val foo = (project in file("foo")).dependsOn(common).settings(
  crossScalaVersions := Seq("2.10.4"),
  scalaVersion := "2.10.4"
)

lazy val bar = (project in file("bar")).dependsOn(common).settings(
  crossScalaVersions := Seq("2.11.4"),
  scalaVersion := "2.11.4"
)

lazy val common = (project in file("common")).settings(
  crossScalaVersions := Seq("2.10.4", "2.11.4")
)

project/build.properties

sbt.version=0.13.7

foo/src/main/scala/Foo.scala

object Foo {
  <xml>{new C}</xml>
}

bar/src/main/scala/Bar.scala

case class Bar(a: C, b: C, c: C, d: C, e: C, f: C, g: C,
  h: C, i: C, j: C, k: C, l: C, m: C, n: C, o: C, p: C,
  q: C, r: C, s: C, t: C, u: C, v: C, w: C, x: C, y: C,
  z: C)

common/src/main/scala/Common.scala

class C {}

Attempt 1

$ sbt compile
[info] Resolving jline#jline;2.12 ...
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: common#common_2.11;0.1-SNAPSHOT: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn] 
[warn]  Note: Unresolved dependencies path:
[warn]      common:common_2.11:0.1-SNAPSHOT
[warn]        +- bar:bar_2.11:0.1-SNAPSHOT
sbt.ResolveException: unresolved dependency: common#common_2.11;0.1-SNAPSHOT: not found

Attempt 2

$ sbt +compile
[error] /home/paul/test/bar/src/main/scala/Bar.scala:1: Implementation restriction: case classes cannot have more than 22 parameters.
[error] case class Bar(a: C, b: C, c: C, d: C, e: C, f: C, g: C,
[error]            ^
[error] one error found
[error] (bar/compile:compile) Compilation failed

Attempt 3

$ sbt foo/compile bar/compile
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  ::          UNRESOLVED DEPENDENCIES         ::
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn]  :: common#common_2.11;0.1-SNAPSHOT: not found
[warn]  ::::::::::::::::::::::::::::::::::::::::::::::
[warn] 
[warn]  Note: Unresolved dependencies path:
[warn]      common:common_2.11:0.1-SNAPSHOT
[warn]        +- bar:bar_2.11:0.1-SNAPSHOT
sbt.ResolveException: unresolved dependency: common#common_2.11;0.1-SNAPSHOT: not found

Attempt 4

$ sbt +foo/compile +bar/compile
[error] /home/paul/test3/foo/src/main/scala/Foo.scala:2: To compile XML syntax, the scala.xml package must be on the classpath.
[error] Please see http://docs.scala-lang.org/overviews/core/scala-2.11.html#scala-xml.
[error]   <xml>{new C}</xml>
[error]   ^
[error] one error found
[error] (foo/compile:compile) Compilation failed

Attempt 5

I even tried defining common_2_10 and common_2_11 projects with that same base directory but different scala versions. I recall reading that targets are namespaced by Scala version, but SBT says there is a conflict.

$ sbt
[error] Overlapping output directories:/home/paul/test3/common/target:
[error]     ProjectRef(file:/home/paul/test3/,common_2_10)
[error]     ProjectRef(file:/home/paul/test3/,common_2_11)

The only thing I've gotten to work is manually specifying versions:

$ sbt ++2.10.4 foo/compile ++2.11.4 bar/compile

But this is a lot of commands, can never use parallelism, and obviates the whole use of (1) project aggregation and (2) cross building.

Am I missing something fundamental about the intent of crossScalaVersions? Or is there a way to have it play well with the rest of SBT, and for me to compile my heterogeneous projects?

like image 390
Paul Draper Avatar asked Feb 09 '15 03:02

Paul Draper


People also ask

Which Scala version does sbt use?

We recommend that you upgrade to sbt versions 1.0 and later which are compatible with the Scala version 2.12 (requires Java 8).

How do we specify library dependencies in sbt?

The libraryDependencies key Most of the time, you can simply list your dependencies in the setting libraryDependencies . It's also possible to write a Maven POM file or Ivy configuration file to externally configure your dependencies, and have sbt use those external configuration files.

What does %% mean in sbt?

This is part of SBT which play uses as a build tool. Specifically this is an import statement. The percent symbol % is a actually a method used to build dependencies. The double percent sign %% injects the current Scala version - this allows you to get the correct library for the version of scala you are running.

What is crossScalaVersions?

SBT's crossScalaVersions allows projects to compile with multiple versions of Scala. However, crossScalaVersions is a hack that reuses projects by mutates settings.


2 Answers

I wound up declaring common twice, once for each version.

lazy val root = (project in file(".")).aggregate(foo, bar)

lazy val foo = (project in file("foo")).dependsOn(common_2_10).settings(
  scalaVersion := "2.10.4"
)

lazy val bar = (project in file("bar")).dependsOn(common_2_11).settings(
  scalaVersion := "2.11.4"
)

def commonProject = (project in file("common")).settings(
  target := baseDirectory.value / s"target-${scalaVersion.value}"
)

lazy val common_2_10 = commonProject.settings(
  scalaVersion := "2.10.4"
)

lazy val common_2_11 = commonProject.settings(
  scalaVersion := "2.11.4"
)

Note that I had to make the target directories different, or else SBT would reject it because they overlapped.

Also note that def makes commonProject not included the SBT's magical (reflection-based) search for project definitions.


This isn't the prettiest, but it is robust, readable, and reasonable. All commands/tasks work as one might expect.

In one way this is even better than crossScalaVersions, in that 2.10 and 2.11 projects can now be compiled in parallel, which does not happen with crossScalaVersions :)


EDIT: I created an SBT plugin, sbt-cross, to help out with this.

like image 157
Paul Draper Avatar answered Oct 07 '22 03:10

Paul Draper


Check out my sbt-doge:

sbt plugin to aggregate tasks across subprojects and their crossScalaVersions

like image 42
Eugene Yokota Avatar answered Oct 07 '22 01:10

Eugene Yokota