How to configure IDEA to look in different source sets depending on the build option?

1 week ago 17
ARTICLE AD BOX

This is a Java/Kotlin project. I want to be able to write code with one abstraction in the main and different implementations in multiple source sets. And then run it as a Java application.


There are two files in the main source set(src/main/kotlin).

A file Times.kt with the abstraction

interface Times { fun now(): Duration }

And a file App.kt with the main method

fun main() { val times: Times = FinalTimes println("now: ${times.now()}") }

The mock source set(src/mock/kotlin) contains implementation for Times.

object FinalTimes : Times { private var index = 0L override fun now(): Duration { return index++.milliseconds } }

The real source set(src/real/kotlin) contains other implementation for Times.

object FinalTimes : Times { override fun now(): Duration { return System.currentTimeMillis().milliseconds } }

The structure of such a project will basically be as follows:

app/src/main/kotlin/test/kotlin/ss/App.kt app/src/main/kotlin/test/kotlin/ss/Times.kt app/src/mock/kotlin/test/kotlin/ss/FinalTimes.kt app/src/real/kotlin/test/kotlin/ss/FinalTimes.kt

I added a new task to build.gradle.kts that should compile and run the code from main along with the code from the mock:

sourceSets.create("mock") { kotlin.srcDirs("src/main/kotlin") } tasks.register<JavaExec>("runMock") { classpath(sourceSets["mock"].runtimeClasspath) mainClass = "test.kotlin.ss.AppKt" }

By running the gradle app:runMock command, you can see that the code compiled successfully and ran. Then I did the same for real. And now gradle app:runMock prints now: 0s, and gradle app:runReal prints now: 20467d 16h 46m 51.296s (depending on the environment).


My problem is that the IDEA for code highlighting still uses source compilation separately.

main does not see mock main does not see mock

mock does not see main mock does not see main


It's worth clarifying that one does not inherit the other. This would be the case with main and test, where test knows about main, but main doesn't know about test. Therefore, simply adding compilations["mock"].associateWith(compilations["main"]) will not be enough. In my case, it should be a common code base. mock knows about Times in main. And main knows about FinalTimes in mock.


I'm inspired by how this separation of sources works for Android projects. The Android plugin for Gradle allows you to separate sources into build types and project flavors. And in Android Studio itself, I can choose which specific set of these types and flavors I want to use now when compiling.

Let's say there are two flavors mock/real and two types debug/release. I run the command gradle app:assembleMockDebug and I get an assembly that is compiled from the code that is located along the path:

src/main/kotlin src/mock/kotlin src/debug/kotlin

If I run the command gradle app:assembleRealRelease I get an assembly by path:

src/main/kotlin src/real/kotlin src/release/kotlin

To sum it up, the idea works. After all, gradle app:runMock compiles and runs correctly. I want to know how to configure IDEA so that it understands that at the moment I am only interested in build variant mock. That is, look at the code only from src/main/kotlin and src/mock/kotlin, and ignore src/real/kotlin. In the spirit of how Android Studio allows you to choose a build variant and then correctly processes the source code corresponding to the chosen variant.


Read Entire Article