Kotlin Project Model Design Documentation 1.0-master Help

Side topics

Module dependencies expansion algorithm

This visibility inference is not configurable by a user and always happens whenever both the consumer and the producer of a dependency are MPP projects.

We determine visibility for fragments in a conservative way, by ensuring that a fragment can’t see any other fragment whose declarations won’t be actually available during platform code generation. Having said that, it’s important to note that the algorithm is based on the variants structure of the project.

Here is a formal description of the algorithm. Consider skipping to the [Example] below before getting to complete understanding of the algorithm.


Consider a fragment s of a multiplatform project p that has a module dependency d that references q, which is another MPP module.

The resulting set R of q ’s fragments that is visible to s is determined as follows:

  • Let C be the set of the variants in the project p which include the fragment s in their refines-closure

  • As the dependency d gets expanded for each of the variant in C, a compatible variant of the project q is chosen. Let V be the set of the variants chosen for all of the variants in C.

  • For each variant v i in V, let R i be the set of fragments in the project q that participated in building the variant v i.

  • The intersection of all R i gives the resulting set R of the visible fragments.

Note that this requires that a dependency is declared as a dependency on the whole library, not separate dependencies on foo-metadata , foo-jvm, foo-js etc.

For an illustration, consider the following project structure, with the producer above and the consumer below. This picture only shows the project structure and does not show fragments visibility nor explains how the visibility is determined:

Here’s an example of how visibility is determined for the consumer’s fragment allJsMain:

Example of module dependency expansion

Example of module dependency expansion

Note that the visibility inference algorithm shows that the consumer’s fragment allJsMain should see the producer’s jsMain as well, but this doesn’t yet happen because currently, we can’t produce Kotlin metadata from sources that can use platform-specific dependencies and language features. This is a purely technical limitation.

And another example for the consumer’s source set jvmAndJsMain:

Example of module dependency expansion

Note that the consumer’s fragment commonMain would see exactly the same set of the producer’s fragments as it participates in the same set of variants.

However, if you add a Linux target to the consumer project, commonMain will automatically participate in its variants, and the new visibility results will show that the consumer’s commonMain only sees the producer’s commonMain and no other source sets.

Debugging variants

You can see which variant a dependency resolved to in a particular configuration by running:

./gradlew dependencyInsight --configuration fooCompileClasspath --dependency bar

It will usually print something like this (the variant name is jvm-api ):

$ gw dependencyInsight --configuration jvmCompileClasspath --dependency third-party-lib ... **> Task :dependencyInsight** com.example.thirdparty:third-party-lib:1.0 variant "jvm-api" [ org.gradle.usage = java-api-jars (compatible with: java-api) org.jetbrains.kotlin.platform.type = jvm org.gradle.status = release (not requested) ] com.example.thirdparty:third-party-lib:1.0 \--- jvmCompileClasspath

Here, fooCompileClasspath is a configuration name, like jvmCompileClasspath, and bar is a substring of the dependency’s group and version (for com.example:bar:lib-bar, it is enough to specify bar ). The particular configuration name for a certain compilation can be taken from a KotlinCompilation' s compileDependencyConfigurationName and runtimeDependencyConfigurationName.

For project("...") dependencies, the variant names that Gradle prints are actually the names of the configurations like jvmApiElements, and these configurations are internally matched with the published variants like jvm-api by the visibility inference code.

The full list of variants is available in the Gradle module metadata (*.module) of a published library and in the Kotlin HMPP structure metadata generated by the task generateProjectStructureMetadata and put under build/kotlinProjectStructureMetadata/kotlin-project-structure-metadata.xml (note that the latter only lists platform variants and does not list the Kotlin metadata variants).

Last modified: 20 May 2021