[Gradle] Implement setupCInteropPropagatedDependencies

This commit will forward/propagate cinterops to intermediate source sets
with only one native dependent (and their metadata compilation)

^KT-47523 Verification Pending
This commit is contained in:
sebastian.sellmair
2021-08-19 14:58:02 +02:00
committed by Space
parent fee670e1a5
commit 882d72b1f7
7 changed files with 181 additions and 51 deletions
@@ -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<T : KotlinNativeTarget>(
}
}
SingleActionPerProject.run(project, "setupCInteropCommonizerDependencies") {
SingleActionPerProject.run(project, "setupCInteropDependencies") {
project.setupCInteropCommonizerDependencies()
project.setupCInteropPropagatedDependencies()
}
if (!konanTarget.enabledOnCurrentHost) {
@@ -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)
}
@@ -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)
}
@@ -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<KotlinNativeCompilation> {
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet()
val allParticipatingSourceSetsOfCompilation = allParticipatingSourceSets()
return multiplatformExtension.targets
.flatMap { target -> target.compilations }
.filterIsInstance<KotlinNativeCompilation>()
.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<KotlinSourceSet> {
return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets()
}
@@ -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<File>()
/* 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<File>()
getAllCInteropOutputFiles(compilation)
}
}
private fun Project.getPropagatedCInteropDependenciesOrEmpty(compilation: KotlinSharedNativeCompilation) = filesProvider files@{
val compilations = compilation.getImplicitlyDependingNativeCompilations()
val platformCompilation = compilations.singleOrNull() ?: return@files emptySet<File>()
getAllCInteropOutputFiles(platformCompilation)
}
private fun Project.getAllCInteropOutputFiles(compilation: KotlinNativeCompilation): List<File> {
return compilation.cinterops
.map { interop -> interop.interopProcessingTaskName }
.mapNotNull { taskName -> tasks.findByName(taskName) as? CInteropProcess }
.map { it.outputFile }
}
@@ -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<KotlinNativeCompilation> {
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet()
val allParticipatingSourceSetsOfCompilation = allParticipatingSourceSets()
return multiplatformExtension.targets
.flatMap { target -> target.compilations }
.filterIsInstance<KotlinNativeCompilation>()
.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<KotlinSourceSet> {
return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets()
}
@@ -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)
}
}