Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

build

sbt

In SBT is the use of aggregate following dependsOn redundant if they both contain the same sub-modules? According to the documentation it seems so, but I have seen this behavior used before and I don't understand what the benefit is. If a project is defined with dependencies, doesn't that already imply what aggregate does for those same dependencies? I notice that my project build is much slower with the use of this redundant aggregate than without and I'd like to know if I can safely remove it.

lazy val module = sbt.Project(...) dependsOn (foo, bar) aggregate (foo, bar) 

OR just...

lazy val module = sbt.Project(...) dependsOn (foo, bar) 

I am using SBT 0.13.6

like image 312
Coder Guy Avatar asked Oct 21 '14 01:10

Coder Guy


People also ask

What does sbt clean do?

clean – delete all generated sources, compiled artifacts, intermediate products, and generally all build-produced files. reload – reload the build, to take into account changes to the sbt plugin and its transitive dependencies.

What does sbt publish do?

The publish action is used to publish your project to a remote repository. To use publishing, you need to specify the repository to publish to and the credentials to use. Once these are set up, you can run publish .

What is sbt command?

The simple build tool (sbt) is used for building Java and Scala projects; its purpose is to allow users to skillfully perform the basics of building and to customize endlessly.

What is sbt tool?

sbt is an open-source build tool for Scala and Java projects, similar to Apache's Maven and Ant. sbt. Original author(s) Mark Harrah. Developer(s)


1 Answers

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.

like image 172
Jacek Laskowski Avatar answered Sep 18 '22 15:09

Jacek Laskowski