I'm having a problem trying to understand the concept of scope in sbt. I want a task to be run under a specific scope, and be able to access scoped settings, i.e.
build.sbt
name := "Superapp"
name in Test := "Testapp"
val printScopedKey = TaskKey[Unit]("psk", "Print Scoped Key")
printScopedKey := println("***** [APP NAME] " + name.value)
I'd expect the following:
> test:psk
> ***** [APP NAME] Testapp
Instead of the actual:
> ***** [APP NAME] Superapp
How can I do this in sbt? Is that even possible?
OP wrote "I'd expect the following:"
> test:psk
> ***** [APP NAME] Testapp
Without actually defining psk
task in Test
configuration, sbt would look for psk
task first in Global
configuration, then in the order of configurations
of the project, which by default is Seq(Compile, Runtime, Test, Provided, Optional)
.
So the following (and @Jacek Laskowski's answer too) describes how one can go about in defining tasks into multiple scopes without code duplication. A setting can be scoped in three axes (project, configuration, and task). The project part doesn't come into play as much so we'll discuss configuration and task here.
It's recommended that task-specific settings are scoped to a task to encourage reuse of keys. For example:
test in assembly := {}
In the above test
key is scoped to assembly
task to control tests that are run before creating a fat JAR. You can define a "task-generator" method that would take a key and create a graph of settings around it:
def assemblyTask(key: TaskKey[File]): Initialize[Task[File]] = Def.task {
val t = (test in key).value
val s = (streams in key).value
Assembly((outputPath in key).value, (assemblyOption in key).value,
(packageOptions in key).value, (assembledMappings in key).value,
s.cacheDirectory, s.log)
}
I use that to define assembly
, packageScala
, and packageDependency
tasks.
lazy val baseAssemblySettings: Seq[sbt.Def.Setting[_]] = Seq(
assembly := Assembly.assemblyTask(assembly).value,
packageScala := Assembly.assemblyTask(packageScala).value,
....
)
So far baseAssemblySettings
is configuration-neutral.
If I wanted to scope it in configurations like Compile
and Test
, I'd call inConfig(conf)(settings)
like this:
lazy val assemblySettings: Seq[sbt.Def.Setting[_]] =
inConfig(Compile)(baseAssemblySettings) ++
inConfig(Test)(baseAssemblySettings)
Now you have multiple task graphs in multiple configurations.
Thanks for the question! I'd initially thought I'd know the answer and then realized it's not so simple. I had to look around for a solution.
I use sbt 0.13.2-RC1.
> about
[info] This is sbt 0.13.2-RC1
[info] The current project is {file:/C:/dev/sandbox/0.13.2/}root-0-13-2 0.1-SNAPSHOT
[info] The current project is built against Scala 2.11.0-RC3
[info] Available Plugins: org.sbtidea.SbtIdeaPlugin, de.johoop.jacoco4sbt.JacocoPlugin, com.timushev.sbt.updates.UpdatesPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.10.3
I found the solution in Mark Harrah's response to a similar question on the sbt mailing list that boils down to the following changes in build.sbt:
scalaVersion := "2.11.0-RC3"
name := "Superapp"
name in Test := "Testapp"
name in Runtime := "Runtimeapp"
lazy val psk = taskKey[Unit]("Print Scoped Key")
val pskSetting = psk := println("***** [APP NAME] " + name.value)
// https://groups.google.com/d/msg/simple-build-tool/A87FFV4Sw4k/KPtygikQvogJ
val myPsks = Seq(Compile, Test, Runtime) flatMap { conf =>
inConfig(conf)( Seq(pskSetting) )
}
myPsks
When the build file's loaded, sbt will automagically know that when you're executing psk
its dependency is name in Compile
while test:psk
depends on name in Test
. Pretty clever.
> psk
***** [APP NAME] Superapp
[success] Total time: 0 s, completed 2014-03-26 21:27:37
> test:psk
***** [APP NAME] Testapp
[success] Total time: 0 s, completed 2014-03-26 21:27:41
> runtime:psk
***** [APP NAME] Runtimeapp
[success] Total time: 0 s, completed 2014-03-26 21:27:44
Use inspect
to dig deeper. It's always quite useful to know how it works under the hood (which is not that hard to understand once you start using right tools, like inspect
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With