Make only relevant file changes trigger execution in multi-project SBT build - scala

I'm trying to set up a multi-project build like so:
+-- common
| +-- build.sbt
+-- api
| +-- build.sbt
+-- indexer
| +-- build.sbt
+-- build.sbt
build.sbt in root looks like so:
lazy val common = (project in file("common"))
lazy val searchApi = (project in file("search"))
.dependsOn(common)
lazy val indexer = (project in file("indexer"))
.dependsOn(common)
As you can see, indexer and api both depend on common but not on each other. The problem is if I try to do a triggered execution for a task, say compile:
sbt ~api/compile
Then change a file in the indexer project, the project will still recompile even though the file changed isn't actually on its classpath - it seems that watchSources is always comprised of every project referenced by the root build.sbt and doesn't take the project that you're actually running the task on into account.
I've tried filtering watchSources but it's hard to do neatly because the build.sbt file that I put it in can only see the resources for the project that it's in... e.g. watchSources in indexer/build.sbt can only see the watched sources inside indexer, build.sbt in the root is only watching src/main/resources in the root directory (it can't see the watchSources of the sub-projects).
Does anyone have a nice way of solving this. The best I've got is putting something like this in every subproject's build.sbt...
watchSources <<= (watchSources) map { files =>
if (Option(System.getProperty("project")).getOrElse("none").equals("indexer")) {
files
} else {
Nil
}
}
... then running sbt -Dproject=indexer ~indexer/compile.

The ~ operator watches the currently-active project for changes. Try running project api first:
> project api
> ~compile

Related

How to create Scala SBT multi independent project

I am trying to create sbt multi independent project.
I want my project structure some thing like
My Scala and sbt version is 2.12.2 and 1.5.5 respectively.
sbt-multi-project-example/
common/
project
src/
main/
test/
target/
build.sbt
multi1/
project
src/
main/
test/
target/
build.sbt
multi2/
project
src/
main/
test/
target/
build.sbt
project/
build.properties
plugins.sbt
build.sbt
so I referred some github repo:
https://github.com/pbassiner/sbt-multi-project-example (project build is success but i want build.sbt in each individual modules.)
how can I create above project structure and basic idea is common project contains common methods and class.
multi1(Independent project) uses common methods and it has its own methods and classes.
multi2(Independent project) uses common methods and it has its own methods and classes.
what are the changes I need to change in all build.sbt in order to achieve above scenario.
This is the basic structure. As mentioned in the comments this can create a separate publishable artefact for each project so no need for a separate build.sbt
lazy val all = (project in file("."))
.aggregate(common, multi1, multi2)
lazy val common =
project
.in(file("common"))
.settings(
name := "common",
version := "0.1",
// other project settings
)
lazy val multi1 =
project
.in(file("multi1"))
.settings(
name := "multi1",
version := "0.1",
)
.dependsOn(common)

Mixing build.sbt with project *.scala files: should all be unified in build.sbt?

Using SBT 0.13.13, our build definition is inherited from an older project. Currently there are a build.sbt and some project/*.scala files. These scala files follow the same pattern. Here is an example:
import sbt._
import sbt.Keys._
object Docs {
lazy val docTask = TaskKey[Unit]("docPackage", "Generate Scaladoc")
lazy val settings = Seq(
docTask := {
val docs = (doc in Compile).value
IO.copyDirectory(docs, new java.io.File("src/main/resources/myapp-scaladoc"), overwrite = true)
},
docTask := (docTask.dependsOn(doc in Compile)).value
)
}
Appendix: .scala build definition says
In the previous versions of sbt, .scala was the only way to create
multi-project build definition
Question: I suppose this mean the use of separate project/*.scala files is discouraged. If so, is it OK to move the code of these *.scala file and put them all in build.sbt?
There is little functional difference between having code in project/ and code in build.sbt.
The advantage to putting code in a .scala file in project/ is that all symbols in it are imported into the namespace of all build.sbt files in all subprojects of your build. This means that helper methods, constants, plugins, or other Scala code can live in a single location, but be called from anywhere.
Ultimately, whether to use this features comes down to whether you want shared items configured in a single location, or in multiple locations.
If you prefer to have your project settings configured entirely in separate build.sbt files in their subproject directories, putting shared settings in the project/ folder is the way to do that.
If you're OK with having shared settings configured in the root build.sbt file (or if you don't use any subproject build.sbt files), you can consolidate all of your shared configuration into it.

How to load/include projects from submodule Build.scala

If I want to to combine two (or more) sbt projects (e.g. include one in the other)
without publish-local.
folder structure:
/ComboProject
build.sbt
/project
Build.scala
/Project1
build.sbt
/project
Build.scala #this includes lazy val p1
/Project2
build.sbt
/project
Build.scala #this includes lazy val p21, p22, p23, p24
The nested Build.scala gets ignored by sbt, however do they contain the project description and more importantly the build settings.
How can I import/make them visible at the ComboProject level without copy-pasting them into the top-level Build.sbt.
The same problem appears when including once project inside the other one.
/Project1 #make Project2 modules visible here
build.sbt
/project
Build.scala #this includes lazy val p1
/Project2
build.sbt
/project
Build.scala #this includes lazy val p21, p22, p23, p24
Thanks a lot,
this would make it a lot easier to depend on fast evolving dependencies.

Control dependency loading ambiguity on SBT multi-project

I have an SBT Scala multi-project with the following structure:
multiprojectRoot
project/SharedProjectBuildCode.scala
project1
src/sourceFiles
project1-build.sbt
project2
src/sourceFiles
project2-build.sbt
projectN
src/sourceFiles
projectN-build.sbt
multiprojectRoot/project/SharedProjectBuildCode.scala: contains multi-project definitions that use dependsOn to create dependencies on local projects. For example:
lazy val project2 = Project(
...
).dependsOn(project1)
multiprojectRoot/project2/project2-build.sbt: Contains the settings and dependencies for a given project. For example:
name := "project2"
libraryDependencies ++= Seq(
...
"my.company" % "project1" % "1.0"
)
First dependency to project1 is declared with dependsOn on SharedProjectBuildCode.scala file and the second is created on standalone project2-build.sbt build definition file.
So, project2 definition contains either:
an ambiguous dependency to project1 or
a double dependency to project1
We want to keep this project structure, because is the best for our current workflow:
Independent .sbt files serve standalone deployment purposes for each project on our continuous delivery server.
Multi-project .scala file with dependsOn is used to facilitate development, allowing us to avoid things such as continuous publishLocal.
We need to have control for such dependency ambiguities someway. Can you help me?
I think you should have in SharedProjectBuildCode.scala
lazy val root = Project(id = "Main-Project",
base = file(".")) aggregate(project1, project2,..)
lazy val project2 = Project(id = "project2",
base = file("project1")).dependsOn(project1)
...
And don't need to add as dependency in build.sbt anymore.
I was able to control which dependency set loaded on each use case by using the rules of build files loading provided by SBT.
When you load SBT from a given root directory, it looks for *.sbt files on the root directory and also for *.scala on the root/project directory. If you have a multi-project build, then it also reads the definitions of .sbt files that are encountered on child projects, but it will not use project/.scala files on child projects:
.sbt build definition
Multi-project builds
So, I changed my multi-project build the following way:
multiprojectRoot
project/SharedProjectBuildCode.scala
project1
src/sourceFiles
project/DeploymentOnlyCode.scala
project1-build.sbt
project2
src/sourceFiles
project/DeploymentOnlyCode.scala
project2-build.sbt
projectN
src/sourceFiles
project/DeploymentOnlyCode.scala
projectN-build.sbt
This way, depending on the use case I run SBT from the multi-project root or a project internal directory:
Development: SBT is run from multiprojectRoot directory. It takes the advantages of having a multi-project build (such as using dependsOn and avoiding publishLocal).
Production: SBT is run from within a concrete project directory, such as multiprojectRoot/project2. It allows the project to be built as stand-alone, having all dependencies as explicit external (useful for declaring a sequence of dependencies on production, continuous integration server).
Now, a project has 3 instances of code that aggregates their attributes for a final build:
multiprojectRoot/project/SharedProjectBuildCode.scala: Contains local dependencies and other code relevant for multi-project build.
multiprojectRoot/project1/project1-build.sbt: Contains project build attributes, common for multi-project and standalone build of a project, such as name or dependencies that are always external. The same should be done for other multi-project projects of the same level, to be explicitly treated as external dependency artifacts.
multiprojectRoot/project1/project/DeploymentOnlyCode.scala: Contains build attributes that will only be taken into consideration for stand-alone build. The same can be done on other sub-projects, if these require to define deployment specific attributes.
This also gives maximum control on how a project is built, whether is a releasable artifact or not, and handle source code relevant only for a given project, as a complete and independent piece.

sbt-onejar and multi-project build

When using oneJar to package a multi project sbt build, project dependencies are not bundled into the jar. My setting is the following:
foo/build.sbt (top-level build.sbt)
foo/src/ (sources of the root project)
foo/gui/build.sbt (project 'build' definition)
foo/gui/src (sources of the 'gui' project)
The build definitions are:
// foo/build.sbt
name := "foo"
version := "0.0.1"
scalaVersion := "2.10.4"
lazy val root = project.in( file(".") )
lazy val gui = project.in( file("gui") ).dependsOn( root )
[...]
//foo/gui/build.sbt
name := "foo-gui"
seq(com.github.retronym.SbtOneJar.oneJarSettings: _*)
[...]
When calling oneJar on the gui project everything seems to run fine, but the classes of the root project are not included in the jar (although the library dependencies are). Is there any fix ?
I never tried a light configuration as you but shouldn't you put the oneJar settings in the root sbt file? You want to package the root and include guy right?
I tried something similar for the first time today and started with oneJar but when using a full sbt configuration the compiler complained that settings were a Seq(_) and sbt expected a single setting or something like that. I switched to sbt-assembly and it worked.
sbt-oneJar has not been updated for 2 years while sbt-assembly was recently updated. I'm not sure which one is preferred but I'd rather use an active tool.