Is the use of ‘aggregate’ following by ‘dependsOn’ redundant with the same modules?

tl;dr aggregate causes the tasks to be executed in the aggregating module and all aggregated one while dependsOn sets a CLASSPATH dependency so the libraries are visible to the aggregateing module (depending on the configuration that’s compile aka default in the example).

A sample to demonstrate the differences.

I’m using the following build.sbt (nothing really interesting):

lazy val a = project

lazy val b = project

lazy val c = project dependsOn b aggregate (a,b)

The build defines three modules a, b, and c with the last c project to be an aggregate for a and b. There’s the fourth module – an implicit one – that aggregates all the modules a, b, and c.

> projects
[info] In file:/Users/jacek/sandbox/aggregate-dependsOn/
[info]     a
[info]   * aggregate-dependson
[info]     b
[info]     c

When I execute a task in an aggreateing module, the task is going to be executed in the aggregated modules.

> compile
[info] Updating {file:/Users/jacek/sandbox/aggregate-dependsOn/}b...
[info] Updating {file:/Users/jacek/sandbox/aggregate-dependsOn/}a...
[info] Updating {file:/Users/jacek/sandbox/aggregate-dependsOn/}aggregate-dependson...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Updating {file:/Users/jacek/sandbox/aggregate-dependsOn/}c...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[success] Total time: 0 s, completed Oct 22, 2014 9:33:20 AM

The same happens when I execute a task in c that will in turn execute it against a and b, but not in the top-level project.

> show c/clean
[info] a/*:clean
[info]  ()
[info] b/*:clean
[info]  ()
[info] c/*:clean
[info]  ()
[success] Total time: 0 s, completed Oct 22, 2014 9:34:26 AM

When a task’s executed in a or b, it runs only within the project.

> show a/clean
[info] ()
[success] Total time: 0 s, completed Oct 22, 2014 9:34:43 AM

Whether or not a task is executed in aggregateing projects is controlled by aggregate key scoped to a project and/or task.

> show aggregate
[info] a/*:aggregate
[info]  true
[info] b/*:aggregate
[info]  true
[info] c/*:aggregate
[info]  true
[info] aggregate-dependson/*:aggregate
[info]  true

Change it as described in Aggregation:

In the project doing the aggregating, the root project in this case, you can control aggregation per-task. (…) aggregate in update is the aggregate key scoped to the update task.

Below I’m changing the key for c module and clean task so clean is no longer executed in aggregated modules a and b:

> set aggregate in (c, clean) := false
[info] Defining c/*:clean::aggregate
[info] The new value will be used by no settings or tasks.
[info] Reapplying settings...
[info] Set current project to aggregate-dependson (in build file:/Users/jacek/sandbox/aggregate-dependsOn/)
> show c/clean
[info] ()
[success] Total time: 0 s, completed Oct 22, 2014 9:39:13 AM

The other tasks for c are unaffected and still executing a task in c will run it in the aggregate modules:

> show c/libraryDependencies
[info] a/*:libraryDependencies
[info]  List(org.scala-lang:scala-library:2.10.4)
[info] b/*:libraryDependencies
[info]  List(org.scala-lang:scala-library:2.10.4)
[info] c/*:libraryDependencies
[info]  List(org.scala-lang:scala-library:2.10.4)

While aggregate sets a dependency for sbt tasks so they get executed in the other aggregated modules, dependsOn sets a CLASSPATH dependency, i.e. a code in dependsOned module is visible in the dependsOning one (sorry for the “new” words).

Let’s assume b has a main object as follows:

object Hello extends App {
  println("Hello from B")
}

Save the Hello object to b/hello.scala, i.e. under b module.

Since c was defined to dependsOn b (see build.sbt above), the Hello object is visible in b (because it belongs to the module), but also in c.

> b/run
[info] Running Hello
Hello from B
[success] Total time: 0 s, completed Oct 22, 2014 9:46:44 AM
> c/runMain Hello
[info] Running Hello
Hello from B
[success] Total time: 0 s, completed Oct 22, 2014 9:46:58 AM

(I had to use runMain in c as run alone couldn’t see the class that I can’t explain).

Trying to run the task in a ends up with java.lang.ClassNotFoundException: Hello since the class is not visible in the module.

> a/runMain Hello
[info] Updating {file:/Users/jacek/sandbox/aggregate-dependsOn/}a...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Running Hello
[error] (run-main-6) java.lang.ClassNotFoundException: Hello
java.lang.ClassNotFoundException: Hello
    at java.lang.ClassLoader.findClass(ClassLoader.java:530)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
[trace] Stack trace suppressed: run last a/compile:runMain for the full output.
java.lang.RuntimeException: Nonzero exit code: 1
    at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last a/compile:runMain for the full output.
[error] (a/compile:runMain) Nonzero exit code: 1
[error] Total time: 0 s, completed Oct 22, 2014 9:48:15 AM

Redefine a to dependsOn b in build.sbt and the exception vanishes.

You should read Multi-project builds in the official documentation.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)