Zac Sweers
09/20/2023, 7:24 PM> Configure project :circuit-retained
w: Following Kotlin Source Set groups can't depend on 'commonJvmTest' together as they belong to different Kotlin Source Set Trees.
Source Sets from 'instrumentedTest' Tree:
* 'androidInstrumentedTest'
Source Sets from 'test' Tree:
* 'jvmTest'
Please keep dependsOn edges only from one group and remove the others.
Screenshot of the snippet is below. I don't fully understand what "they belong to different Kotlin Source Set Trees" means in this context, and I'm not sure how else we can reuse compatible shared dependencies across jvm/android source sets. Is it just no longer possible now? And if so... why not? I think it'd be helpful if this warning linked an issue or doc like some of the others do
Edit: it also appears to be specific to instrumented test, no warning for unit tests.mbonnin
09/20/2023, 9:30 PMtest
and instrumentedTest
here?) you have to create a third compilation that you associate.kotlin {
jvm {
val testSharedCompilation = compilations.create("testShared")
compilations.getByName("test") {
associateWith(testSharedCompilation)
}
compilations.getByName("instrumentedTest") {
associateWith(testSharedCompilation)
}
}
}
Anton Lakotka [JB]
09/21/2023, 7:28 AMcommonTest
behaviour. Whether should it be included to Unit Tests, Instrumented tests or Both. It can be done via using source set tree API. For more info on how to do that you can read related Comment in the link above.
In practice it means that one Source Set can dependsOn
another only if they are from the same Source Set Tree.
By default (excluding android) Source Set Tree equals to compilation name. So there are by default two trees: main and test.
So this is allowed: jvmMain dependsOn commonMain
, iosX64 dependsOn nativeMain
, nativeMain dependsOn commonMain
.
This is not allowed: commonTest dependsOn commonMain
, iosX64Main dependsOn iosX64Test
and etc.
It is not allowed because of the nature of compilations and source set organization.
For example: iosX64Test
compilation would compile against all sources it depends on: iosX64Test, nativeTest, commonTest
. If you would add a dependsOn relation from commonTest
to commonMain
. Then upon iosX64Test
compilation sources from commonMain
would leak into this compilation. And if commonMain contained some expect classes or functions then they would fail to compile because corresponding actuals are not present.
What you usually need is to add one's compilation output as dependency to another compilation. For example
test compilations depend on main compilations. It is done through associateWith
mechanism. https://kotlinlang.org/docs/gradle-configure-project.html#associate-compiler-tasks
This is sample on how you can setup integration tests:
https://youtrack.jetbrains.com/issue/KT-61007/Multiplatform-with-multiple-targets-of-the[…]ith-Consumable-configurations-must-have-unique-attributes
Hope this answers your questions.mbonnin
09/21/2023, 7:51 AMif commonMain contained some expect classes or functions then they would fail to compile because corresponding actuals are not presentPlaying the devil's advocate there but how big of a problem would that be? If people start sharing sourceSets between compilations they have to add actuals to the corresponding compilations? That sounds acceptable?
Zac Sweers
09/21/2023, 6:41 PMandroidUnitTest dependsOn commonJvmTest
is allowed, but androidInstrumentedTest dependsOn commonJvmTest
is not, as both can use JVM sources no?Anton Lakotka [JB]
09/22/2023, 9:45 AMPlaying the devil's advocate there but how big of a problem would that be? If people start sharing sourceSets between compilations they have to add actuals to the corresponding compilations? That sounds acceptable?That was one of the reasons what can go wrong when someone adds "commonTest dependsOn commonMain" relation. If you want to share some code between "main" and "test" compilations. Then the only way is to create separate module and these compilations depend on it. In other words "main" compilations or "test" compilations are formed closed group of sources. They can't share any source set this is model invariant. Another analogy: if Kotlin Compiler had an ability to compile multiplatform code in a single invocation then "main" and "test" would be two separate compiler invocations. Just like in java gradle plugin. main and test source sets are compiled separately. Yes, nothing stops you from including a source file to both compiler invocations but why would you need that? And if you really need it then just add it to needed compilations.
kotlinSourceSet.kotlin.srcDir("path/to/extra/sources")
.
That all makes sense to me, what has me confused is thatboth these constructions are allowed. But you would need to "allow it" but setting correct SourceSetTree. And via setting SourceSetTree we also add dependsOn relation for users convenience Here is the list of all possible cases: NB: source set names are defined by Android Source Set Layout: https://kotlinlang.org/docs/multiplatform-android-layout.html please check it and migrate accordinglyis allowed, butandroidUnitTest dependsOn commonJvmTest
is not, as both can use JVM sources no?androidInstrumentedTest dependsOn commonJvmTest
androidTarget {
// Case 1 (Default one)
// androidInstrumentedTest can't see commonTest
// androidUnitTest can see commonTest
instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.instrumentedTest)
unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test)
// Case 2
// androidInstrumentedTest can see commonTest
// androidUnitTest can see commonTest
instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test)
unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.test)
// Case 3
// androidInstrumentedTest can see commonTest
// androidUnitTest can't see commonTest
instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.test)
unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.unitTest)
// Case 4
// androidInstrumentedTest can't see commonTest
// androidUnitTest can't see commonTest
instrumentedTestVariant.sourceSetTree.set(KotlinSourceSetTree.instrumentedTest)
unitTestVariant.sourceSetTree.set(KotlinSourceSetTree.unitTest)
}
mbonnin
09/22/2023, 10:03 AMNothing stops you from including a source file to both compiler invocations but why would you need that?Sharing utils functions and what not? Software engineers love to factor their stuff out 😄
And if you really need it then just add it to needed compilations.This works but then you have to declare the dependencies as well (also probably breaks IDE support, see below). My mental model of the kotlin SourceSet is that it encapsulates the sources with their dependencies so it's a bit self-contained and a convenient way to think about sources. All in all, looks like sharing code could be either: 1. sharing a common Gradle project 2. sharing a common Compilation 3. sharing a common SourceSet 4. sharing a common directory sourced with.kotlinSourceSet.kotlin.srcDir("path/to/extra/sources")
srcDir()
They all have different properties and probably IDE impact too. I think 4. breaks in the IDE because the same file is included in different compilations and if it uses symbols outside its own directory, this symbol might live in different source sets, which is hard to locate without expect/actual. So if 3. is not possible, that leaves us with 1. and 2. which is largely enough for all cases but I was just curious if there was a hard reason why 3. was not possible like IDE support or so.