From da073c9cc34551167d8d06586a6fad2841c8fd74 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Tue, 18 Apr 2023 18:45:58 +0200 Subject: [PATCH] [mpp] Warn about using pre-HMPP dependencies ^KT-58086 Fixed --- .../mpp/PreHmppDependenciesDeprecationIT.kt | 105 ++++++++++++++++++ .../gradle/testbase/outputAssertions.kt | 11 +- .../kotlin/gradle/testbase/testDsl.kt | 10 +- .../build.gradle.kts | 33 ++++++ .../gradle.properties | 1 + .../src/commonMain/kotlin/Foo.kt | 9 ++ .../build.gradle.kts | 25 +++++ .../build.gradle.kts | 20 ++++ .../build.gradle.kts | 3 + .../consumer/build.gradle.kts | 22 ++++ .../consumer/gradle.properties | 1 + .../gradle.properties | 1 + .../producer/build.gradle.kts | 14 +++ .../settings.gradle.kts | 1 + .../preHmppLibrary/build.gradle.kts | 24 ++++ .../preHmppLibrary/gradle.properties | 1 + .../src/commonMain/kotlin/Foo.kt | 9 ++ .../build.gradle.kts | 18 +++ .../simpleReport/build.gradle.kts | 18 +++ .../build.gradle.kts | 18 +++ .../gradle/plugin/PropertiesProvider.kt | 5 + .../mpp/internal/deprecationDiagnostics.kt | 1 + .../reportDeprecatedPreHmppDependencies.kt | 85 ++++++++++++++ 23 files changed, 431 insertions(+), 4 deletions(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/PreHmppDependenciesDeprecationIT.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/src/commonMain/kotlin/Foo.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsInPlatformSpecificSourceSets/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnPopularDependencies/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/producer/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/settings.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/gradle.properties create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/src/commonMain/kotlin/Foo.kt create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/reportFromTransitiveDependencies/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/simpleReport/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/transitiveDependencyUpgradesVersion/build.gradle.kts create mode 100644 libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/reportDeprecatedPreHmppDependencies.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/PreHmppDependenciesDeprecationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/PreHmppDependenciesDeprecationIT.kt new file mode 100644 index 00000000000..c7c2be4457a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/PreHmppDependenciesDeprecationIT.kt @@ -0,0 +1,105 @@ +package org.jetbrains.kotlin.gradle.mpp + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.plugin.mpp.internal.DEPRECATED_PRE_HMPP_LIBRARIES_DETECTED_MESSAGE +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.replaceText +import org.junit.jupiter.api.io.TempDir +import java.nio.file.Path +import kotlin.io.path.writeText + +@MppGradlePluginTests +class PreHmppDependenciesDeprecationIT : KGPBaseTest() { + + @GradleTest + fun testSimpleReport(gradleVersion: GradleVersion, @TempDir tempDir: Path) { + publishLibrary("preHmppLibrary", gradleVersion, tempDir) + checkDiagnostics(gradleVersion, "simpleReport", tempDir, expectReportForDependency = "preHmppLibrary") + } + + @GradleTest + fun testTransitiveDependencyUpgradesVersion(gradleVersion: GradleVersion, @TempDir tempDir: Path) { + // 0.1 + publishLibrary("preHmppLibrary", gradleVersion, tempDir) + + // 0.2 -- still pre-HMPP + publishLibrary("preHmppLibrary", gradleVersion, tempDir) { + buildGradleKts.replaceText("0.1", "0.2") + } + publishLibrary("hmppLibraryWithPreHmppInDependencies", gradleVersion, tempDir) { + buildGradleKts.replaceText("0.1", "0.2") + } + + // Check that even though the version of requested dependency is different from resolved, the report warning is still emitted + checkDiagnostics(gradleVersion, "transitiveDependencyUpgradesVersion", tempDir, expectReportForDependency = "preHmppLibrary") + } + + @GradleTest + fun noReportFromTransitiveDependencies(gradleVersion: GradleVersion, @TempDir tempDir: Path) { + publishLibrary("preHmppLibrary", gradleVersion, tempDir) + publishLibrary("hmppLibraryWithPreHmppInDependencies", gradleVersion, tempDir) + checkDiagnostics(gradleVersion, "reportFromTransitiveDependencies", tempDir) + } + + @GradleTest + fun noReportWhenSuppressed(gradleVersion: GradleVersion, @TempDir tempDir: Path) { + publishLibrary("preHmppLibrary", gradleVersion, tempDir) + checkDiagnostics(gradleVersion, "simpleReport", tempDir) { + gradleProperties.writeText("kotlin.mpp.allow.legacy.dependencies=true") + } + } + + @GradleTest + fun testNoWarningsOnPopularDependencies(gradleVersion: GradleVersion) { + checkDiagnostics(gradleVersion, "noWarningsOnPopularDependencies") + } + + @GradleTest + fun testNoWarningsOnProjectDependencies(gradleVersion: GradleVersion) { + checkDiagnostics(gradleVersion, "noWarningsOnProjectDependencies", taskToCall = ":consumer:dependencies") + } + + @GradleTest + fun testNoWarningsInPlatformSpecificSourceSets(gradleVersion: GradleVersion) { + checkDiagnostics(gradleVersion, "noWarningsInPlatformSpecificSourceSets") + } + + @GradleTest + fun testNoWarningsInPreHmppProjects(gradleVersion: GradleVersion, @TempDir tempDir: Path) { + publishLibrary("preHmppLibrary", gradleVersion, tempDir) + checkDiagnostics(gradleVersion, "simpleReport", tempDir) { + gradleProperties.writeText("kotlin.internal.mpp.hierarchicalStructureByDefault=false") + } + } + + private fun checkDiagnostics( + gradleVersion: GradleVersion, + projectName: String, + tempDir: Path? = null, + taskToCall: String = "dependencies", + expectReportForDependency: String? = null, + preBuildAction: TestProject.() -> Unit = {} + ) { + project("preHmppDependenciesDeprecation/$projectName", gradleVersion, localRepoDir = tempDir?.resolve("repo")) { + preBuildAction() + build(taskToCall) { + if (expectReportForDependency != null) { + assertOutputContainsExactlyTimes( + DEPRECATED_PRE_HMPP_LIBRARIES_DETECTED_MESSAGE.replace("{0}", ".*$expectReportForDependency.*").toRegex() + ) + } else { + assertOutputDoesNotContain( + DEPRECATED_PRE_HMPP_LIBRARIES_DETECTED_MESSAGE.replace("{0}", ".*").toRegex() + ) + } + } + } + } + + private fun publishLibrary(name: String, gradleVersion: GradleVersion, tempDir: Path, prePublishAction: TestProject.() -> Unit = {}) { + project("preHmppDependenciesDeprecation/$name", gradleVersion, localRepoDir = tempDir.resolve("repo")) { + prePublishAction() + build("publish") + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt index 4e055f8b9bb..9e0294ed67d 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt @@ -135,7 +135,14 @@ fun BuildResult.assertOutputContainsExactlyTimes( expected: String, expectedCount: Int = 1 ) { - val occurrenceCount = expected.toRegex(RegexOption.LITERAL).findAll(output).count() + assertOutputContainsExactlyTimes(expected.toRegex(RegexOption.LITERAL), expectedCount) +} + +fun BuildResult.assertOutputContainsExactlyTimes( + expected: Regex, + expectedCount: Int = 1 +) { + val occurrenceCount = expected.findAll(output).count() assert(occurrenceCount == expectedCount) { printBuildOutput() @@ -228,4 +235,4 @@ fun findParameterInOutput(name: String, output: String): String? = output.lineSequence().mapNotNull { line -> val (key, value) = line.split('=', limit = 2).takeIf { it.size == 2 } ?: return@mapNotNull null if (key.endsWith(name)) value else null - }.firstOrNull() \ No newline at end of file + }.firstOrNull() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt index 91e4fcd59f8..1c929dbf25c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt @@ -447,6 +447,8 @@ internal fun Path.addDefaultBuildFiles() { } internal fun Path.addPluginManagementToSettings() { + val buildGradle = resolve("build.gradle") + val buildGradleKts = resolve("build.gradle.kts") val settingsGradle = resolve("settings.gradle") val settingsGradleKts = resolve("settings.gradle.kts") when { @@ -474,7 +476,11 @@ internal fun Path.addPluginManagementToSettings() { } } - else -> settingsGradle.toFile().writeText(DEFAULT_GROOVY_SETTINGS_FILE) + Files.exists(buildGradle) -> settingsGradle.toFile().writeText(DEFAULT_GROOVY_SETTINGS_FILE) + + Files.exists(buildGradleKts) -> settingsGradleKts.toFile().writeText(DEFAULT_KOTLIN_SETTINGS_FILE) + + else -> error("No build-file or settings file found") } if (Files.exists(resolve("buildSrc"))) { @@ -653,4 +659,4 @@ internal fun TestProject.enableStableConfigurationCachePreview() { |enableFeaturePreview("STABLE_CONFIGURATION_CACHE") """.trimMargin() ) -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/build.gradle.kts new file mode 100644 index 00000000000..e46579b866c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + kotlin("multiplatform") + id("maven-publish") +} + +publishing { + repositories { + maven("") + } +} + +repositories { + mavenCentral() + mavenLocal() + maven("") +} + +group = "org.jetbrains.kotlin.tests" +version = "0.1" + +kotlin { + jvm() + js() + linuxX64() + + sourceSets { + val commonMain by getting { + dependencies { + api("org.jetbrains.kotlin.tests:preHmppLibrary:0.1") + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/gradle.properties new file mode 100644 index 00000000000..fc1efeed349 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/gradle.properties @@ -0,0 +1 @@ +kotlin.internal.mpp.hierarchicalStructureByDefault=true diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/src/commonMain/kotlin/Foo.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/src/commonMain/kotlin/Foo.kt new file mode 100644 index 00000000000..5c4a0996072 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/hmppLibraryWithPreHmppInDependencies/src/commonMain/kotlin/Foo.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package foo + +class Foo { +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsInPlatformSpecificSourceSets/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsInPlatformSpecificSourceSets/build.gradle.kts new file mode 100644 index 00000000000..87cdec4a097 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsInPlatformSpecificSourceSets/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + kotlin("multiplatform") + id("maven-publish") +} + +publishing { + repositories { + maven("") + } +} + +group = "org.jetbrains.kotlin.tests" +version = "0.1" + +kotlin { + jvm() + js() + linuxX64() + + listOf("jvmMain", "jsMain", "linuxX64Main").forEach { + sourceSets.getByName(it).dependencies { + implementation("org.jetbrains.kotlin.tests:preHmppLibrary:0.1") + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnPopularDependencies/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnPopularDependencies/build.gradle.kts new file mode 100644 index 00000000000..59bef0d6a33 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnPopularDependencies/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() + mavenLocal() +} + +kotlin { + jvm() + linuxX64() + + sourceSets.getByName("commonMain").dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("kotlin-test")) + + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/build.gradle.kts new file mode 100644 index 00000000000..05fd2cd299c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + kotlin("multiplatform") apply false +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/build.gradle.kts new file mode 100644 index 00000000000..b8dd17ef377 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() + mavenLocal() +} + +kotlin { + jvm() + js() + linuxX64() + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":producer")) + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/gradle.properties new file mode 100644 index 00000000000..fc1efeed349 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/consumer/gradle.properties @@ -0,0 +1 @@ +kotlin.internal.mpp.hierarchicalStructureByDefault=true diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/gradle.properties new file mode 100644 index 00000000000..96dc7c3f2b0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/gradle.properties @@ -0,0 +1 @@ +kotlin.internal.mpp.hierarchicalStructureByDefault=false diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/producer/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/producer/build.gradle.kts new file mode 100644 index 00000000000..ade3a487b78 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/producer/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() + mavenLocal() +} + +kotlin { + jvm() + js() + linuxX64() +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/settings.gradle.kts new file mode 100644 index 00000000000..2ca3448a947 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/noWarningsOnProjectDependencies/settings.gradle.kts @@ -0,0 +1 @@ +include("consumer", "producer") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/build.gradle.kts new file mode 100644 index 00000000000..e8b770ac93c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + kotlin("multiplatform") + id("maven-publish") +} + +publishing { + repositories { + maven("") + } +} + +repositories { + mavenCentral() + mavenLocal() +} + +group = "org.jetbrains.kotlin.tests" +version = "0.1" + +kotlin { + jvm() + js() + linuxX64() +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/gradle.properties new file mode 100644 index 00000000000..96dc7c3f2b0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/gradle.properties @@ -0,0 +1 @@ +kotlin.internal.mpp.hierarchicalStructureByDefault=false diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/src/commonMain/kotlin/Foo.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/src/commonMain/kotlin/Foo.kt new file mode 100644 index 00000000000..5c4a0996072 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/preHmppLibrary/src/commonMain/kotlin/Foo.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package foo + +class Foo { +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/reportFromTransitiveDependencies/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/reportFromTransitiveDependencies/build.gradle.kts new file mode 100644 index 00000000000..3465b15e857 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/reportFromTransitiveDependencies/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() + mavenLocal() + maven("") +} + +kotlin { + jvm() + linuxX64() + + sourceSets.getByName("commonMain").dependencies { + implementation("org.jetbrains.kotlin.tests:hmppLibraryWithPreHmppInDependencies:0.1") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/simpleReport/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/simpleReport/build.gradle.kts new file mode 100644 index 00000000000..fa069cd5f15 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/simpleReport/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenCentral() + mavenLocal() + maven("") +} + +kotlin { + jvm() + linuxX64() + + sourceSets.getByName("commonMain").dependencies { + implementation("org.jetbrains.kotlin.tests:preHmppLibrary:0.1") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/transitiveDependencyUpgradesVersion/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/transitiveDependencyUpgradesVersion/build.gradle.kts new file mode 100644 index 00000000000..8d28965cfa8 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/preHmppDependenciesDeprecation/transitiveDependencyUpgradesVersion/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("multiplatform") +} + +repositories { + mavenLocal() + maven("") +} + +kotlin { + jvm() + linuxX64() + + sourceSets.getByName("commonMain").dependencies { + implementation("org.jetbrains.kotlin.tests:hmppLibraryWithPreHmppInDependencies:0.2") + implementation("org.jetbrains.kotlin.tests:preHmppLibrary:0.1") + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index b382c2b03d9..518eed38c1d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLI import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_COMPILER_USE_PRECISE_COMPILATION_RESULTS_BACKUP import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_CREATE_DEFAULT_MULTIPLATFORM_PUBLICATIONS import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_JS_KARMA_BROWSERS +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ALLOW_LEGACY_DEPENDENCIES import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_GRADLE_PLUGIN_COMPATIBILITY_NO_WARN import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_ANDROID_STYLE_NO_WARN import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_MPP_ANDROID_SOURCE_SET_LAYOUT_VERSION @@ -517,6 +518,9 @@ internal class PropertiesProvider private constructor(private val project: Proje val runKotlinCompilerViaBuildToolsApi: Boolean get() = booleanProperty(KOTLIN_RUN_COMPILER_VIA_BUILD_TOOLS_API) ?: false + val allowLegacyMppDependencies: Boolean + get() = booleanProperty(KOTLIN_MPP_ALLOW_LEGACY_DEPENDENCIES) ?: false + /** * Retrieves a comma-separated list of browsers to use when running karma tests for [target] * @see KOTLIN_JS_KARMA_BROWSERS @@ -604,6 +608,7 @@ internal class PropertiesProvider private constructor(private val project: Proje const val KOTLIN_SUPPRESS_EXPERIMENTAL_IC_OPTIMIZATIONS_WARNING = "kotlin.compiler.suppressExperimentalICOptimizationsWarning" const val KOTLIN_CREATE_DEFAULT_MULTIPLATFORM_PUBLICATIONS = "kotlin.internal.mpp.createDefaultMultiplatformPublications" const val KOTLIN_RUN_COMPILER_VIA_BUILD_TOOLS_API = "kotlin.compiler.runViaBuildToolsApi" + const val KOTLIN_MPP_ALLOW_LEGACY_DEPENDENCIES = "kotlin.mpp.allow.legacy.dependencies" } companion object { diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt index 0cf5d157c02..e0169cdf87d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/deprecationDiagnostics.kt @@ -31,6 +31,7 @@ internal fun runDeprecationDiagnostics(project: Project) { project.runProjectConfigurationHealthCheckWhenEvaluated { checkAndReportDeprecatedNativeTargets(project) reportTargetsWithNonUniqueConsumableConfigurations(project) + checkAndReportPreHmppDependenciesUsage(project) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/reportDeprecatedPreHmppDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/reportDeprecatedPreHmppDependencies.kt new file mode 100644 index 00000000000..1f9c8b539c3 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/internal/reportDeprecatedPreHmppDependencies.kt @@ -0,0 +1,85 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.plugin.mpp.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.component.ComponentIdentifier +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.artifacts.result.ResolvedDependencyResult +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.Usage +import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages +import org.jetbrains.kotlin.gradle.plugin.mpp.resolvableMetadataConfiguration +import org.jetbrains.kotlin.gradle.plugin.sources.internal +import org.jetbrains.kotlin.gradle.targets.metadata.isKotlinGranularMetadataEnabled +import org.jetbrains.kotlin.gradle.utils.SingleWarningPerBuild +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +@Suppress("NAME_SHADOWING") +internal fun checkAndReportPreHmppDependenciesUsage( + project: Project, + reportWarning: (Project, String) -> Unit = DEFAULT_DIAGNOSTIC_REPORTER, +) { + if (!project.isKotlinGranularMetadataEnabled || project.kotlinPropertiesProvider.allowLegacyMppDependencies) return + + val metadataTarget = project.multiplatformExtensionOrNull?.targets?.matching { it is KotlinMetadataTarget }?.singleOrNull() + ?: return + + // Note that we do not inspect dependencies of compilations, because adding dependencies into compilation is esoteric enough on its own, + // and x2 esoteric for KotlinMetadataTarget + val sourceSetsInMetadataCompilations = metadataTarget.compilations.flatMapTo(mutableSetOf()) { it.allKotlinSourceSets } + val configurationsToInspect = sourceSetsInMetadataCompilations.map { it.internal.resolvableMetadataConfiguration } + + // Resolution of configurations can happen concurrently, so need to use thread-safe primitives + val reportedDependencies: MutableSet = Collections.newSetFromMap(ConcurrentHashMap()) + val processedDependencies: MutableSet = Collections.newSetFromMap(ConcurrentHashMap()) + + for (configuration in configurationsToInspect) { + configuration.incoming.afterResolve { configuration -> + val resolvedDependencies = configuration.resolutionResult.root.dependencies + .filterIsInstance() + // We don't want to report deprecation on transitive dependencies. Gradle will add them into list of 'dependencies', + // but will mark them as 'isConstraint' + .filter { it.selected.id is ModuleComponentIdentifier && !it.isConstraint } + + for (dependency in resolvedDependencies) { + val dependencyId = dependency.selected.id as ModuleComponentIdentifier + if (!processedDependencies.add(dependencyId)) continue + + if (isPreHmppDependency(dependency) && reportedDependencies.add(dependencyId)) { + reportWarning( + project, + DEPRECATED_PRE_HMPP_LIBRARIES_DETECTED_MESSAGE.replace("{0}", dependencyId.displayName) + ) + } + } + } + } +} + +private fun isPreHmppDependency(dependency: ResolvedDependencyResult): Boolean { + val attributes = dependency.resolvedVariant.attributes + val kotlinPlatformAttribute = attributes.getAttribute(Attribute.of(KotlinPlatformType.attribute.name, String::class.java)) + ?: return false + val usageAttribute = attributes.getAttribute(Attribute.of(Usage.USAGE_ATTRIBUTE.name, String::class.java)) ?: return false + + return kotlinPlatformAttribute == KotlinPlatformType.common.name && usageAttribute != KotlinUsages.KOTLIN_METADATA +} + +private val DEFAULT_DIAGNOSTIC_REPORTER = { project: Project, message: String -> + SingleWarningPerBuild.show(project, message) +} + +@InternalKotlinGradlePluginApi +val DEPRECATED_PRE_HMPP_LIBRARIES_DETECTED_MESSAGE = + "w: The dependency {0} was published in the legacy mode. Support for such dependencies will be removed in the future. " + + "See https://kotl.in/0b5kn8 for details."