Is there a reason the `jsMain` source set doesn't ...
# gradle
c
Is there a reason the
jsMain
source set doesn't depend on the
commonMain
source set? I first learned of this here, but we couldn't find any documentation explaining why that is.
today i learned 2
t
ัั @Anton Lakotka [JB]
a
Ok, sorry for the long delay in the response. But actually, jsMain should depend on commonMain! But it does not immediately depend on it! It will be added after a certain evaluation round. There is NO GUARANTEE that the configuration state will be final after N'ths
afterEvaluate
block. We can only guarantee that the state is final once all tasks are configured and ready for execution. So this will not work:
Copy code
afterEvaluate {
    println(kotlin.sourceSets.getByName("jsMain").dependsOn)
}
but this should work:
Copy code
gradle.taskGraph.whenReady {
    println(kotlin.sourceSets.getByName("jsMain").dependsOn) // prints [source set commonMain]
}
However, you should never modify anything in the
gradle.taskGraph.whenReady
block. The state is final and is meant for reading purposes only. If you want to get all source sets of certain target (compilation) and do something with it (like collect all related resources and etc). You should use
KotlinCompilation.allSourceSets
API. Let me show you an example of how you can do that:
Copy code
kotlin {
   jvm()
   js()
   
   // user can configure source sets as they want
}

val archiveKotlinJsResources by tasks.registering(Zip::class) {
    val jsMainCompilation = kotlin.js().compilations.getByName("main")
    val allJsResources = project.files().from({
        jsMainCompilation.allKotlinSourceSets.map { it.resources.sourceDirectories }
    })
    from(allJsResources)
    archiveClassifier.set("kjs-assets")
}
With this setup, there is a guarantee that all intermediate source sets that are included to JS target will be present in
allKotlinSourceSets
when Gradle will configure Task Graph.
cc: @mbonnin @Edoardo Luppi as you til 'ed
thank you color 2
m
Somewhat related, I just realized today that
androidInstrumentedTest
does not depend on
commonTest
. Would be nice to have this diagram but with full source sets details (mermaid maybe?)
c
^ it did in the past, but was removed, because it would run your android unit tests twice
๐Ÿ‘ 1
m
Makes sense but also not the most intuitive at first
โž• 1
a
Somewhat related, I just realized today that
androidInstrumentedTest
does not depend on
commonTest
Yes, this is intentional. As some users want and others do not. That is why we made a possible to configure it on user side. here is how:
Copy code
kotlin {
    @OptIn(ExperimentalKotlinGradlePluginApi::class)
    androidTarget {
        instrumentedTestVariant {
            sourceSetTree.set(KotlinSourceSetTree.test) // make it depend on commonTest
            sourceSetTree.set(KotlinSourceSetTree.instrumentedTest) // make it to not depend on commonTest
        }
        unitTestVariant {
            sourceSetTree.set(KotlinSourceSetTree.test) // make it depend on commonTest
            sourceSetTree.set(KotlinSourceSetTree.unitTest) // make it to not depend on commonTest
        }
    }
}
This is an experimental API and gives the user a lot of flexibility. As you can change
sourceSetTree
to anything you want. But the configuration options I mentioned above are the most common ones.
๐Ÿ˜ฎ 1
today i learned 1
m
til twice ๐Ÿ˜„
I'd argue that the opposite default would be conceptually nicer though
Although conceptual beauty vs actual usage is always a tradeoff
a
Agree! It was just a huge pain in integration between KMP and AGP. Let's hope this year it will be improved . ๐Ÿ™‚
๐Ÿคž 1
m
The KMP ecosystem has already improved so much in 2023 ๐Ÿ’™ When the AGP/KGP integration story gets finalized you should throw a huge party ๐Ÿ™‚
thank you color 1
๐Ÿป 2
c
Who wants to get a drink at KotlinConf to celebrate build tool improvements? ๐Ÿ‘€
๐Ÿ™Œ 1
m
I'm in!
c
Think we can invite the #amper team? ๐Ÿ‘€
m
I've never used amper but the more the merrier!
c
Same. I'm fine with Gradle, but I'm looking closely to what they are doing
@Anton Lakotka [JB] where does
kotlin.js()
come from? I would have assumed from the
kotlin
extension, but it doesn't seem to be available on the
KotlinProjectExtension
type.
e
KotlinMultiplatformExtension
c
it's internal ๐Ÿ˜•
I'll just downcast for now ๐Ÿ˜•
a
Recommended way is to go with Gradle Idiomatic way of getting gradle plugin extensions. https://docs.gradle.org/current/userguide/custom_plugins.html#sec:getting_input_from_the_build Kotlin Multiplatform Gradle Plugin registers extension by name
kotlin
with type
KotlinMultiplatformExtension
this is stable and public API. So you can safely request it in your Gradle Plugin like this:
Copy code
project.extensions.getByType<KotlinMultiplatformExtension>()
If your plugin can work with both Multiplatform and Kotlin/JVM plugins then you can get it by name and do
when is
checks.
Copy code
val kotlinExtension = project.extensions.getByName>("kotlin")
when (kotlinExtension) {
  is KotlinMultiplatform -> TODO("KMP")
  is KotlinJvmProjectExtension -> TODO("JVM")
  else -> TODO("Unknown Plugin")
}
It is important to say that if you want to get an extension of some Gradle Plugin you should ensure that this plugin is applied. the safest way is to write this hook:
Copy code
project.plugins.withId("org.jetbrains.kotlin.multiplatform") {
    project.extensions.getByType<...>()
}
We may open to public these static accessors in the future. However I'm not sure if it is really needed at this moment.
e
@CLOVIS
It's internal
I apply the first rule of Java programmers: a private declaration is a public declaration, it just doesn't know it, yet.
๐Ÿ˜… 2
c
To close this thread, I applied the recommendations here and it indeed is much better than what we had previously. Thanks a lot!