sbt skip tests in subproject unless running from within that project? - scala

I have an SBT project with multiple subprojects. One of the subprojects have tests that I don't want to run unless I explicitly do something like ";project xyz ;test-only". So, if my project structure is:
main
main/abc
main/def
main/xyz
Ideally, running "test" in main would execute any tests in main, main/abc, and main/def projects, but not main/xyz.
I tried to add a test filter in the build file for the main class that excludes all tests in main/xyz (by package name), then adding a separate build.sbt file in the main/xyz project to add them back, but this still results in the tests being executed from the top-level project...

"Aggregation" is the name of the feature that makes test execute on other projects (known as aggregated projects or "execution dependencies") as well as the current one. You can find more information on the Multi-Project Builds page.
I would create a custom task in the "main" project that depends on the tasks you want to run. For example,
myTestTask <<= Seq(
test in (main, Test),
test in (abc, Test),
test in (deff, Test)
).dependOn
where val myTestTask = TaskKey[Unit]("my-test-task") and main, abc, and deff are references to your Projects.
Aggregation is only applied to the top-level task specified on the command line. So, if you call my-test-task, that will be the only task aggregated (in this case, it won't be defined on any subprojects, so no tasks get added through aggregation). In particular, its dependencies, the explicitly listed test tasks, don't get aggregated. The consequence is that test in xyz doesn't get executed when you call my-test-task.
Finally, note that you can say xyz/test-only to run test-only for the xyz project without changing to that project.

Related

Build sbt project in uber jar instead of individual submodule jars

Been recently trying to change the way my Scala project does the assembly so that only a single jar is generated out of it instead of one per module.
main-project
| - inner-module-one [inner_module_one]
| - inner-module-two [inner_module_two]
What I've currently done is the following for the main module (the one I want its uber jar containing other modules content).
project
.in(file("."))
.settings(
name := "main-project",
assemblyName,
settings
)
.aggregate(
inner_module_one,
inner_module_two
)
Having the other two modules declared as follows.
lazy val inner_module_one = project
.in(file("inner-module-one"))
.settings(
name := "inner-module-one",
assemblyName,
settings,
dependencies and such (...)
)
.dependsOn(
inner_module_two
)
The jar file generated for the main-project is really, really small (no more than 5mbs in size) and only contains Scala related stuff, no project classes or such. However, other modules jars are complete and contains everything they require.
I've already tried adding the following setting to the main-project.
aggregate in assembly := false
But still no luck so far. Jars for submodules aren't generated but the main-project jar still doesn't contain the contents of the submodules.
Any clue where the issue could be?
EDIT
Tried what #LuisMiguelMejíaSuárez suggested and seems to be wanting to build, however, some errors arise that were already solved within their respective modules. In a given module there are some conflicts which are solved due to some overrides, but now they are appearing again.
[error] deduplicate: different file contents found in the following:
Having the dependsOn instead of aggregate affects the way dependencies are added, overridden and such?
Let's divide your question into two sections, how to include the submodules into the main-project, and you want to know how to not package assembly the 2 submodules.
Let's start from the easier, the second. sbt-assembly, is a plugin for sbt. Just like any other plugin, you cann disable it using the disablePlugins command. Therefore, if you define:
lazy val inner_module_one = project.disablePlugins(AssemblyPlugin)
.in(file("inner-module-one"))
.settings(
name := "inner-module-one"
)
.dependsOn(
inner_module_two
)
then inner_module_one won't build a jar.
To solve the first question, we need to understand the difference between aggregate and dependsOn. For that I'll quote from Jacek Laskowski great explanation:
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).
Therefore, when you aggregate inner_module_one, and inner_module_two, you just cause then to assembly as well. In order to get those to be part of main-project all you need to do is declare:
project
.in(file("."))
.settings(
name := "main-project",
assemblyName,
settings
)
.dependsOn(
inner_module_one,
inner_module_two
)
In order to test it, I created the following structue:
main-project
|-inner_module_one
|-main
|-scala
|One.scala
|-inner_module_two
|-main
|-scala
|Two.scala
In order to check whether the class One was in a jar I used the command:
jar tfv inner-module-one/target/scala-2.12/inner-module-one-assembly-0.1.0-SNAPSHOT.jar | grep One
Then, when running sbt assembly with the original aggregate, the command above provided empty results on the output jar. And when running sbt assembly with dependsOn, the command above provided one result:
480 Fri Oct 09 01:48:14 IDT 2020 One.class
If this causes conflicts, you can read about it at sbt-assembly: deduplication found error

sbt-assembly multimodule project?

My project is separated on multiple parts:
* core project
* utils (as example) project
Both of them have some unit-tests, and "core project" relies on code in "utils" project by "dependsOn" mechanism of sbt.
I am using sbt-assembly plugin for building "uber-jar"/"fat-jar", but sbt assembly task does`t run test on utils project - which is what I am trying to achieve (I can not see any tests from "utils project" in logs)
Changing "dependsOn" to "aggregate" introduce new problem:
sbt assembly aggregate deduplicate
something similar to this issue-on-github
So my question is how to organize multimodule project which can be assembled by sbt-assembly and all of the tests are executed during assembly task?
My guess is that you should have both dependsOn and aggregate relationships between your subprojects. They are not mutually exclusive, only serve different purposes.
DependsOn introduces code dependency, so if core depends on utils it means that you can reference types from utils in core.
Aggregate introduces task dependency. That means if you execute compile or test on core and it aggregates utils then the task will be executed on both subprojects.
Problems with deduplicate are another beast - it means, that there are duplicates in resources or classes when attempting to create one jar. The reasons for this may be various and you can tackle them by verifying the library dependencies in the build or creating MergeStrategy - https://github.com/sbt/sbt-assembly#merge-strategy

sbt: how to add fastOptJS to stage

I have cross-project (JVM + JS) and also use sbt native packager. With clean JVM projects I have found handy to use ~stage command on my development iterations.
The project contains plenty of mains, and this style permit to run multiple mains with script getting CLASSPATH from prepared stages on the separated terminals.
Now I'd want to add fastOptJS command be also executed (inserted into, merged with, or what?) on the ~stage triggering cycle.
How to?
You can also create a dependency.
stage := (stage dependsOn fullOptJs).value
This should execute fullOptJs for every stage call.
Ugh, in fact it is very simple - just use
~ ;fastOptJS ;stage
in sbt session.

Unit testing SBT code within the local project

I have an SBT project, within which I have defined a source generator that parses some DB migration scripts in order to make the current DB version available to the application at compile-time.
For example, I might have the following resources:
subproject1/src/main/resources/db/migration/
|- V1__Baseline.sql
|- V2__AddCustomerTable.sql
|- V2_1__Refactor.sql
And I would require my source generator to create a scala object with the field val version = "2.1".
The SBT project is structured as:
project/
|- build.sbt
|- SchemaVersionParser.scala
Where SchemaVersionParser defines the utility functions that are used by build.sbt in order to parse the resources and generate the appropriate file.
Now since this is a key component of my app, I'd like to ensure that the functions in SchemaVersionParser which extract and sort the versions are unit tested.
Is there any supported means of unit testing SBT code residing locally in the one project? I know I can do this if I create a separate SBT plugin, however it'd be nice if I could avoid this.
SBT build definitions are SBT projects. So put test code in project/src/test/scala should be able to access SchemaVersionParser, and to launch the tests you just do sbt test in project directory.

Run "clean" all dependent SBT subprojects

I have an SBT project, specifically a Play Framework 2.1 project, that has a number of subprojects specified in the configuration. The dependencies seem to work fine when compiling, but "clean" only seems to clean the currently selected project, not including its dependencies. Is there any way to clean both the selected project and its dependent subprojects?
If your main project aggregates subjects, like this:
lazy val root = Project("name", file("."))
.aggregate(module1, module2, macros)
then any command called on this root project will be executed for all subprojects. If you call inspect clean command in your sbt session, you'll see, under Related section all subprojects which relates on this clean
On the side note in the comment
aggregate and dependsOn are different command for different purposes. The purpose of aggregation is in running commands called on the root project. In my example by calling test command on my root project, this command will be executed also for module1 module2 and macros. If you want to turn off such behaviour with the following setting:
aggregate in test := false
Aggregated project are independent on the code in them. It's usually used on the root project, nfor example not to call test on each project, but to call it on root. Remeber that in case of aggregation commands will be executed in parallel.
And dependsOn means that your project will depend on the code from other project. And in this case SBT will execute command sequentialy, in order to compile your root project, which dependsOn some modules, it should compile those modules at first step, the the root project.