paulmdavies
paulmdavies

Reputation: 1288

SBT TaskKey If Statement

I'm using SBT 0.13. I'm running into a problem, which is exhibited by the following toy example:

lazy val nativeOne = TaskKey[Unit]( "nativeOne", "One" )
lazy val nativeTwo = TaskKey[Unit]( "nativeTwo", "Two" )

lazy val testProj = Project(
    id = "testProj",
    base = file( "." ),
    settings = Defaults.defaultSettings ++ Seq(
        scalaVersion := "2.10.2",
        nativeOne :=
        {
            println( "Native one"
        },  
        nativeTwo :=
        {
            if ( true )
            {
                println( "Native two" )
            }
            else
            {
                val n1 = nativeOne.value
            }
        }
    )
)

If I go into sbt and run nativeTwo:

> nativeTwo
Native one
Native two

Why is this happening? Why is the false branch being evaluated? Is branching in TaskKeys not allowed?

Upvotes: 1

Views: 2004

Answers (1)

jsuereth
jsuereth

Reputation: 5624

In sbt ALL dependencies are computed in parallel before your task runs. If you were to construct a graph of what you have, it would be:

nativeOne
   ^
   |
nativeTwo

What you're trying to do is order the tasks. In sbt, we try to promote tasks returning the results that other tasks use directly, hence the .value. What that does is not directly run a task, but say "give me the result of this other task".

The best way understand sbt's syntax is as a mimic of the scala-async project. Your code is similar to the following async code:

nativeOne = async {
  println( "Native one")
}
nativeTwo = async {
  if(true) println("Native Two")
  else await(nativeOne)
}

There, it's a little more obvious that you're just saying "wait for the value if it hasn't already computed.

If you want to optionally execute a Task, you need to do so at the Task[_] layer of sbt, which is a bit more verbose. We're using a 'dynamic task'. That is one that chooses which tasks are actually run at runtime.

SO, you'll need to use the Def.taskDyn method which lets you compose tasks in the order they are defined. You'll work on the tasks themselves using the toTask method of TaskKeys. This is instead of working on the values the tasks produce. Here's an example build.sbt:

val taskOne = taskKey[Int]("test")

val taskTwo = taskKey[Int]("test2")

val taskThree = taskKey[Int]("test3")

val switchEm = settingKey[Boolean]("switch 'em")

switchEm := true

taskOne := { println("taskOne"); 1 }

taskTwo := { println("taskTwo"); 2 }

taskThree := Def.taskDyn({
  if(switchEm.value) taskTwo.toTask
  else taskOne.toTask
}).value

And then in the sbt console:

> reload
[info] Loading project definition from /home/jsuereth/projects/sbt/test-projects/project
[info] Set current project to test-projects (in build file:/home/jsuereth/projects/sbt/test-projects/)
> taskThree
2

Also note, you won't see the corrected dependencies in the inspect command, because the dependencies are dynamically added:

> inspect taskThree
[info] Task: Int
[info] Description:
[info]  test3
[info] Provided by:
[info]  {file:/home/jsuereth/projects/sbt/test-projects/}test-projects/*:taskThree
[info] Defined at:
[info]  /home/jsuereth/projects/sbt/test-projects/build.sbt:15
[info] Dependencies:
[info]  *:switchEm
[info]  *:settingsData        <- TaskOne + TaskTwo are missing, this replaces
[info] Delegates:
[info]  *:taskThree
[info]  {.}/*:taskThree
[info]  */*:taskThree

Upvotes: 6

Related Questions