2

I want to write a task that is run after all subproject tasks are complete.

Eg, if I do

sbt a b then after task a is complete on ALL subprojects I want to execute task b. I don't want to do (a b) on each project.

Is that possible?

In fact, I'll take it so that I modify the build.sbt directly. I don't necessarily have to specify it at the command line.

sksamuel
  • 16,154
  • 8
  • 60
  • 108

1 Answers1

1

I wrote a blog post on the subject: sequencing tasks with sbt-sequential.

addCommandAlias

Here's an example. We'll define a custom task a in sub1 and sub2 and b in root. The simplest way to achieve sequential execution is using addCommandAlias, so we'll just do that.

lazy val a = taskKey[Unit]("a")
lazy val b = taskKey[Unit]("b")

lazy val root = (project in file(".")).
  aggregate(sub1, sub2).
  settings(addCommandAlias("ab", ";a;b"): _*).
  settings(
    b := {
      println("b")
    }
  )

lazy val sub1 = (project in file("sub1")).
  settings(a := println("a - sub1"))

lazy val sub2 = (project in file("sub2")).
  settings(a := println("a - sub2"))

You can run this from shell as sbt ab.

$ sbt ab
[info] Loading global plugins from ...
[info] Loading project definition from ...
[info] Set current project to root (in build ...)
a - sub2
a - sub1
[success] Total time: 0 s, completed Nov 22, 2014 8:36:18 PM
b
[success] Total time: 0 s, completed Nov 22, 2014 8:36:18 PM

Def.taskDyn

Here's another example. This time using Def.taskDyn, which is also featured in the blog post. I'm constructing a ScopeFilter from the aggregate and then I'm dispatching task a to them.

lazy val a = taskKey[File]("a")
lazy val b = taskKey[Seq[File]]("b")

lazy val root = (project in file(".")).
  aggregate(sub1, sub2).
  settings(
    b := (Def.taskDyn {
      val proj = thisProject.value
      val filter = ScopeFilter(inProjects(proj.aggregate: _*))
      Def.task {
        val allValues = a.all(filter).value
        println(allValues.mkString(","))
        allValues
      }
    }).value
  )

lazy val sub1 = (project in file("sub1")).
  settings(a := new File("a"))

lazy val sub2 = (project in file("sub2")).
  settings(a := new File("b"))

You can run this from shell as sbt b.

$ sbt b
[info] Loading global plugins from ...
[info] Loading project definition from ...
[info] Set current project to root (in build ...)
a,b
[success] Total time: 0 s, completed Nov 23, 2014 9:42:16 PM
Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
  • I've read this before (its really good) but isn't it about sequential ordering within a project? I want task B to execute after task A has executed on all subjects. Eg, A A A A A B. – sksamuel Nov 21 '14 at 23:56
  • Does this work because b is not defined for the subprojects? The reason I ask is because the task I want to run after all the others, is a task added by a plugin. Basically, I have a report task, then after report has run for all subprojects I want to run a report-aggregation task. I can't run that after each project individually because we need all the reports generated first. – sksamuel Nov 23 '14 at 22:37
  • 1
    What you're after may be `ScopeFilter` (http://www.scala-sbt.org/0.13/docs/Tasks.html#Getting+values+from+multiple+scopes). Added an example that creates one from current project's aggregate. – Eugene Yokota Nov 24 '14 at 02:49
  • The combination of `thisProject.value`, `ScopeFilter(inPorjects(...))`, and `.all(filter).value` was key for me. I didn't know there was an `.all` enrichment! – metasim May 20 '15 at 13:13