3

I have an SBT project with 2 subprojects and I want to have test compiler settings different from the normal compiler settings, for all the subprojects. I.e. I want to enforce -Xfatal-warnings for the main code but not for tests.

I'm looking for a solution that doesn't involve setting each subproject individuall but all at once.

I'm working with SBT 1.3.9 and Scala 2.13.3

This is a sample of what I have in SBT:

lazy val root: Project = project
  .in(file("."))
  .aggregate(projectA, projectB)

lazy val projectA: Project = project
  .in(file("projectA"))
  .settings(
    name := "projectA",
  )

lazy val projectB: Project = project
  .in(file("projectA"))
  .settings(
    name := "projectA",
  )

I tried plenty of options but I can't set test options different to compile options. As a quick summary, I tried to play around with scoping:

  • scalacOptions := Seq(XXX) + Test/scalacOptions := Seq(YYY)
  • Compile/scalacOptions := Seq(XXX) + Test/scalacOptions := Seq(YYY)
  • ThisBuild/scalacOptions := Seq(XXX) + ThisBuild/Test/scalacOptions := Seq(YYY)
  • ThisBuild/Compile/scalacOptions := Seq(XXX) + ThisBuild/Test/scalacOptions := Seq(YYY)
  • Also tried to scope under test and played around with various combinations of the above.

This question is the closest to what I need but it doesn't work, probably beucase it's not meant for multiproject files.

UPDATE:

I've just discovered that if I scope the setting to a specific project then I have the behaviour I want:

projectA / Compile / scalacOptions := Seq(XXX)
projectA / Test / scalacOptions := Seq(YYY)
projectB / Compile / scalacOptions := Seq(XXX)
projectB / Test / scalacOptions := Seq(YYY)

but if I use ThisBuild it doesn't work

ThisBuild / Compile / scalacOptions := Seq(XXX)
ThisBuild / Test / scalacOptions := Seq(XXX)

Do I have a wrong understanding of something or is this a bug?

ColOfAbRiX
  • 1,039
  • 1
  • 13
  • 27

1 Answers1

1

ThisBuild scope axis value is often misunderstood when attempting to define common settings for multiple subprojects. Please note it does not mean

add this common setting to all the subprojects in this build

Instead the meaning is closer to

If a subproject does not define this setting, then, possibly, delegate (fall back) to ThisBuild

For example, executing inspect Test / scalacOptions on basic hello world project gives

...
[info] Provided by:
[info]  ProjectRef(uri("file:/my/Path/To/Project/"), "root") / Test / scalacOptions
...
[info] Delegates:
[info]  Test / scalacOptions
[info]  Runtime / scalacOptions
[info]  Compile / scalacOptions
[info]  scalacOptions
[info]  ThisBuild / Test / scalacOptions
[info]  ThisBuild / Runtime / scalacOptions
[info]  ThisBuild / Compile / scalacOptions
[info]  ThisBuild / scalacOptions
[info]  Zero / Test / scalacOptions
[info]  Zero / Runtime / scalacOptions
[info]  Zero / Compile / scalacOptions
[info]  Global / scalacOptions
...

The Provided by section shows the scope with highest precedence

ProjectRef(uri("file:/my/Path/To/Project/"), "root") / Test / scalacOptions

then Delegates section shows the remainder of precedence order where we notice there are bunch of scopes "above" the ones using ThisBuild as project axis. Therefore because of how sbt scope delegation rules work

ThisBuild / Test / scalacOptions

does not mean tests within subprojects will automatically pick up the new value as it is quite low in the picking order.

I believe the most straightforward approach around sbt's arguably confusing scoping rules is to define a val with common settings and then simply add that value to each subproject, or define an auto plugin

Auto plugins can inject project settings across the board

Consider my related answer which demonstrates the two approaches.

Mario Galic
  • 47,285
  • 6
  • 56
  • 98
  • I knew all this stuff and yet I couldn't put things together. SBT is complex. I really dislike using a common setting `val`, I'm trying with an autoplugin as I have already defined a few – ColOfAbRiX Oct 22 '20 at 22:07