SBT aggregating subproject - scala

Given a project structure:
rootProject
aggregatingSubProject
subA
subB
subC
How do I run the package task for aggregatingSubProject and all its aggregated projects, without specifying them by hand?
So far I can only have subA and subB built when doing package in the rootProject - regardless if I put .aggregate(subA, subB) in aggregatingProject's build.sbt.
I need the rootProject to define common settings for the build and I want to build multiple projects (some aggregating other projects, like aggregatingSubProject does) in a single build.
EDIT: I need to do this without specifying all the sub-sub-projects in the root build.sbt. I'd like to define subA and subB in aggregatingSubProject/build.sbt.
Using sbt 0.13.16.

Given this in build.sbt:
val rootProject = project in file(".")
val aggregatingSubProject = project
val subC = project
and this in aggregatingSubProject/build.sbt:
aggregateProjects(subA, subB)
val subA = project
val subB = project
You can run aggregatingSubProject/package and get:
> aggregatingSubProject/package
[info] Updating {file:/s/t-subaggregate/}subB...
[info] Updating {file:/s/t-subaggregate/}aggregatingSubProject...
[info] Updating {file:/s/t-subaggregate/}subA...
[info] Done updating.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/subA/target/scala-2.12/suba_2.12-0.1-SNAPSHOT.jar ...
[info] Done updating.
[info] Done packaging.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/subB/target/scala-2.12/subb_2.12-0.1-SNAPSHOT.jar ...
[info] Done packaging.
[info] Done updating.
[info] Packaging /s/t-subaggregate/aggregatingSubProject/target/scala-2.12/aggregatingsubproject_2.12-0.1-SNAPSHOT.jar ...
[info] Done packaging.

Related

Sequential test setup/cleanup in sbt

I have an SBT project with multiple subprojects aggregated within a root project. I want to make sure that test suites are ran sequentially, so I have set Global / concurrentRestrictions += Tags.limit(Tags.Test, 1).
However, I am also using Tests.Setup()/Cleanup() to initialise memory-heavy resources for each subproject's test suite. The problem I'm facing is that concurrentRestrictions has no effect on test setup and cleanup: all of my test setup and cleanup logic is being ran in parallel, when I would like it to run sequentially before and after each test suite.
Here is a sample build.sbt:
import sbt._
import scala.sys.process._
lazy val testSettings = Seq(
testOptions ++= Seq(
Tests.Setup(() => {
println(s"setup for ${name.value}")
}),
Tests.Cleanup(() => {
println(s"cleanup for ${name.value}")
})
)
)
Global / libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.1.0" % "test"
)
def module(path: String) = {
Project(path, file(path))
.settings(testSettings)
}
lazy val module1 = module("module1")
lazy val module2 = module("module2")
lazy val module3 = module("module3")
lazy val root = (project in file("."))
.aggregate(module1, module2, module3)
Global / concurrentRestrictions += Tags.limit(Tags.Test, 1)
and a dummy test I'm using for each subproject:
import org.scalatest.matchers.should.Matchers
import org.scalatest._
class DummyTest extends FlatSpec with Matchers {
"this test" should "run a test for module 1" in {}
}
Running sbt test gives the following output, modulo the vagaries of concurrency:
$ sbt test
[info] Loading settings for project global-plugins from plugins.sbt ...
[info] Loading global plugins from /home/jejost/.sbt/1.0/plugins
[info] Loading project definition from /home/jejost/dev/tmp/sbt-yunowork/project
[info] Loading settings for project root from build.sbt ...
[...]
setup for module1
setup for module3
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module1
cleanup for module3
cleanup for module2
[...]
when the behaviour I want is this:
$ sbt test
[info] Loading settings for project global-plugins from plugins.sbt ...
[info] Loading global plugins from /home/jejost/.sbt/1.0/plugins
[info] Loading project definition from /home/jejost/dev/tmp/sbt-yunowork/project
[info] Loading settings for project root from build.sbt ...
[...]
setup for module1
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
cleanup for module1
setup for module3
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
cleanup for module3
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module2
[...]
(Ordering of module1, module3, module2 doesn't matter to me as long as setup/teardown is sequential before/after each subproject)
Is it possible to ensure sequential test setup and cleanup like this, ideally without defining my own custom task?
I am using SBT 1.3.4.
EDIT: this has been flagged as a duplicate of the following question: How to run subprojects tests (including setup methods) sequentially when testing
This is indeed the same problem, however, the accepted answer does not work and does not change anything to the task run order! I tried to reproduce the other question's setup to see if it's a regression in SBT, unfortunately the OP's SBT version is too old to build successfully on my machine.
Commands, unlike tasks, are by default executed sequentially so consider simply dropping down to command level
addCommandAlias("sequentialtest", ";module1/test;module2/test;module3/test;")
which gives something like
sbt:hello-world-scala> sequentialtest
setup for module1
[info] DummyTest:
[info] this test
[info] - should run a test for module 1
cleanup for module1
[info] Run completed in 164 milliseconds.
...
setup for module2
[info] DummyTest:
[info] this test
[info] - should run a test for module 2
cleanup for module2
[info] Run completed in 148 milliseconds.
...
setup for module3
[info] DummyTest:
[info] this test
[info] - should run a test for module 3
cleanup for module3
[info] Run completed in 154 milliseconds.
...
Addressing the comment
commands += Command.command("sequentialtest") { state =>
Project
.extract(state)
.structure
.allProjects
.iterator
.map(_.id)
.filterNot(_ == "root")
.map(id => s"$id/test;")
.mkString :: state
}

scala.io.source.fromFile with sbt subProjects reading from root resource folder

I have a sbt build with multiple subProjects, when trying to use scala.io.source.fromFile it tries to read files from the root project and not from each individual subProject. This is what I need in some cases but in others I want to use the root resource.
What is the idiomatic way of reading from subproject and root resource folders in SBT with subprojects?
It depends on which scope you are calling resourceDirectory from. Here is an example of build with one root project and two sub-projects (from sbt REPL):
> resourceDirectory
[info] alpha/compile:resourceDirectory
[info] /Users/xyz/sbt-tutorial/sub-projects/sub-a/src/main/resources
[info] beta/compile:resourceDirectory
[info] /Users/xyz/sbt-tutorial/sub-projects/sub-b/src/main/resources
[info] root/compile:resourceDirectory
[info] /Users/xyz/sbt-tutorial/sub-projects/src/main/resources
As you can see, calling alpha/compile:resourceDirectory will get you the resource directory for the sub-project called alpha.
If you want the sbt DSL notation for this it will be:
myTask := {
val resDir = (resourceDirectory in (alpha, Compile)).value
...
}
You can replace alpha etc. with ThisBuild if you want to get the resource directory for the root project.

Prevent sbt from publishing an automatically aggregated root project

I have an sbt multi project build with two projects core and mac. I don't have a root project which aggregates them. Unfortunately sbt does create one automatically.
How can I prevent this dummy aggregate project from being published? I get something like "default-6a1ca6" apart from core and mac when I run sbt publish-local.
I am looking for something like
autoRoot := false
?
For sbt 1.x you can also use
lazy val root = (project in file(".")).
aggregate(core, mac).
settings(
skip in publish := true
)
See https://github.com/sbt/sbt/issues/3136
tl;dr Use packagedArtifacts in file(".") := Map.empty
With the latest SBT 0.13.1 and the following two files (and no other files inside the project):
build.sbt
lazy val core = project
lazy val mac = project
project/build.properties
sbt.version=0.13.1
...executing sbt publish-local gives:
$ sbt publish-local
...
[info] Set current project to root-0__multi (in build file:/Users/jacek/sandbox/so/0__multi/)
...
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/poms/mac_2.10.pom
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/jars/mac_2.10.jar
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/srcs/mac_2.10-sources.jar
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/docs/mac_2.10-javadoc.jar
[info] published ivy to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[info] published root-0__multi_2.10 to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/poms/root-0__multi_2.10.pom
[info] published root-0__multi_2.10 to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/jars/root-0__multi_2.10.jar
[info] published root-0__multi_2.10 to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/srcs/root-0__multi_2.10-sources.jar
[info] published root-0__multi_2.10 to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/docs/root-0__multi_2.10-javadoc.jar
[info] published ivy to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/poms/core_2.10.pom
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/jars/core_2.10.jar
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/srcs/core_2.10-sources.jar
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/docs/core_2.10-javadoc.jar
[info] published ivy to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[success] Total time: 2 s, completed Feb 4, 2014 1:38:28 AM
It means that by default all there projects are published.
When you however add the setting packagedArtifacts in file(".") := Map.empty to set packagedArtifacts for the current (root) project:
build.sbt
lazy val core = project
lazy val mac = project
packagedArtifacts in file(".") := Map.empty
...executing sbt publish-local gives:
$ sbt publish-local
...
[info] Set current project to root-0__multi (in build file:/Users/jacek/sandbox/so/0__multi/)
...
[info] published ivy to /Users/jacek/.ivy2/local/default/root-0__multi_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/poms/core_2.10.pom
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/jars/core_2.10.jar
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/srcs/core_2.10-sources.jar
[info] published core_2.10 to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/docs/core_2.10-javadoc.jar
[info] published ivy to /Users/jacek/.ivy2/local/core/core_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/poms/mac_2.10.pom
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/jars/mac_2.10.jar
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/srcs/mac_2.10-sources.jar
[info] published mac_2.10 to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/docs/mac_2.10-javadoc.jar
[info] published ivy to /Users/jacek/.ivy2/local/mac/mac_2.10/0.1-SNAPSHOT/ivys/ivy.xml
[success] Total time: 1 s, completed Feb 4, 2014 1:39:34 AM
No current (root) project is published.
This answer is a work around: Create an explicit root project and disable publishing for it. I wish there was a simpler way, though.

SBT dependsOn RootProject: doesn't compile the dependency

I have a pretty simple configuration:
//lazy val bananaRdfProject = RootProject( uri("git://github.com:stample/banana-rdf.git#"+bananaGitBranch) )
// lazy val bananaRdfProject = RootProject( uri("https://github.com/stample/banana-rdf.git#"+bananaGitBranch) )
// lazy val bananaRdfProject = ProjectRef( uri("https://github.com/stample/banana-rdf.git#"+bananaGitBranch) ,"banana-rdf")
lazy val bananaRdfProject = RootProject( file("../banana-rdf") )
lazy val main = play.Project(appName, appVersion, appDependencies).settings(...)
.dependsOn( bananaRdfProject )
I tried using the 4 different project declarations above for bananaRdfProject.
As I may edit this banana-rdf locally, I want it to be recompiled each time I build my play project, so that I do not have to publish the banana-rdf...
But when I try to compile my main play project, that uses banana-rdf, it doesn't compile banana-rdf, but tries to compile the main project: the compilation fails because banana-rdf classes are missing in the classpath.
sebastien#clemence-XPS-L412Z:rww-play (master *%)$ ./play.old/play
[info] Loading project definition from /home/sebastien/Bureau/rww-play/project
[warn] there were 1 deprecation warning(s); re-run with -deprecation for details
[warn] one warning found
[info] Loading project definition from /home/sebastien/Bureau/banana-rdf/project
[info] Updating {file:/home/sebastien/Bureau/banana-rdf/project/}banana-rdf-build...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 2 Scala sources to /home/sebastien/Bureau/banana-rdf/project/target/scala-2.10/sbt-0.13/classes...
[warn] there were 11 deprecation warning(s); re-run with -deprecation for details
[warn] there were 2 feature warning(s); re-run with -feature for details
[warn] two warnings found
[info] Set current project to RWWeb (in build file:/home/sebastien/Bureau/rww-play/)
_
_ __ | | __ _ _ _
| '_ \| |/ _' | || |
| __/|_|\____|\__ /
|_| |__/
play 2.2-TLS built with Scala 2.10.3-RC3 (running Java 1.7.0_45), http://www.playframework.com
> Type "help play" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.
[RWWeb] $ compile
[info] Updating {file:/home/sebastien/Bureau/banana-rdf/}banana...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Updating {file:/home/sebastien/Bureau/rww-play/}RWWeb...
[info] Resolving org.fusesource.jansi#jansi;1.4 ...
[info] Done updating.
[info] Compiling 53 Scala sources and 1 Java source to /home/sebastien/Bureau/rww-play/target/scala-2.10/classes...
[error] /home/sebastien/Bureau/rww-play/app/controllers/CORSProxy.scala:4: object banana is not a member of package org.w3
[error] import org.w3.banana.plantain.Plantain
[error] ^
[error] /home/sebastien/Bureau/rww-play/app/controllers/CORSProxy.scala:7: not found: type Plantain
[error] object CORSProxy extends org.www.readwriteweb.play.CORSProxy[Plantain](webClient)
.................
Isn't it supposed to compile banana-rdf before trying to compile my main project? If not, what is the point of depending on an external RootProject?
The RootProject(file("../banana-rdf")) project reference just references the root project, and what you really need is a reference to the banana-rdf subproject (in the rdf subdirectory).
The available subprojects are defined in https://github.com/w3c/banana-rdf/blob/master/project/build.scala. There are a few:
[main]> projects
[info] In file:/Users/jacek/sandbox/stackoverflow/19832655/
[info] * main
[info] In https://github.com/w3c/banana-rdf.git
[info] banana
[info] banana-jena
[info] banana-rdf
[info] banana-rdf-test-suite
[info] banana-sesame
[info] examples
[info] experimental
[info] ldp
[info] patch
To reference banana-rdf you should then use the following ProjectRef pointing at a right module (subproject) in a build configuration. Note ProjectRef as well as the name of the subproject - banana-rdf.
lazy val bananaRdfProject =
ProjectRef(uri("https://github.com/w3c/banana-rdf.git"), "banana-rdf")
With the ProjectRef you should be able to resolve any types defined in the banana-rdf subproject.
There is a difference between aggregate projects and dependsOn. I think you need to aggregate to get all projects to build together, dependsOn only makes sure the classes from that project is on the classpath, but it could of course be an old artifact if you haven't built it recently. Check out the sbt docs for more info: http://www.scala-sbt.org/0.12.3/docs/Getting-Started/Multi-Project.html

SBT does not want to enter my project (using project command)

My build is simple:
lazy val stampleWebProject = play.Project("stample-web", appVersion, appDependencies,path = file("stample-web"))
.dependsOn(stampleCoreProject,stampleSearchProject)
.aggregate(stampleCoreProject,stampleSearchProject)
lazy val stampleCoreProject = Project(id = "stample-core",base = file("stample-core"))
lazy val stampleSearchProject = Project(id = "stample-search",base = file("stample-search"))
All these projects have a build.sbt file with dependencies, without any scala build (which would be ignored as far as I know)
When I start SBT (12.4), I get the following:
[info] Set current project to stample-core (in build file:/home/sebastien/Bureau/Stample/)
> projects
[info] In file:/home/sebastien/Bureau/Stample/
[info] * stample-core
[info] stample-search
[info] stample-web
> project stample-search
[info] Set current project to stample-search (in build file:/home/sebastien/Bureau/Stample/)
> projects
[info] In file:/home/sebastien/Bureau/Stample/
[info] stample-core
[info] * stample-search
[info] stample-web
> project stample-core
[info] Set current project to stample-core (in build file:/home/sebastien/Bureau/Stample/)
> projects
[info] In file:/home/sebastien/Bureau/Stample/
[info] * stample-core
[info] stample-search
[info] stample-web
> project stample-web
[info] Set current project to stample-search (in build file:/home/sebastien/Bureau/Stample/)
[stample-search] $ projects
[info] In file:/home/sebastien/Bureau/Stample/
[info] stample-core
[info] stample-search
[info] * stample-web
[stample-search] $ compile
[info] Updating {file:/home/sebastien/Bureau/Stample/}stample-core...
[info] Resolving org.slf4j#slf4j-api;1.6.6 ...
[info] Done updating.
[info] Updating {file:/home/sebastien/Bureau/Stample/}stample-web...
[error] a module is not authorized to depend on itself: stample-search#stample-search_2.10;1.0
[error] (stample-web/*:update) java.lang.IllegalArgumentException: a module is not authorized to depend on itself: stample-search#stample-search_2.10;1.0
[error] Total time: 1 s, completed 26 août 2013 21:57:45
I do not understand some stuff here:
How is choosen the project in which we are by default. I've seem documentation was added in SBT 13.0 but did not see it in the 12.4 multibuild documentation.
How comes I type project stample-web and it tells me I'm in stample-search
Why is there a special display in my sbt console for the project I'm in (stample-web or stample-search, I don't really know...) (this appears here: [stample-search] $ compile, is this relative to play projects?
Why it can't compile stample-search, since it doesn't depend on itself in my build (I suspect it tries to compile the web project but there's a naming problem or something?
Is this an SBT bug. If so, is it possible to use the new 13.0 version with Play framework?
Eugene Yokota is right: I have a conflict in the stample-web folder, which was set the stample-web name in the scala build, but it has a wrong build.sbt in the folder which has name := stample-search