https://kotlinlang.org logo
#eap
Title
# eap
z

Zac Sweers

09/20/2023, 7:24 PM
I'm trying to understand this warning in 1.9.20-Beta
Copy code
> 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.
m

mbonnin

09/20/2023, 9:30 PM
I think if you want to reuse between different compilations (
test
and
instrumentedTest
here?) you have to create a third compilation that you associate.
I'd say something like this:
Copy code
kotlin {
    jvm {
        val testSharedCompilation = compilations.create("testShared")

        compilations.getByName("test") {
            associateWith(testSharedCompilation)
        }
        compilations.getByName("instrumentedTest") {
            associateWith(testSharedCompilation)
        }
    }
}
@Anton Lakotka [JB] will know more
a

Anton Lakotka [JB]

09/21/2023, 7:28 AM
Hello! I'm sorry for the confusion that this diagnostic message gives to you. I'll discuss wether or not it is possible to update messaging to be more clear. Perhaps, with some code samples and etc. So let me do some brief explanation here. Source Set Tree concept was introduced as part of this YT issue https://youtrack.jetbrains.com/issue/KT-34662/Provide-an-option-for-Android-targets-to-c[…]-tests-as-unit-tests-only-instrumented-tests-only-or-both It gives an ability to android android developers to control
commonTest
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.
thank you color 1
m

mbonnin

09/21/2023, 7:51 AM
if commonMain contained some expect classes or functions then they would fail to compile because corresponding actuals are not present
Playing 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?
z

Zac Sweers

09/21/2023, 6:41 PM
Thanks for the detailed writeup! That all makes sense to me, what has me confused is that
androidUnitTest dependsOn commonJvmTest
is allowed, but
androidInstrumentedTest dependsOn commonJvmTest
is not, as both can use JVM sources no?
a

Anton Lakotka [JB]

09/22/2023, 9:45 AM
Playing 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 that
androidUnitTest dependsOn commonJvmTest
is allowed, but
androidInstrumentedTest dependsOn commonJvmTest
is not, as both can use JVM sources no?
both 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 accordingly
Copy code
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)
    }
m

mbonnin

09/22/2023, 10:03 AM
Nothing 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.
kotlinSourceSet.kotlin.srcDir("path/to/extra/sources")
.
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
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.
5 Views