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.
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
:
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
:
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:
It will usually print something like this (the variant name is jvm-api
):
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).