Scala logo

Scala Plugin

Scala Plugin for IntelliJ IDEA and Android Studio

New Module Layout for sbt Projects

Try out the enhanced sbt integration with IntelliJ Scala Plugin 2024.3

We’re introducing a new mode that better represents the structure of sbt projects in IntelliJ IDEA by organizing main and test sources into separate modules. The improved layout resolves several issues with compilation and highlighting. It also allows using different compiler options for main and test sources.

We strongly encourage you to try out this new functionality and share your feedback! Enable it via Settings | Build, Execution, Deployment | Build Tools | sbt and select Create separate modules for production and test sources.

Technical background

Each build tool has its own model to represent the project – this includes concepts of modules, dependencies, and much more.When the project is imported to IntelliJ IDEA, it is essential to translate this model into the IDE’s internal model, which may not perfectly align with the build tool’s structure.

Improperly mapping a given build tool’s model to IntelliJ IDEA’s model can lead to issues, such as code compiling when it shouldn’t (or vice versa) and incorrect highlighting.

A significant challenge when mapping the sbt model was the limited distinction between main and test sources. When managing dependencies between two modules, all sources from the dependent module were added to the parent module, making it impossible to include only the main sources. Another issue was configuring separate compiler options for main and test sources.

To address these issues, a new approach has been developed for mapping the sbt model to IntelliJ IDEA – creating separate modules for main and test sources.

How it works

For each project declared in the sbt build, two additional modules are created: main and test. They contain the main and test sources, respectively.

When the project is reloaded in the new mode, each run configuration that references a module should switch to the corresponding main or test module. This happens automatically when the new mode is activated. In very rare cases, it may not be possible to change the modules in the run configurations automatically. If this happens, a notification will appear, giving you the option to update the run configurations manually. If you miss the notification, you can still update the run configurations by explicitly using the Update Run Configurations to the new module naming scheme action.

Key improvements

These examples illustrate the most noticeable changes. However, enabling the new mode also allows for many other improvements, such as more accurate handling of transitive dependencies, support for compile->test dependencies, and better management of -internal configurations.

Different compiler options for Compile and Test scopes

sbt allows you to declare different compiler options in various configuration scopes. The new project model in IntelliJ IDEA now leverages this capability by recognizing compiler options declared in the Test scope, allowing you to configure different options for Compile and Test scopes in the IDE.

For example, if you configure scalacOptions in an sbt project like this:
Test / scalacOptions += "-Ywarn-value-discard"
then warnings about discarded values will only be displayed in the test sources.

If you configure the options in the Compile scope like this:
Compile / scalacOptions += "-Ywarn-value-discard"
then warnings can be displayed both in the main and test sources.

Compile scope project dependencies

Keeping main and test sources in separate modules allows for a more accurate representation of dependencies. This is particularly significant for classpath dependencies, which are always per-configuration in sbt.

Let’s consider a very common dependency example:
core.dependsOn(foo)
which is always resolved to 
core.dependsOn(foo %”compile->compile”)

When separate modules for main and sources are created, this dependency resolves so that both core.main and core.test modules contain only the core.foo.main dependency. None of the core modules has foo.test in its dependencies, which is a proper sbt representation.

It’s worth mentioning that in the old implementation, the core module included the entire foo module. This meant that core test sources had access to foo test sources.

How to enable this feature

As this feature is currently in Beta, it is not enabled by default. Enable it via Settings | Build, Execution, Deployment | Build Tools | sbt and select Create separate modules for production and test sources.

Feedback

We need your feedback! If you encounter any bugs in this feature, please report them to our YouTrack. If you have any questions, feel free to ask them on in our Discord.

Happy developing!

The IntelliJ Scala Plugin team