Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SBT: How to make one task depend on another in multi-project builds, and not run in the root project?

Tags:

scala

sbt

For my multi-project build, I'm trying to create a verify task that just results in scct:test and then scalastyle being executed in order. I would like scct:test to execute for all the subprojects, but not the top-level project. (If it executes for the top-level project, I get "timed out waiting for coverage report" from scct, since there's no source and no tests in that project.) What I had thought to do was to create verify as a task with dependencies on scct:test and scalastyle. This has turned out to be fairly baroque. Here is my Build.scala from my top-level project/ directory:

object MyBuild extends Build {
  val verifyTask = TaskKey[Unit]("verify", "Compiles, runs tests via scct:test and then runs scalastyle")
  val scctTestTask = (test in ScctPlugin.Scct).scopedKey
  val scalastyleTask = PluginKeys.scalastyleTarget.scopedKey

  lazy val root = Project("rootProject",
                      file("."),
                      settings =  Defaults.defaultSettings ++
                                  ScalastylePlugin.Settings ++
                                  ScctPlugin.instrumentSettings ++
                                  ScctPlugin.mergeReportSettings ++
                                  Seq(
                                    verifyTask in Global := {},
                                    verifyTask <<= verifyTask.dependsOn(scctTestTask, scalastyleTask)
                                  )
              ) aggregate(lift_webapp, selenium_tests)

  lazy val subproject_1 = Project(id = "subproject_1", base = file("subproject_1"))

  lazy val subproject_2 = Project(id = "subproject_2", base = file("subproject_2"))
}

However, the verify task only seems to exist for the root project; when I run it I don't see the same task being run in the subprojects. This is exactly the opposite of what I want; I'd like to issue sbt verify and have scct:test and scalastyle run in each of the subprojects but not in the top-level project. How might I go about doing that?

like image 509
Paul Blair Avatar asked Aug 24 '13 23:08

Paul Blair


People also ask

Which is the correct way to add dependencies in sbt file?

If you have JAR files (unmanaged dependencies) that you want to use in your project, simply copy them to the lib folder in the root directory of your SBT project, and SBT will find them automatically.

How do we specify library dependencies in sbt?

The libraryDependencies key Most of the time, you can simply list your dependencies in the setting libraryDependencies . It's also possible to write a Maven POM file or Ivy configuration file to externally configure your dependencies, and have sbt use those external configuration files.

How do I create a sbt task?

To declare a new task, define a lazy val of type TaskKey : lazy val sampleTask = taskKey[Int]("A sample task.") The name of the val is used when referring to the task in Scala code and at the command line. The string passed to the taskKey method is a description of the task.

What does provided mean in build sbt?

sbt file. The “provided” keyword indicates that the dependency is provided by the runtime, so there's no need to include it in the JAR file.


1 Answers

solution 1: define verifyTask in subprojects

First thing to note is that if you want some task (verify, test, etc) to run in some projects, you need to define them scoped to the subprojects. So in your case, the most straightforward thing to do this is to define verifyTask in subproject_1 and subproject_2.

lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.0.4"

lazy val verify = taskKey[Unit]("verify")
def verifySettings = Seq(
  skip in verify := false,
  verify := (Def.taskDyn {
    val sk = (skip in verify).value
    if (sk) Def.task { println("skipping verify...") }
    else (test in Test)
  }).value
)

lazy val root = (project in file("."))
  .aggregate(sub1, sub2)
  .settings(
    verifySettings,
    scalaVersion in ThisBuild := "2.12.4",
    skip in verify := true
  )

lazy val sub1 = (project in file("sub1"))
  .settings(
    verifySettings,
    libraryDependencies += scalaTest % Test
  )

lazy val sub2 = (project in file("sub2"))
  .settings(
    verifySettings,
    libraryDependencies += scalaTest % Test
  )

solution 2: ScopeFilter

There was a recent Reddit thread that mentioned this question, so I'll post what I've done there. If you want to manually aggregate on some subprojects, there's also a technique called ScopeFilter.

Note that I am using sbt 1.x here, but it should work with sbt 0.13 some minor change.

lazy val packageAll = taskKey[Unit]("package all the projects")
lazy val myTask = inputKey[Unit]("foo")

lazy val root = (project in file("."))
  .aggregate(sub1, sub2)
  .settings(
    scalaVersion in ThisBuild := "2.12.4",
    packageAll := {
      (packageBin in Compile).all(nonRootsFilter).value
      ()
    },
    myTask := {
      packageAll.value
    }
  )

lazy val sub1 = (project in file("sub1"))

lazy val sub2 = (project in file("sub2"))

def nonRootsFilter = {
  import sbt.internal.inc.ReflectUtilities
  def nonRoots: List[ProjectReference] =
    allProjects filter {
      case LocalProject(p) => p != "root"
      case _               => false
    }
  def allProjects: List[ProjectReference] =
    ReflectUtilities.allVals[Project](this).values.toList map { p =>
      p: ProjectReference
    }
  ScopeFilter(inProjects(nonRoots: _*), inAnyConfiguration)
}

In the above, myTask depends on packageAll, which aggregates (packageBin in Compile) for all non-root subprojects.

sbt:root> myTask
[info] Packaging /Users/xxx/packageall/sub1/target/scala-2.12/sub1_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[info] Packaging /Users/xxx/packageall/sub2/target/scala-2.12/sub2_2.12-0.1.0-SNAPSHOT.jar ...
[info] Done packaging.
[success] Total time: 0 s, completed Feb 2, 2018 7:23:23 PM
like image 167
Eugene Yokota Avatar answered Sep 22 '22 06:09

Eugene Yokota