diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithNativeShortcuts.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithNativeShortcuts.kt index baedaa7a1fe..fd208da6a7f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithNativeShortcuts.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinTargetContainerWithNativeShortcuts.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.dsl import org.gradle.api.Action +import org.jetbrains.kotlin.gradle.dsl.NativeTargetShortcutTrace.Companion.nativeTargetShortcutTrace import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet @@ -15,6 +16,7 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_MAIN_ import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet.Companion.COMMON_TEST_SOURCE_SET_NAME import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.tooling.core.extrasReadWriteProperty private const val SHORTCUTS_DEPRECATION_MESSAGE = "Use applyDefaultHierarchyTemplate() instead. " + @@ -52,12 +54,22 @@ interface KotlinTargetContainerWithNativeShortcuts : KotlinTargetContainerWithPr } private fun createIntermediateSourceSets( + trace: NativeTargetShortcutTrace, namePrefix: String, children: List, parent: DefaultSourceSets? = null, ): DefaultSourceSets { val main = createIntermediateSourceSet("${namePrefix}Main", children.map { it.main }, parent?.main) val test = createIntermediateSourceSet("${namePrefix}Test", children.map { it.test }, parent?.test) + + main.nativeTargetShortcutTrace = trace + test.nativeTargetShortcutTrace = trace + + children.forEach { child -> + child.main.nativeTargetShortcutTrace = trace + child.test.nativeTargetShortcutTrace = trace + } + return DefaultSourceSets(main, test) } @@ -70,7 +82,9 @@ interface KotlinTargetContainerWithNativeShortcuts : KotlinTargetContainerWithPr iosArm64("${namePrefix}Arm64"), iosX64("${namePrefix}X64") ) - createIntermediateSourceSets(namePrefix, targets.defaultSourceSets(), mostCommonSourceSets()) + createIntermediateSourceSets( + NativeTargetShortcutTrace("ios"), namePrefix, targets.defaultSourceSets(), mostCommonSourceSets() + ) targets.forEach { it.configure() } } @@ -108,7 +122,10 @@ interface KotlinTargetContainerWithNativeShortcuts : KotlinTargetContainerWithPr tvosArm64("${namePrefix}Arm64"), tvosX64("${namePrefix}X64") ) - createIntermediateSourceSets(namePrefix, targets.defaultSourceSets(), mostCommonSourceSets()) + createIntermediateSourceSets( + NativeTargetShortcutTrace("tvos"), + namePrefix, targets.defaultSourceSets(), mostCommonSourceSets() + ) targets.forEach { it.configure() } } @@ -149,12 +166,16 @@ interface KotlinTargetContainerWithNativeShortcuts : KotlinTargetContainerWithPr val simulatorX64 = watchosX64("${namePrefix}X64") val deviceTargets = listOf(device32, device64) + val trace = NativeTargetShortcutTrace("watchos") + val deviceSourceSets = createIntermediateSourceSets( + trace, "${namePrefix}Device", deviceTargets.defaultSourceSets() ) createIntermediateSourceSets( + trace, namePrefix, listOf(deviceSourceSets, simulatorX64.defaultSourceSets()), mostCommonSourceSets() @@ -192,3 +213,9 @@ interface KotlinTargetContainerWithNativeShortcuts : KotlinTargetContainerWithPr @Deprecated(SHORTCUTS_DEPRECATION_MESSAGE) fun watchos(configure: Action) = watchos { configure.execute(this) } } + +internal class NativeTargetShortcutTrace(val shortcut: String) : Throwable() { + companion object { + var KotlinSourceSet.nativeTargetShortcutTrace by extrasReadWriteProperty() + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt index e8efdef2f27..3c89d5103fc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.plugin.diagnostics import org.gradle.api.Project import org.jetbrains.kotlin.gradle.InternalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinSourceSetConvention.isRegisteredByKotlinSourceSetConventionAt +import org.jetbrains.kotlin.gradle.dsl.NativeTargetShortcutTrace import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.KotlinTarget import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider @@ -464,6 +465,33 @@ object KotlinToolingDiagnostics { ) } + object KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected : ToolingDiagnosticFactory(WARNING) { + internal operator fun invoke(project: Project, trace: NativeTargetShortcutTrace) = build( + """ + The Default Kotlin Hierarchy was not applied to '${project.displayName}': + Deprecated '${trace.shortcut}()' shortcut was used: + + kotlin { + ${trace.shortcut}() + } + + Could be replaced by declaring the supported ${trace.shortcut} targets directly: + + kotlin { + ${trace.shortcut}X64() + ${trace.shortcut}Arm64() + ${trace.shortcut}SimulatorArm64() /* <- Note: Was not previously applied */ + /* ... */ + } + + To suppress the 'Default Hierarchy Template' add + '$KOTLIN_MPP_APPLY_DEFAULT_HIERARCHY_TEMPLATE=false' + to your gradle.properties + """.trimIndent(), + throwable = trace + ) + } + object KotlinDefaultHierarchyFallbackIllegalTargetNames : ToolingDiagnosticFactory(WARNING) { operator fun invoke(project: Project, illegalTargetNamesUsed: Iterable) = build( """ diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/hierarchy/defaultKotlinHierarchySetup.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/hierarchy/defaultKotlinHierarchySetup.kt index aaa808e0fa1..37096fa3dfc 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/hierarchy/defaultKotlinHierarchySetup.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/hierarchy/defaultKotlinHierarchySetup.kt @@ -6,15 +6,19 @@ package org.jetbrains.kotlin.gradle.plugin.hierarchy import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.NativeTargetShortcutTrace.Companion.nativeTargetShortcutTrace import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.* -import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPlugin.Companion.dynamicallyApplyWhenAndroidPluginIsApplied +import org.jetbrains.kotlin.gradle.plugin.KotlinHierarchyTemplate import org.jetbrains.kotlin.gradle.plugin.KotlinPluginLifecycle.Stage.FinaliseRefinesEdges +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackDependsOnUsageDetected import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackIllegalTargetNames +import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics.KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected import org.jetbrains.kotlin.gradle.plugin.diagnostics.kotlinToolingDiagnosticsCollector +import org.jetbrains.kotlin.gradle.plugin.diagnostics.reportDiagnostic +import org.jetbrains.kotlin.gradle.plugin.requiredStage import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName internal suspend fun Project.setupDefaultKotlinHierarchy() = requiredStage(FinaliseRefinesEdges) setup@{ @@ -29,6 +33,23 @@ internal suspend fun Project.setupDefaultKotlinHierarchy() = requiredStage(Final return@setup } + /* + User used ios(), tvos(), ... shortcuts. + This will be incompatible with the default template, as it sets dependsOn edges. + We detect this case manually to give even better diagnostic + */ + run check@{ + extension.sourceSets + .mapNotNull { it.nativeTargetShortcutTrace } + .toSet() + .ifEmpty { return@check } + .onEach { trace -> project.reportDiagnostic(KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected(project, trace)) } + + setupPreMultiplatformStableDefaultDependsOnEdges() + return@setup + } + + /* User manually added a .dependsOn: We fall back to the old behaviour and add the commonMain/commonTest default edges diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/DefaultHierarchySetupDiagnosticTest.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/DefaultHierarchySetupDiagnosticTest.kt index 69067eded1b..c22cb6bb01f 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/DefaultHierarchySetupDiagnosticTest.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/diagnosticsTests/DefaultHierarchySetupDiagnosticTest.kt @@ -15,6 +15,8 @@ import org.jetbrains.kotlin.gradle.plugin.launchInStage import org.jetbrains.kotlin.gradle.util.buildProjectWithMPP import org.jetbrains.kotlin.gradle.util.checkDiagnostics import org.jetbrains.kotlin.gradle.util.runLifecycleAwareTest +import org.jetbrains.kotlin.konan.target.HostManager +import org.junit.Assume import kotlin.test.Test class DefaultHierarchySetupDiagnosticTest { @@ -44,4 +46,49 @@ class DefaultHierarchySetupDiagnosticTest { project.checkDiagnostics("KotlinDefaultHierarchyFallbackIllegalTargetNames") } } -} \ No newline at end of file + + @Test + fun `test - warning - ios shortcut`() { + Assume.assumeTrue(HostManager.hostIsMac) + + buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + @Suppress("DEPRECATION") + kotlin.ios() + + project.launchInStage(KotlinPluginLifecycle.Stage.ReadyForExecution) { + project.checkDiagnostics("KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-ios") + } + } + } + + @Test + fun `test - warning - watchos shortcut`() { + Assume.assumeTrue(HostManager.hostIsMac) + + buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + @Suppress("DEPRECATION") + kotlin.watchos() + + project.launchInStage(KotlinPluginLifecycle.Stage.ReadyForExecution) { + project.checkDiagnostics("KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-watchos") + } + } + } + + @Test + fun `test - warning - tvos shortcut`() { + Assume.assumeTrue(HostManager.hostIsMac) + + buildProjectWithMPP().runLifecycleAwareTest { + val kotlin = multiplatformExtension + @Suppress("DEPRECATION") + kotlin.tvos() + + project.launchInStage(KotlinPluginLifecycle.Stage.ReadyForExecution) { + project.checkDiagnostics("KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-tvos") + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-ios.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-ios.txt new file mode 100644 index 00000000000..ec50ad10542 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-ios.txt @@ -0,0 +1,19 @@ +[KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected | WARNING] The Default Kotlin Hierarchy was not applied to 'root project 'test'': +Deprecated 'ios()' shortcut was used: + + kotlin { + ios() + } + +Could be replaced by declaring the supported ios targets directly: + + kotlin { + iosX64() + iosArm64() + iosSimulatorArm64() /* <- Note: Was not previously applied */ + /* ... */ + } + +To suppress the 'Default Hierarchy Template' add + 'kotlin.mpp.applyDefaultHierarchyTemplate=false' +to your gradle.properties diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-tvos.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-tvos.txt new file mode 100644 index 00000000000..5f12d6dd4a0 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-tvos.txt @@ -0,0 +1,19 @@ +[KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected | WARNING] The Default Kotlin Hierarchy was not applied to 'root project 'test'': +Deprecated 'tvos()' shortcut was used: + + kotlin { + tvos() + } + +Could be replaced by declaring the supported tvos targets directly: + + kotlin { + tvosX64() + tvosArm64() + tvosSimulatorArm64() /* <- Note: Was not previously applied */ + /* ... */ + } + +To suppress the 'Default Hierarchy Template' add + 'kotlin.mpp.applyDefaultHierarchyTemplate=false' +to your gradle.properties diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-watchos.txt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-watchos.txt new file mode 100644 index 00000000000..e6139eedbd1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/resources/expectedDiagnostics/KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected-watchos.txt @@ -0,0 +1,19 @@ +[KotlinDefaultHierarchyFallbackNativeTargetShortcutUsageDetected | WARNING] The Default Kotlin Hierarchy was not applied to 'root project 'test'': +Deprecated 'watchos()' shortcut was used: + + kotlin { + watchos() + } + +Could be replaced by declaring the supported watchos targets directly: + + kotlin { + watchosX64() + watchosArm64() + watchosSimulatorArm64() /* <- Note: Was not previously applied */ + /* ... */ + } + +To suppress the 'Default Hierarchy Template' add + 'kotlin.mpp.applyDefaultHierarchyTemplate=false' +to your gradle.properties