copumpkin
copumpkin

Reputation: 2826

How to change many settings in scope at once in build.sbt?

I want to group settings by the context in which they apply in my build.sbt. Instead of

foo in ThisBuild := bar

baz in ThisBuild := bar

bar in Compile := 5

quux in Compile := 7

I'd like something that roughly looks like (don't care much about the specific syntax/API, just the conceptual grouping and lack of repetition)

in(ThisBuild) {
  foo := bar
  baz := bar
}

in(Compile) {
  bar  := 5
  quux := 7
}

Is such a thing possible/clean/idiomatic?

Upvotes: 3

Views: 600

Answers (2)

Eugene Yokota
Eugene Yokota

Reputation: 95624

Defaults.scala

As a supplement to Jacek's answer, I'd encourage you to see sbt 0.13.5's JvmPlugin.scala and Defaults.scala. JvmPlugin is an auto plugin that's enabled by default and adds basic JVM tasks like compile:compile and test:test.

Defaults.defaultConfigs

For instance, Defaults.defaultConfigs adds both compile:compile and test:test as follows:

lazy val defaultConfigs: Seq[Setting[_]] =
  inConfig(Compile)(compileSettings) ++
  inConfig(Test)(testSettings) ++
  inConfig(Runtime)(Classpaths.configSettings)

Defaults.testSettings

As Jacek noted, the expected type on the passed in settings are all Seq[Def.Setting[_]]. Like

lazy val testSettings: Seq[Setting[_]] = configSettings ++ testTasks

Defaults.testTasks

These sequences can be constructed by appending reused sequences that are scoped in different ways as testTasks:

lazy val testTasks: Seq[Setting[_]] =
  testTaskOptions(test) ++
  testTaskOptions(testOnly) ++
  testTaskOptions(testQuick) ++
  testDefaults ++ Seq(
    ....
    test := {
        val trl = (testResultLogger in (Test, test)).value
        val taskName = Project.showContextKey(state.value)(resolvedScoped.value)
        trl.run(streams.value.log, executeTests.value, taskName)
    },
    testOnly <<= inputTests(testOnly),
    testQuick <<= inputTests(testQuick)
)

Project scoping and auto plugin

If you're using multi-project build.sbt, the project scoping would be done by passing the settings to project's settings method.

lazy val root = (project in file("."))

lazy val foo = project.
  settings(yourSettings: _*).
  settings(someOtherSettings: _*)

lazy val bar = project.
  settings(yourSettings: _*).
  settings(someOtherSettings: _*)

You can define def yourSettings in build.sbt (or project/foo.scala). When you start to find passing settings explicitly to be tedious, you can define an autoplugin similar to sbt's JvmPlugin. This would let you write:

lazy val root = (project in file("."))

lazy val foo = project.
  enablePlugins(FooPlugin, BarPlugin)

lazy val bar = project.
  enablePlugins(FooPlugin, BarPlugin)

Edit:

If you make FooPlugin and BarPlugin triggered out of thin air, you could write:

lazy val root = (project in file("."))

lazy val foo, bar = project

See Plugins for the details.

I want to clarify that you can chose the degree of magic/automation when it comes to auto plugins. In the above, both FooPlugin and BarPlugin are triggered out of nothing, but you could easily make BarPlugin triggered by FooPlugin or not triggered at all. This is meant to automate things like "use coffeescript plugin for all web-related projects". I hope I'm still on-topic with your question "I want to group settings by the context."

Upvotes: 2

Jacek Laskowski
Jacek Laskowski

Reputation: 74679

For Compile configuration use inConfig as follows:

inConfig(Compile) {
  Seq(
    bar  := 5,
    quux := 7
  )
}

For ThisBuild scope use inScope as follows:

inScope(ThisScope.copy(project=Select(ThisBuild))) {
  Seq(
    foo := bar,
    baz := bar
  )
}

Note that all the in* methods work for Seqs.

Note also that since they appear in build.sbt file they're automatically scoped to the project the build definition belongs to. Obviously, in ThisBuild settings belong to the build.

As to the idiomatic nature of using the in* methods in *.sbt files, I have never seen them in the files. Since they're pretty new (since sbt 0.13) people could be yet to catch up with their use.

The trait ProjectExtra offers inConfig, inTask and inScope methods. As @copumpkin pointed out in the comment there's no inProject method that would complement the others. I've no idea why, but with inScope it is not necessary and neither are the inConfig and inTask since they pretty much map a scope to a config or a task with the other axes unchanged.

For reference, this is the definition of Scope:

final case class Scope(project: ScopeAxis[Reference], config: ScopeAxis[ConfigKey], task: ScopeAxis[AttributeKey[_]], extra: ScopeAxis[AttributeMap])

Upvotes: 3

Related Questions