Alex Wilson
Alex Wilson

Reputation: 6740

Idiomatically defining dynamic tasks in SBT 0.13?

I'm moving an SBT plugin from 0.12 over to 0.13. At various points in my plugin I schedule a dynamic set of tasks onto the SBT build graph.

Below is my old code. Is this still the idiomatic way to express this, or is it possible to leverage the macros to make everything prettier?

import sbt._
import Keys._

object Toplevel extends Build
{
    lazy val ordinals = taskKey[Seq[String]]("A list of things")
    lazy val times = taskKey[Int]("Number of times to list things")
    lazy val inParallel = taskKey[Seq[String]]("Strings to log in parallel")

    lazy val Foo = Project( id="Foo", base=file("foo"),
        settings = Defaults.defaultSettings ++ Seq(
            scalaVersion    := "2.10.2",

            ordinals := Seq( "First", "Second", "Third", "Four", "Five" ),

            times := 3,

            inParallel <<= (times, ordinals, streams) flatMap
            { case (t, os, s) =>
                os.map( o => toTask( () =>
                {
                    (0 until t).map( _ => o ).mkString(",")
                } ) ).join
            }

        )
    )
}

Apologies for the entirely contrived example!

EDIT

So, taking Mark's advice into account I have the following tidier code:

import sbt._
import Keys._

object Toplevel extends Build
{
    lazy val ordinals = taskKey[Seq[String]]("A list of things")
    lazy val times = taskKey[Int]("Number of times to list things")
    lazy val inParallel = taskKey[Seq[String]]("Strings to log in parallel")

    def parTask = Def.taskDyn
    {
        val t = times.value
        ordinals.value.map(o => ordinalTask(o, t)).join
    }

    def ordinalTask(o: String, t: Int) = Def.task
    {
        (0 until t).map(_ => o).mkString(",")
    }

    lazy val Foo = Project( id="Foo", base=file("foo"),
        settings = Defaults.defaultSettings ++ Seq(
            scalaVersion    := "2.10.2",

            ordinals := Seq( "First", "Second", "Third", "Four", "Five" ),

            times := 3,

            inParallel := parTask.value
        )
    )
}

This seems to be nearly there, but fails the build with:

[error] /home/alex.wilson/tmp/sbt0.13/project/build.scala:13: type mismatch;
[error]  found   : sbt.Def.Initialize[Seq[sbt.Task[String]]]
[error]  required: sbt.Def.Initialize[sbt.Task[?]]
[error]         ordinals.value.map(o => ordinalTask(o, t)).join

Upvotes: 2

Views: 1463

Answers (1)

Mark Harrah
Mark Harrah

Reputation: 7019

You can use Def.taskDyn, which provides the new syntax for flatMap. The difference from Def.task is that the expected return type is a task Initialize[Task[T]] instead of just T. Translating your example,

inParallel := parTask.value

def parTask = Def.taskDyn {
   val t = times.value
   ordinals.value.map(o => ordinalTask(o, t)).joinWith(_.join)
}

def ordinalTask(o: String, t: Int) = Def.task {
   (0 until t).map(_ => o).mkString(",")
}

Upvotes: 3

Related Questions