Skip to content

Commit 95577e9

Browse files
committed
Improve runtime libraries compatibility check in Compose projects
1 parent 0bb15d4 commit 95577e9

File tree

2 files changed

+64
-26
lines changed

2 files changed

+64
-26
lines changed

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/RuntimeLibrariesCompatibilityCheck.kt

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ private fun KotlinTarget.configureRuntimeLibrariesCompatibilityCheck() {
5252
joinLowerCamelCase("check", target.name, compilation.name, "composeLibrariesCompatibility"),
5353
) {
5454
expectedVersion.set(composeVersion)
55+
projectPath.set(project.path)
56+
configurationName.set(runtimeDependencyConfigurationName)
5557
runtimeDependencies.set(provider { config.incoming.resolutionResult.allComponents })
5658
}
5759
compilation.compileTaskProvider.dependsOn(task)
@@ -60,8 +62,10 @@ private fun KotlinTarget.configureRuntimeLibrariesCompatibilityCheck() {
6062

6163
internal abstract class RuntimeLibrariesCompatibilityCheck : DefaultTask() {
6264
private companion object {
63-
const val FOUNDATION_DEP = "org.jetbrains.compose.foundation:foundation"
64-
const val UI_DEP = "org.jetbrains.compose.ui:ui"
65+
val librariesForCheck = listOf(
66+
"org.jetbrains.compose.foundation:foundation",
67+
"org.jetbrains.compose.ui:ui"
68+
)
6569
}
6670

6771
@get:Inject
@@ -70,6 +74,12 @@ internal abstract class RuntimeLibrariesCompatibilityCheck : DefaultTask() {
7074
@get:Input
7175
abstract val expectedVersion: Property<String>
7276

77+
@get:Input
78+
abstract val projectPath: Property<String>
79+
80+
@get:Input
81+
abstract val configurationName: Property<String>
82+
7383
@get:Input
7484
abstract val runtimeDependencies: SetProperty<ResolvedComponentResult>
7585

@@ -82,21 +92,47 @@ internal abstract class RuntimeLibrariesCompatibilityCheck : DefaultTask() {
8292
@TaskAction
8393
fun run() {
8494
val expectedRuntimeVersion = expectedVersion.get()
85-
val deps = listOf(FOUNDATION_DEP, UI_DEP)
86-
runtimeDependencies.get().forEach { component ->
87-
component.moduleVersion?.let { lib ->
88-
val depName = lib.group + ":" + lib.name
89-
if (depName in deps) {
90-
val actualRuntimeVersion = lib.version
91-
if (actualRuntimeVersion != expectedRuntimeVersion) {
92-
logger.warn(
93-
"w: runtime dependency version mismatch!\n\t" +
94-
"expected: '$depName:$expectedRuntimeVersion', " +
95-
"actual: '$depName:$actualRuntimeVersion'"
96-
)
97-
}
98-
}
99-
}
95+
val foundLibs = runtimeDependencies.get().filter { component ->
96+
component.moduleVersion?.let { lib -> lib.group + ":" + lib.name } in librariesForCheck
97+
}
98+
val problems = foundLibs.mapNotNull { component ->
99+
val module = component.moduleVersion ?: return@mapNotNull null
100+
if (module.version == expectedRuntimeVersion) return@mapNotNull null
101+
ProblemLibrary(module.group + ":" + module.name, module.version)
102+
}
103+
104+
if (problems.isNotEmpty()) {
105+
logger.warn(
106+
getMessage(
107+
projectPath.get(),
108+
configurationName.get(),
109+
problems,
110+
expectedRuntimeVersion
111+
)
112+
)
113+
}
114+
}
115+
116+
private data class ProblemLibrary(val name: String, val version: String)
117+
118+
private fun getMessage(
119+
projectName: String,
120+
configurationName: String,
121+
problemLibs: List<ProblemLibrary>,
122+
expectedVersion: String
123+
): String = buildString {
124+
appendLine("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
125+
problemLibs.forEach { lib ->
126+
appendLine(" expected: '${lib.name}:$expectedVersion'")
127+
appendLine(" actual: '${lib.name}:${lib.version}'")
128+
appendLine()
100129
}
130+
appendLine("This may lead to compilation errors or unexpected behavior at runtime.")
131+
appendLine("Such version mismatch might be caused by dependency constrains in one of the included libraries.")
132+
val taskName = if (projectName.isNotEmpty() && !projectName.endsWith(":")) "$projectName:dependencies" else "${projectName}dependencies"
133+
appendLine("You can inspect resulted dependencies tree via `./gradlew $taskName --configuration ${configurationName}`.")
134+
appendLine("See more details in Gradle documentation: https://docs.gradle.org/current/userguide/viewing_debugging_dependencies.html#sec:listing-dependencies")
135+
appendLine()
136+
appendLine("Please update Compose Multiplatform gradle plugin version or align dependencies' versions to match the current plugin version.")
101137
}
102138
}

gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/RuntimeLibrariesCompatibilityCheckTest.kt

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,30 @@ class RuntimeLibrariesCompatibilityCheckTest : GradlePluginTestBase() {
1616
) {
1717
gradle("assembleAndroidMain").checks {
1818
check.logDoesntContain("checkAndroidMainComposeLibrariesCompatibility")
19-
check.logDoesntContain("w: runtime dependency version mismatch!")
19+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
2020
}
2121
gradle("metadataMainClasses").checks {
2222
check.logDoesntContain("checkMetadataMainComposeLibrariesCompatibility")
23-
check.logDoesntContain("w: runtime dependency version mismatch!")
23+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
2424
}
2525
gradle("jvmMainClasses").checks {
2626
check.taskSuccessful(":checkJvmMainComposeLibrariesCompatibility")
27-
check.logDoesntContain("w: runtime dependency version mismatch!")
27+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
2828
}
2929
gradle("jvmTestClasses").checks {
3030
check.taskSuccessful(":checkJvmMainComposeLibrariesCompatibility")
3131
check.taskSuccessful(":checkJvmTestComposeLibrariesCompatibility")
32-
check.logDoesntContain("w: runtime dependency version mismatch!")
32+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
3333
}
3434
gradle("wasmJsMainClasses").checks {
3535
check.taskSuccessful(":checkWasmJsMainComposeLibrariesCompatibility")
36-
check.logDoesntContain("w: runtime dependency version mismatch!")
36+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
3737
}
3838

3939
if (currentOS == OS.MacOS) {
4040
gradle("compileKotlinIosSimulatorArm64").checks {
4141
check.taskSuccessful(":checkIosSimulatorArm64MainComposeLibrariesCompatibility")
42-
check.logDoesntContain("w: runtime dependency version mismatch!")
42+
check.logDoesntContain("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
4343
}
4444
}
4545

@@ -49,9 +49,11 @@ class RuntimeLibrariesCompatibilityCheckTest : GradlePluginTestBase() {
4949
"api(\"org.jetbrains.compose.ui:ui\") { version { strictly(\"1.9.3\") } }"
5050
)
5151
}
52-
val msg = "w: runtime dependency version mismatch!\n\t" +
53-
"expected: 'org.jetbrains.compose.ui:ui:${defaultTestEnvironment.composeVersion}', " +
54-
"actual: 'org.jetbrains.compose.ui:ui:1.9.3'"
52+
val msg = buildString {
53+
appendLine("w: Compose Multiplatform runtime dependencies version didn't match with plugin version.")
54+
appendLine(" expected: 'org.jetbrains.compose.ui:ui:${defaultTestEnvironment.composeVersion}'")
55+
appendLine(" actual: 'org.jetbrains.compose.ui:ui:1.9.3'")
56+
}
5557
gradle("jvmMainClasses").checks {
5658
check.taskSuccessful(":checkJvmMainComposeLibrariesCompatibility")
5759
check.logContains(msg)

0 commit comments

Comments
 (0)