diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt index d1931643cb8..c3c79dbc17c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt @@ -14,10 +14,7 @@ import org.jetbrains.kotlin.compilerRunner.konanVersion import org.jetbrains.kotlin.gradle.plugin.* import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider import org.jetbrains.kotlin.gradle.targets.native.DisabledNativeTargetsReporter -import org.jetbrains.kotlin.gradle.targets.native.internal.NativeDistributionTypeProvider -import org.jetbrains.kotlin.gradle.targets.native.internal.PlatformLibrariesGenerator -import org.jetbrains.kotlin.gradle.targets.native.internal.setupCInteropCommonizerDependencies -import org.jetbrains.kotlin.gradle.targets.native.internal.setupKotlinNativePlatformDependencies +import org.jetbrains.kotlin.gradle.targets.native.internal.* import org.jetbrains.kotlin.gradle.utils.NativeCompilerDownloader import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject import org.jetbrains.kotlin.konan.CompilerVersion @@ -92,8 +89,9 @@ abstract class AbstractKotlinNativeTargetPreset( } } - SingleActionPerProject.run(project, "setupCInteropCommonizerDependencies") { + SingleActionPerProject.run(project, "setupCInteropDependencies") { project.setupCInteropCommonizerDependencies() + project.setupCInteropPropagatedDependencies() } if (!konanTarget.enabledOnCurrentHost) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AddIntransitiveMetadataDependency.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AddIntransitiveMetadataDependency.kt new file mode 100644 index 00000000000..360fcef6c2a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/AddIntransitiveMetadataDependency.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2021 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.targets.native.internal + +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet + +/** + * Dependencies here are using a special configuration called 'intransitiveMetadataConfiguration'. + * This special configuration can tell the IDE that this dependencies shall *not* be transitively be visible + * to dependsOn edges. This is necessary for the way the commonizer handles it's "expect refinement" approach. + * In this mode, every source set will receive exactly one commonized library to analyze its source code with. + */ +internal fun Project.addIntransitiveMetadataDependencyIfPossible(sourceSet: DefaultKotlinSourceSet, dependency: FileCollection) { + val dependencyConfigurationName = + if (project.isIntransitiveMetadataConfigurationEnabled) sourceSet.intransitiveMetadataConfigurationName + else sourceSet.implementationMetadataConfigurationName + project.dependencies.add(dependencyConfigurationName, dependency) +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt index fdca7ed267d..ceb2f8621f9 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependencies.kt @@ -6,9 +6,7 @@ package org.jetbrains.kotlin.gradle.targets.native.internal import org.gradle.api.Project -import org.gradle.api.file.FileCollection import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet import org.jetbrains.kotlin.gradle.utils.filesProvider @@ -17,13 +15,11 @@ import java.io.File internal fun Project.setupCInteropCommonizerDependencies() { val kotlin = this.multiplatformExtensionOrNull ?: return - kotlin.targets.withType(KotlinMetadataTarget::class.java).all { target -> - target.compilations.withType(KotlinSharedNativeCompilation::class.java).all { compilation -> - setupCInteropCommonizerDependenciesForCompilation(compilation) - } + kotlin.forAllSharedNativeCompilations { compilation -> + setupCInteropCommonizerDependenciesForCompilation(compilation) } - kotlin.sourceSets.withType(DefaultKotlinSourceSet::class.java).all { sourceSet -> + kotlin.forAllDefaultKotlinSourceSets { sourceSet -> setupCInteropCommonizerDependenciesForIde(sourceSet) } } @@ -45,7 +41,7 @@ private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilatio private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: DefaultKotlinSourceSet) { val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return - addDependency(sourceSet, filesProvider files@{ + addIntransitiveMetadataDependencyIfPossible(sourceSet, filesProvider files@{ val directlyDependent = CInteropCommonizerDependent.from(project, sourceSet) val associateDependent = CInteropCommonizerDependent.fromAssociateCompilations(project, sourceSet) @@ -54,16 +50,3 @@ private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: Default } }) } - -/** - * Dependencies here are using a special configuration called 'intransitiveMetadataConfiguration'. - * This special configuration can tell the IDE that this dependencies shall *not* be transitively be visible - * to dependsOn edges. This is necessary for the way the commonizer handles it's "expect refinement" approach. - * In this mode, every source set will receive exactly one commonized library to analyze its source code with. - */ -private fun Project.addDependency(sourceSet: DefaultKotlinSourceSet, dependency: FileCollection) { - val dependencyConfigurationName = - if (project.isIntransitiveMetadataConfigurationEnabled) sourceSet.intransitiveMetadataConfigurationName - else sourceSet.implementationMetadataConfigurationName - project.dependencies.add(dependencyConfigurationName, dependency) -} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt index 79e6c461dfb..5a4e36b0383 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropCommonizerDependent.kt @@ -7,15 +7,11 @@ package org.jetbrains.kotlin.gradle.targets.native.internal import org.gradle.api.Project import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget -import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation import org.jetbrains.kotlin.gradle.plugin.mpp.associateWithTransitiveClosure -import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinSourceSetsIncludingDefault -import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropIdentifier.Scope import org.jetbrains.kotlin.gradle.utils.UnsafeApi @@ -80,7 +76,7 @@ internal fun CInteropCommonizerDependent.Factory.from( internal fun CInteropCommonizerDependent.Factory.from(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? { return from( compilation.project.getCommonizerTarget(compilation) as? SharedCommonizerTarget ?: return null, - compilation.findDependingNativeCompilations() + compilation.getImplicitlyDependingNativeCompilations() ) } @@ -104,23 +100,3 @@ internal fun CInteropCommonizerDependent.Factory.fromAssociateCompilations( .toSet() ) } - -private fun KotlinSharedNativeCompilation.findDependingNativeCompilations(): Set { - val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet() - val allParticipatingSourceSetsOfCompilation = allParticipatingSourceSets() - - return multiplatformExtension.targets - .flatMap { target -> target.compilations } - .filterIsInstance() - .filter { nativeCompilation -> nativeCompilation.allParticipatingSourceSets().containsAll(allParticipatingSourceSetsOfCompilation) } - .toSet() -} - -/** - * Some implementations of [KotlinCompilation] do not contain the default source set in - * [KotlinCompilation.kotlinSourceSets] or [KotlinCompilation.allKotlinSourceSets] - * see KT-45412 - */ -private fun KotlinCompilation<*>.allParticipatingSourceSets(): Set { - return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets() -} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropPropagatedDependencies.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropPropagatedDependencies.kt new file mode 100644 index 00000000000..c22d0b3c88c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CInteropPropagatedDependencies.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2021 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.targets.native.internal + +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet +import org.jetbrains.kotlin.gradle.tasks.CInteropProcess +import org.jetbrains.kotlin.gradle.utils.filesProvider +import java.io.File + +/** + * Will propagate "original"/"platform" cinterops to intermediate source sets + * and 'shared native' compilations if necessary. + * + * cinterops will be forwarded when a source set/ compilation has just a single platform + * dependee + * + * e.g. + * + * ``` + * kotlin { + * sourceSets { + * val nativeMain by sourceSets.creating + * val linuxX64Main by sourceSets.getting + * linuxX64Main.dependsOn(nativeMain) + * } + * } + * ``` + * + * In this example 'nativeMain' has only a single native + * target and a single native source set depending on it. + * All cinterops defined on linuxX64's main compilation shall be propagated + * to the 'nativeMain' source set (and its 'shared native' compilation) if it exists. + */ +internal fun Project.setupCInteropPropagatedDependencies() { + val kotlin = this.multiplatformExtensionOrNull ?: return + + kotlin.forAllSharedNativeCompilations { compilation -> + compilation.compileDependencyFiles += getPropagatedCInteropDependenciesOrEmpty(compilation) + } + + kotlin.forAllDefaultKotlinSourceSets { sourceSet -> + addIntransitiveMetadataDependencyIfPossible( + sourceSet, getPropagatedCInteropDependenciesOrEmpty(sourceSet) + ) + } +} + +private fun Project.getPropagatedCInteropDependenciesOrEmpty(sourceSet: DefaultKotlinSourceSet): FileCollection { + return filesProvider files@{ + /* + compatibility metadata variant will still register + a 'KotlinMetadataCompilation for 'commonMain' which is irrelevant here + */ + val compilations = CompilationSourceSetUtil.compilationsBySourceSets(this)[sourceSet].orEmpty() + .filter { compilation -> compilation !is KotlinMetadataCompilation } + + /* Participating in multiple compilations? -> can't propagate -> should be commonized */ + val compilation = compilations.singleOrNull() as? KotlinNativeCompilation ?: return@files emptySet() + + /* Source Set is directly included in compilation -> No need to add dependency again (will be handled already) */ + if (sourceSet in compilation.kotlinSourceSets) return@files emptySet() + getAllCInteropOutputFiles(compilation) + } +} + +private fun Project.getPropagatedCInteropDependenciesOrEmpty(compilation: KotlinSharedNativeCompilation) = filesProvider files@{ + val compilations = compilation.getImplicitlyDependingNativeCompilations() + val platformCompilation = compilations.singleOrNull() ?: return@files emptySet() + getAllCInteropOutputFiles(platformCompilation) +} + +private fun Project.getAllCInteropOutputFiles(compilation: KotlinNativeCompilation): List { + return compilation.cinterops + .map { interop -> interop.interopProcessingTaskName } + .mapNotNull { taskName -> tasks.findByName(taskName) as? CInteropProcess } + .map { it.outputFile } +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetDependingNativeCompilations.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetDependingNativeCompilations.kt new file mode 100644 index 00000000000..2f3b972c1eb --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/GetDependingNativeCompilations.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2021 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.targets.native.internal + +import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinSourceSetsIncludingDefault +import org.jetbrains.kotlin.gradle.plugin.sources.resolveAllDependsOnSourceSets + +internal fun KotlinSharedNativeCompilation.getImplicitlyDependingNativeCompilations(): Set { + val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet() + val allParticipatingSourceSetsOfCompilation = allParticipatingSourceSets() + + return multiplatformExtension.targets + .flatMap { target -> target.compilations } + .filterIsInstance() + .filter { nativeCompilation -> nativeCompilation.allParticipatingSourceSets().containsAll(allParticipatingSourceSetsOfCompilation) } + .toSet() +} + +/** + * Some implementations of [KotlinCompilation] do not contain the default source set in + * [KotlinCompilation.kotlinSourceSets] or [KotlinCompilation.allKotlinSourceSets] + * see KT-45412 + */ +private fun KotlinCompilation<*>.allParticipatingSourceSets(): Set { + return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets() +} diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/uitls.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/uitls.kt new file mode 100644 index 00000000000..6dd0407108c --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/uitls.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2021 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.targets.native.internal + +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet + +internal inline fun KotlinMultiplatformExtension.forAllSharedNativeCompilations( + crossinline action: (compilation: KotlinSharedNativeCompilation) -> Unit +) { + targets.withType(KotlinMetadataTarget::class.java).all { target -> + target.compilations.withType(KotlinSharedNativeCompilation::class.java).all { compilation -> + action(compilation) + } + } +} + +internal inline fun KotlinMultiplatformExtension.forAllDefaultKotlinSourceSets( + crossinline action: (sourceSet: DefaultKotlinSourceSet) -> Unit +) { + sourceSets.withType(DefaultKotlinSourceSet::class.java).all { sourceSet -> + action(sourceSet) + } +} \ No newline at end of file