Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

accessing SBT settings under current scope

Tags:

sbt

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?

like image 528
user2502025 Avatar asked Mar 26 '14 15:03

user2502025


2 Answers

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.

like image 54
Eugene Yokota Avatar answered Nov 09 '22 14:11

Eugene Yokota


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).

like image 29
Jacek Laskowski Avatar answered Nov 09 '22 14:11

Jacek Laskowski