[Gradle] CInterop commonization: Improve grouping and dependency management
^KT-47775 Verification Pending This implementation will be lenient toward the common *Test.dependsOn(*Main) mistake. ^KT-47053 Verification Pending Support source sets that do not have a dedicated shared native compilation. Also support additional visible source sets coming from associate compilations.
This commit is contained in:
committed by
Space
parent
a7e81d5154
commit
02d4c866ca
+1
-1
@@ -135,10 +135,10 @@ class CommonizerIT : BaseGradleIT() {
|
||||
val expectedOutputDirectoryForBuild = projectDir.resolve("build/classes/kotlin/commonizer")
|
||||
|
||||
build(":copyCommonizeCInteropForIde") {
|
||||
assertSuccessful()
|
||||
assertTasksExecuted(":cinteropCurlTargetA")
|
||||
assertTasksExecuted(":cinteropCurlTargetB")
|
||||
assertTasksExecuted(":commonizeCInterop")
|
||||
assertSuccessful()
|
||||
|
||||
assertTrue(expectedOutputDirectoryForIde.isDirectory, "Missing output directory for IDE")
|
||||
assertTrue(expectedOutputDirectoryForBuild.isDirectory, "Missing output directory for build")
|
||||
|
||||
+380
-48
@@ -8,36 +8,27 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizationParameters
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerGroup
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.supports
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget.*
|
||||
import kotlin.test.*
|
||||
|
||||
class CInteropCommonizerTaskTest {
|
||||
class CInteropCommonizerTaskTest : MultiplatformExtensionTest() {
|
||||
|
||||
private lateinit var project: ProjectInternal
|
||||
private lateinit var kotlin: KotlinMultiplatformExtension
|
||||
private val task: CInteropCommonizerTask get() = project.commonizeCInteropTask?.get() ?: fail("Missing commonizeCInteropTask")
|
||||
|
||||
@BeforeTest
|
||||
fun setup() {
|
||||
project = ProjectBuilder.builder().build() as ProjectInternal
|
||||
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableGranularSourceSetsMetadata", "true")
|
||||
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableCInteropCommonization", "true")
|
||||
project.plugins.apply("kotlin-multiplatform")
|
||||
kotlin = project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
|
||||
override fun setup() {
|
||||
enableGranularSourceSetsMetadata()
|
||||
enableCInteropCommonization()
|
||||
super.setup()
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `nativeMain linux macos`() {
|
||||
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName")
|
||||
@@ -53,15 +44,18 @@ class CInteropCommonizerTaskTest {
|
||||
macosMain.dependsOn(nativeMain)
|
||||
|
||||
project.evaluate()
|
||||
val nativeMainCompilation = kotlin.targets.flatMap { it.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.single { it.defaultSourceSet == nativeMain }
|
||||
|
||||
val groups = task.getAllInteropsGroups()
|
||||
assertEquals(1, groups.size, "Expected only one InteropsGroup")
|
||||
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain)
|
||||
|
||||
assertEquals(
|
||||
CInteropCommonizationParameters(
|
||||
setOf(CommonizerTarget(LINUX_X64, MACOS_X64)), setOf(linuxInterop.identifier, macosInterop.identifier)
|
||||
CInteropCommonizerGroup(
|
||||
setOf(CommonizerTarget(LINUX_X64, MACOS_X64)),
|
||||
setOf(linuxInterop.identifier, macosInterop.identifier)
|
||||
),
|
||||
task.getCommonizationParameters(nativeMainCompilation)
|
||||
task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -80,17 +74,18 @@ class CInteropCommonizerTaskTest {
|
||||
macosMain.dependsOn(nativeMain)
|
||||
|
||||
project.evaluate()
|
||||
val nativeMainCompilation = kotlin.targets.flatMap { it.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.single { it.defaultSourceSet == nativeMain }
|
||||
|
||||
assertNull(
|
||||
task.getCommonizationParameters(nativeMainCompilation),
|
||||
findCInteropCommonizerDependent(nativeMain),
|
||||
"Expected no CInteropCommonizerTarget from nativeMain, since one target has not defined any cinterop"
|
||||
)
|
||||
|
||||
assertNull(
|
||||
findCInteropCommonizerDependent(expectSharedNativeCompilation(nativeMain)),
|
||||
"Expected no CInteropCommonizerTarget from nativeMain, since one target has not defined any cinterop"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun `nativeMain iosMain linux macos iosX64 iosArm64`() {
|
||||
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier
|
||||
@@ -116,33 +111,370 @@ class CInteropCommonizerTaskTest {
|
||||
project.evaluate()
|
||||
|
||||
assertEquals(
|
||||
CInteropCommonizationParameters(
|
||||
setOf(
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64),
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64, MACOS_X64, LINUX_X64)
|
||||
),
|
||||
setOf(linuxInterop, macosInterop, iosX64Interop, iosArm64Interop)
|
||||
), task.getCommonizationParameters(sharedNativeCompilation(nativeMain))
|
||||
1, task.getAllInteropsGroups().size,
|
||||
"Expected exactly one InteropsGroup for task"
|
||||
)
|
||||
|
||||
val group = CInteropCommonizerGroup(
|
||||
setOf(
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64, MACOS_X64, LINUX_X64),
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64)
|
||||
),
|
||||
setOf(
|
||||
linuxInterop, macosInterop, iosX64Interop, iosArm64Interop
|
||||
)
|
||||
)
|
||||
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(iosMain)
|
||||
|
||||
assertEquals(group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain)))
|
||||
assertEquals(group, task.findInteropsGroup(expectCInteropCommonizerDependent(iosMain)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nativeTest nativeMain linux macos`() {
|
||||
`nativeTest nativeMain linux macos`(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nativeTest nativeMain linux macos - nativeTest dependsOn nativeMain`() {
|
||||
`nativeTest nativeMain linux macos`(true)
|
||||
}
|
||||
|
||||
private fun `nativeTest nativeMain linux macos`(
|
||||
nativeTestDependsOnNativeMain: Boolean
|
||||
) {
|
||||
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier
|
||||
val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier
|
||||
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val nativeMain = kotlin.sourceSets.create("nativeMain")
|
||||
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
|
||||
val macosMain = kotlin.sourceSets.getByName("macosMain")
|
||||
|
||||
val nativeTest = kotlin.sourceSets.create("nativeTest")
|
||||
val linuxTest = kotlin.sourceSets.getByName("linuxTest")
|
||||
val macosTest = kotlin.sourceSets.getByName("macosTest")
|
||||
|
||||
linuxTest.dependsOn(nativeTest)
|
||||
macosTest.dependsOn(nativeTest)
|
||||
|
||||
linuxMain.dependsOn(nativeMain)
|
||||
macosMain.dependsOn(nativeMain)
|
||||
|
||||
nativeMain.dependsOn(commonMain)
|
||||
|
||||
if (nativeTestDependsOnNativeMain) {
|
||||
nativeTest.dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
|
||||
assertEquals(
|
||||
1, task.getAllInteropsGroups().size,
|
||||
"Expected exactly 1 'SharedInteropsGroup' for task"
|
||||
)
|
||||
|
||||
val group = CInteropCommonizerGroup(
|
||||
setOf(CommonizerTarget(LINUX_X64, MACOS_X64)),
|
||||
setOf(linuxInterop, macosInterop)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
CInteropCommonizationParameters(
|
||||
setOf(
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64),
|
||||
CommonizerTarget(IOS_X64, IOS_ARM64, MACOS_X64, LINUX_X64)
|
||||
),
|
||||
setOf(linuxInterop, macosInterop, iosX64Interop, iosArm64Interop)
|
||||
), task.getCommonizationParameters(sharedNativeCompilation(iosMain))
|
||||
expectCInteropCommonizerDependent(nativeMain),
|
||||
expectCInteropCommonizerDependent(expectSharedNativeCompilation(nativeMain)),
|
||||
"Expected same dependent from 'nativeMain' source set and 'nativeMain' compilation"
|
||||
)
|
||||
|
||||
assertTrue(
|
||||
task.getCommonizationParameters(sharedNativeCompilation(nativeMain))!!.supports(sharedNativeCompilation(nativeMain)),
|
||||
"Expected CInteropCommonizerTarget of nativeMain to support iosMain"
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
|
||||
assertNull(
|
||||
findCInteropCommonizerDependent(nativeTest),
|
||||
"Expected nativeTest to not depend on CInteropCommonizer"
|
||||
)
|
||||
}
|
||||
|
||||
private fun sharedNativeCompilation(sourceSet: KotlinSourceSet): KotlinSharedNativeCompilation {
|
||||
return kotlin.targets.flatMap { it.compilations }.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.single { it.defaultSourceSet == sourceSet }
|
||||
@Test
|
||||
fun `nativeTest nativeMain linux macos - test compilation defines custom cinterop`() {
|
||||
`nativeTest nativeMain linux macos - test compilation defines custom cinterop`(false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `nativeTest nativeMain linux macos - test compilation defines custom cinterop - nativeTest dependsOn nativeMain`() {
|
||||
`nativeTest nativeMain linux macos - test compilation defines custom cinterop`(true)
|
||||
}
|
||||
|
||||
private fun `nativeTest nativeMain linux macos - test compilation defines custom cinterop`(
|
||||
nativeTestDependsOnNativeMain: Boolean
|
||||
) {
|
||||
val linuxInterop = kotlin.linuxX64("linux").compilations.getByName("main").cinterops.create("anyInteropName").identifier
|
||||
val macosInterop = kotlin.macosX64("macos").compilations.getByName("main").cinterops.create("anyInteropName").identifier
|
||||
kotlin.linuxX64("linux").compilations.getByName("test").cinterops.create("anyOtherName").identifier
|
||||
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val nativeMain = kotlin.sourceSets.create("nativeMain")
|
||||
val linuxMain = kotlin.sourceSets.getByName("linuxMain")
|
||||
val macosMain = kotlin.sourceSets.getByName("macosMain")
|
||||
|
||||
val nativeTest = kotlin.sourceSets.create("nativeTest")
|
||||
val linuxTest = kotlin.sourceSets.getByName("linuxTest")
|
||||
val macosTest = kotlin.sourceSets.getByName("macosTest")
|
||||
|
||||
linuxTest.dependsOn(nativeTest)
|
||||
macosTest.dependsOn(nativeTest)
|
||||
|
||||
linuxMain.dependsOn(nativeMain)
|
||||
macosMain.dependsOn(nativeMain)
|
||||
|
||||
nativeMain.dependsOn(commonMain)
|
||||
|
||||
if (nativeTestDependsOnNativeMain) {
|
||||
nativeTest.dependsOn(nativeMain)
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
|
||||
assertEquals(
|
||||
1, task.getAllInteropsGroups().size,
|
||||
"Expected exactly 1 'SharedInteropsGroup' for task"
|
||||
)
|
||||
|
||||
val group = CInteropCommonizerGroup(
|
||||
setOf(CommonizerTarget(LINUX_X64, MACOS_X64)),
|
||||
setOf(linuxInterop, macosInterop)
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain))
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
group, task.findInteropsGroup(expectCInteropCommonizerDependent(expectSharedNativeCompilation(nativeMain)))
|
||||
)
|
||||
|
||||
assertNull(
|
||||
findCInteropCommonizerDependent(nativeTest),
|
||||
"Expected 'nativeTest' to not be CInteropCommonizer dependent"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hierarchical project - testSourceSetsDependOnMainSourceSets = true`() {
|
||||
`hierarchical project`(testSourceSetsDependOnMainSourceSets = true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `hierarchical project - testSourceSetsDependOnMainSourceSets = false`() {
|
||||
`hierarchical project`(testSourceSetsDependOnMainSourceSets = false)
|
||||
}
|
||||
|
||||
private fun `hierarchical project`(testSourceSetsDependOnMainSourceSets: Boolean) {
|
||||
/* Define targets */
|
||||
val linux = kotlin.linuxX64("linux")
|
||||
val macos = kotlin.macosX64("macos")
|
||||
val iosX64 = kotlin.iosX64()
|
||||
val iosArm64 = kotlin.iosArm64()
|
||||
val windows64 = kotlin.mingwX64("windows64")
|
||||
val windows32 = kotlin.mingwX86("windows32")
|
||||
kotlin.jvm()
|
||||
kotlin.js().browser()
|
||||
|
||||
val nativeTargets = listOf(linux, macos, iosX64, iosArm64, windows64, windows32)
|
||||
val windowsTargets = listOf(windows64, windows32)
|
||||
val unixLikeTargets = listOf(linux, macos, iosX64, iosArm64)
|
||||
val appleTargets = listOf(macos, iosX64, iosArm64)
|
||||
val iosTargets = listOf(iosX64, iosArm64)
|
||||
|
||||
/* Define interops */
|
||||
nativeTargets.map { target ->
|
||||
target.compilations.getByName("main").cinterops.create("nativeHelper").identifier
|
||||
}
|
||||
|
||||
nativeTargets.map { target ->
|
||||
target.compilations.getByName("test").cinterops.create("nativeTestHelper").identifier
|
||||
}
|
||||
|
||||
windowsTargets.map { target ->
|
||||
target.compilations.getByName("main").cinterops.create("windowsHelper").identifier
|
||||
}
|
||||
|
||||
unixLikeTargets.map { target ->
|
||||
target.compilations.getByName("main").cinterops.create("unixHelper").identifier
|
||||
}
|
||||
|
||||
appleTargets.map { target ->
|
||||
target.compilations.getByName("main").cinterops.create("appleHelper").identifier
|
||||
}
|
||||
|
||||
appleTargets.map { target ->
|
||||
target.compilations.getByName("test").cinterops.create("appleTestHelper").identifier
|
||||
}
|
||||
|
||||
iosTargets.map { target ->
|
||||
target.compilations.getByName("main").cinterops.create("iosHelper").identifier
|
||||
}
|
||||
|
||||
iosX64.compilations.getByName("main").cinterops.create("iosX64Helper").identifier
|
||||
iosX64.compilations.getByName("test").cinterops.create("iosX64TestHelper").identifier
|
||||
|
||||
/* Define source set hierarchy */
|
||||
val commonMain = kotlin.sourceSets.getByName("commonMain")
|
||||
val commonTest = kotlin.sourceSets.getByName("commonTest")
|
||||
val nativeMain = kotlin.sourceSets.create("nativeMain")
|
||||
val nativeTest = kotlin.sourceSets.create("nativeTest")
|
||||
val unixMain = kotlin.sourceSets.create("unixMain")
|
||||
val appleMain = kotlin.sourceSets.create("appleMain")
|
||||
val appleTest = kotlin.sourceSets.create("appleTest")
|
||||
val windowsMain = kotlin.sourceSets.create("windowsMain")
|
||||
val iosMain = kotlin.sourceSets.create("iosMain")
|
||||
val iosTest = kotlin.sourceSets.create("iosTest")
|
||||
|
||||
nativeMain.dependsOn(commonMain)
|
||||
nativeTest.dependsOn(commonTest)
|
||||
unixMain.dependsOn(nativeMain)
|
||||
appleMain.dependsOn(nativeMain)
|
||||
appleTest.dependsOn(nativeTest)
|
||||
windowsMain.dependsOn(nativeMain)
|
||||
iosMain.dependsOn(appleMain)
|
||||
|
||||
if (testSourceSetsDependOnMainSourceSets) {
|
||||
nativeTest.dependsOn(nativeMain)
|
||||
appleTest.dependsOn(appleMain)
|
||||
iosTest.dependsOn(iosMain)
|
||||
}
|
||||
|
||||
windowsTargets.forEach { target ->
|
||||
target.compilations.getByName("main").defaultSourceSet.dependsOn(windowsMain)
|
||||
}
|
||||
|
||||
iosTargets.forEach { target ->
|
||||
target.compilations.getByName("main").defaultSourceSet.dependsOn(iosMain)
|
||||
target.compilations.getByName("test").defaultSourceSet.dependsOn(iosTest)
|
||||
}
|
||||
|
||||
appleTargets.forEach { target ->
|
||||
target.compilations.getByName("main").defaultSourceSet.dependsOn(appleMain)
|
||||
target.compilations.getByName("test").defaultSourceSet.dependsOn(appleTest)
|
||||
}
|
||||
|
||||
unixLikeTargets.forEach { target ->
|
||||
target.compilations.getByName("main").defaultSourceSet.dependsOn(unixMain)
|
||||
}
|
||||
|
||||
nativeTargets.forEach { target ->
|
||||
target.compilations.getByName("main").defaultSourceSet.dependsOn(nativeMain)
|
||||
target.compilations.getByName("test").defaultSourceSet.dependsOn(nativeTest)
|
||||
}
|
||||
|
||||
project.evaluate()
|
||||
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(nativeMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(unixMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(windowsMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(appleMain)
|
||||
assertCInteropDependentEqualsForSourceSetAndCompilation(iosMain)
|
||||
|
||||
val groups = task.getAllInteropsGroups()
|
||||
assertEquals(2, groups.size, "Expected exactly two interop groups: main and test")
|
||||
|
||||
val nativeCommonizerTarget = SharedCommonizerTarget(nativeTargets.map { it.konanTarget })
|
||||
val unixLikeCommonizerTarget = SharedCommonizerTarget(unixLikeTargets.map { it.konanTarget })
|
||||
val windowsCommonizerTarget = SharedCommonizerTarget(windowsTargets.map { it.konanTarget })
|
||||
val appleCommonizerTarget = SharedCommonizerTarget(appleTargets.map { it.konanTarget })
|
||||
val iosCommonizerTarget = SharedCommonizerTarget(iosTargets.map { it.konanTarget })
|
||||
|
||||
val expectedMainGroup = CInteropCommonizerGroup(
|
||||
targets = setOf(
|
||||
nativeCommonizerTarget, unixLikeCommonizerTarget, windowsCommonizerTarget,
|
||||
appleCommonizerTarget, iosCommonizerTarget
|
||||
),
|
||||
interops = nativeTargets.map { target -> target.mainCinteropIdentifier("nativeHelper") }.toSet() +
|
||||
unixLikeTargets.map { target -> target.mainCinteropIdentifier("unixHelper") } +
|
||||
windowsTargets.map { target -> target.mainCinteropIdentifier("windowsHelper") } +
|
||||
appleTargets.map { target -> target.mainCinteropIdentifier("appleHelper") } +
|
||||
iosTargets.map { target -> target.mainCinteropIdentifier("iosHelper") } +
|
||||
iosX64.mainCinteropIdentifier("iosX64Helper")
|
||||
)
|
||||
|
||||
val expectedTestGroup = CInteropCommonizerGroup(
|
||||
targets = setOf(nativeCommonizerTarget, appleCommonizerTarget, iosCommonizerTarget),
|
||||
interops = nativeTargets.map { target -> target.testCinteropIdentifier("nativeTestHelper") }.toSet() +
|
||||
appleTargets.map { target -> target.testCinteropIdentifier("appleTestHelper") } +
|
||||
iosX64.testCinteropIdentifier("iosX64TestHelper")
|
||||
)
|
||||
|
||||
val mainGroup = groups.maxBy { it.targets.size }!!
|
||||
val testGroup = groups.minBy { it.targets.size }!!
|
||||
|
||||
assertEquals(
|
||||
expectedMainGroup, mainGroup,
|
||||
"mainGroup does not match"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expectedTestGroup, testGroup,
|
||||
"testGroup does not match"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeMain)),
|
||||
"Expected nativeMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(unixMain)),
|
||||
"Expected unixMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(windowsMain)),
|
||||
"Expected windowsMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(appleMain)),
|
||||
"Expected appleMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
mainGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(iosMain)),
|
||||
"Expected iosMain being part of the mainGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(nativeTest)),
|
||||
"Expected nativeTest being part of the testGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(appleTest)),
|
||||
"Expected appleTest being part of the testGroup"
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
testGroup, task.findInteropsGroup(expectCInteropCommonizerDependent(iosTest)),
|
||||
"Expected iosTest being part of the testGroup"
|
||||
)
|
||||
|
||||
kotlin.targets
|
||||
/* Shared K/N targets still are considered type common */
|
||||
.filter { it.platformType != KotlinPlatformType.common }
|
||||
.flatMap { it.compilations }.map { it.defaultSourceSet }.forEach { targetDefaultSourceSet ->
|
||||
assertNull(
|
||||
findCInteropCommonizerDependent(targetDefaultSourceSet),
|
||||
"Expected target source set ${targetDefaultSourceSet.name} not be CInteropCommonizerDependent"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertCInteropDependentEqualsForSourceSetAndCompilation(sourceSet: KotlinSourceSet) {
|
||||
assertEquals(
|
||||
expectCInteropCommonizerDependent(sourceSet),
|
||||
expectCInteropCommonizerDependent(expectSharedNativeCompilation(sourceSet)),
|
||||
"Expected found CInteropCommonizerDependent for source set '$sourceSet and it's shared native compilation to be equal"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Associate compilations are not yet supported by the IDE. KT-34102 */
|
||||
@file:Suppress("invisible_reference", "invisible_member", "FunctionName", "DuplicatedCode")
|
||||
|
||||
package org.jetbrains.kotlin.gradle
|
||||
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinCompilationData
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropCommonizerDependent
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.CInteropIdentifier
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.from
|
||||
import kotlin.test.BeforeTest
|
||||
import kotlin.test.assertNotNull
|
||||
|
||||
abstract class MultiplatformExtensionTest {
|
||||
protected val project: ProjectInternal = ProjectBuilder.builder().build() as ProjectInternal
|
||||
protected lateinit var kotlin: KotlinMultiplatformExtension
|
||||
|
||||
@BeforeTest
|
||||
open fun setup() {
|
||||
project.plugins.apply("kotlin-multiplatform")
|
||||
kotlin = project.extensions.getByName("kotlin") as KotlinMultiplatformExtension
|
||||
}
|
||||
|
||||
protected fun enableGranularSourceSetsMetadata() {
|
||||
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableGranularSourceSetsMetadata", "true")
|
||||
}
|
||||
|
||||
protected fun enableCInteropCommonization() {
|
||||
project.extensions.getByType(ExtraPropertiesExtension::class.java).set("kotlin.mpp.enableCInteropCommonization", "true")
|
||||
}
|
||||
|
||||
internal fun expectCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent {
|
||||
return assertNotNull(
|
||||
CInteropCommonizerDependent.from(compilation), "Can't find SharedInterops for ${compilation.name} compilation"
|
||||
)
|
||||
}
|
||||
|
||||
internal fun expectCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent {
|
||||
return assertNotNull(
|
||||
CInteropCommonizerDependent.from(project, sourceSet), "Can't find SharedInterops for ${sourceSet.name} source set"
|
||||
)
|
||||
}
|
||||
|
||||
internal fun findCInteropCommonizerDependent(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? {
|
||||
return CInteropCommonizerDependent.from(compilation)
|
||||
}
|
||||
|
||||
internal fun findCInteropCommonizerDependent(sourceSet: KotlinSourceSet): CInteropCommonizerDependent? {
|
||||
return CInteropCommonizerDependent.from(project, sourceSet)
|
||||
}
|
||||
|
||||
internal fun expectSharedNativeCompilation(sourceSet: KotlinSourceSet): KotlinSharedNativeCompilation {
|
||||
return kotlin.targets.flatMap { it.compilations }.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.single { it.defaultSourceSet == sourceSet }
|
||||
}
|
||||
|
||||
internal fun KotlinNativeTarget.mainCinteropIdentifier(name: String): CInteropIdentifier {
|
||||
return compilations.getByName("main").cinteropIdentifier(name)
|
||||
}
|
||||
|
||||
internal fun KotlinNativeTarget.testCinteropIdentifier(name: String): CInteropIdentifier {
|
||||
return compilations.getByName("test").cinteropIdentifier(name)
|
||||
}
|
||||
|
||||
internal fun KotlinCompilationData<*>.cinteropIdentifier(name: String): CInteropIdentifier {
|
||||
return CInteropIdentifier(CInteropIdentifier.Scope.create(this), name)
|
||||
}
|
||||
}
|
||||
-18
@@ -27,9 +27,6 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.statistics.KotlinBuildStatsService
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.*
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.commonizeCInteropTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.copyCommonizeCInteropForIdeTask
|
||||
import org.jetbrains.kotlin.gradle.targets.native.internal.getLibraries
|
||||
import org.jetbrains.kotlin.gradle.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.utils.addExtendsFromRelation
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
@@ -318,21 +315,6 @@ class KotlinMetadataTargetConfigurator :
|
||||
compileDependencyFiles = project.files()
|
||||
}
|
||||
}
|
||||
|
||||
if (this is KotlinSharedNativeCompilation) {
|
||||
project.commonizeCInteropTask?.let { task ->
|
||||
compileDependencyFiles = compileDependencyFiles.plus(task.getLibraries(this))
|
||||
}
|
||||
project.copyCommonizeCInteropForIdeTask?.let { task ->
|
||||
val libraries = task.getLibraries(this)
|
||||
kotlinSourceSetsIncludingDefault.filterIsInstance<DefaultKotlinSourceSet>().forEach { sourceSet ->
|
||||
val dependencyConfigurationName =
|
||||
if (project.isIntransitiveMetadataConfigurationEnabled) sourceSet.intransitiveMetadataConfigurationName
|
||||
else sourceSet.implementationMetadataConfigurationName
|
||||
project.dependencies.add(dependencyConfigurationName, libraries)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
@@ -16,6 +16,7 @@ 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.utils.NativeCompilerDownloader
|
||||
import org.jetbrains.kotlin.gradle.utils.SingleActionPerProject
|
||||
@@ -91,6 +92,10 @@ abstract class AbstractKotlinNativeTargetPreset<T : KotlinNativeTarget>(
|
||||
}
|
||||
}
|
||||
|
||||
SingleActionPerProject.run(project, "setupCInteropCommonizerDependencies") {
|
||||
project.setupCInteropCommonizerDependencies()
|
||||
}
|
||||
|
||||
if (!konanTarget.enabledOnCurrentHost) {
|
||||
with(HostManager()) {
|
||||
val supportedHosts = enabledByHost.filterValues { konanTarget in it }.keys
|
||||
|
||||
+14
-15
@@ -8,10 +8,10 @@ package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.fileName
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.base64Hash
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerOutputFileLayout.ensureMaxFileNameLength
|
||||
import org.jetbrains.kotlin.commonizer.identityString
|
||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||
import java.io.File
|
||||
|
||||
@@ -19,27 +19,26 @@ internal abstract class AbstractCInteropCommonizerTask : DefaultTask() {
|
||||
@get:OutputDirectory
|
||||
abstract val outputDirectory: File
|
||||
|
||||
internal fun outputDirectory(parameters: CInteropCommonizationParameters): File {
|
||||
internal fun outputDirectory(group: CInteropCommonizerGroup): File {
|
||||
val interopsDirectoryName = group.interops.map { it.interopName }.toSet().joinToString(";")
|
||||
val groupDisambiguation = group.targets.joinToString { it.identityString } +
|
||||
group.interops.joinToString { it.uniqueName }
|
||||
|
||||
return outputDirectory
|
||||
.resolve(parameters.targets.fileName)
|
||||
.resolve(parameters.interops.map { it.interopName }.distinct().joinToString("-"))
|
||||
.resolve(ensureMaxFileNameLength(interopsDirectoryName))
|
||||
.resolve(base64Hash(groupDisambiguation))
|
||||
}
|
||||
|
||||
internal abstract fun getCommonizationParameters(compilation: KotlinSharedNativeCompilation): CInteropCommonizationParameters?
|
||||
internal abstract fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup?
|
||||
|
||||
internal fun getLibraries(compilation: KotlinSharedNativeCompilation): FileCollection {
|
||||
val compilationCommonizerTarget = project.getCommonizerTarget(compilation) ?: return project.files()
|
||||
internal fun commonizedOutputLibraries(dependent: CInteropCommonizerDependent): FileCollection {
|
||||
val fileProvider = project.filesProvider {
|
||||
val parameters = getCommonizationParameters(compilation) ?: return@filesProvider emptySet<File>()
|
||||
val group = findInteropsGroup(dependent) ?: return@filesProvider emptySet<File>()
|
||||
CommonizerOutputFileLayout
|
||||
.resolveCommonizedDirectory(outputDirectory(parameters), compilationCommonizerTarget)
|
||||
.resolveCommonizedDirectory(outputDirectory(group), dependent.target)
|
||||
.listFiles().orEmpty().toSet()
|
||||
}
|
||||
|
||||
return fileProvider.builtBy(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun TaskProvider<out AbstractCInteropCommonizerTask>.getLibraries(compilation: KotlinSharedNativeCompilation): FileCollection {
|
||||
return compilation.target.project.files(map { it.getLibraries(compilation) })
|
||||
}
|
||||
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.KotlinMetadataTarget
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.sources.DefaultKotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.utils.filesProvider
|
||||
import java.io.File
|
||||
|
||||
// TODO NOW: Integration tests!
|
||||
|
||||
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.sourceSets.withType(DefaultKotlinSourceSet::class.java).all { sourceSet ->
|
||||
setupCInteropCommonizerDependenciesForIde(sourceSet)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.setupCInteropCommonizerDependenciesForCompilation(compilation: KotlinSharedNativeCompilation) {
|
||||
val cinteropCommonizerTask = project.commonizeCInteropTask ?: return
|
||||
|
||||
compilation.compileDependencyFiles += filesProvider {
|
||||
val cinteropCommonizerDependent = CInteropCommonizerDependent.from(compilation) ?: return@filesProvider emptySet<File>()
|
||||
cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IDE will resolve the dependencies provided on source sets.
|
||||
* This will use the [Project.copyCommonizeCInteropForIdeTask] over the regular cinterop commonization task.
|
||||
* The copying task prevent red code within the IDE after cleaning the build output.
|
||||
*/
|
||||
private fun Project.setupCInteropCommonizerDependenciesForIde(sourceSet: DefaultKotlinSourceSet) {
|
||||
val cinteropCommonizerTask = project.copyCommonizeCInteropForIdeTask ?: return
|
||||
|
||||
addDependency(sourceSet, filesProvider files@{
|
||||
val sourceSetCommonizerTarget = getCommonizerTarget(sourceSet)
|
||||
|
||||
val additionalVisibleSourceSets = sourceSet.getAdditionalVisibleSourceSets()
|
||||
.filter { sourceSet -> getCommonizerTarget(sourceSet) == sourceSetCommonizerTarget }
|
||||
|
||||
val cinteropCommonizerDependents = (additionalVisibleSourceSets + sourceSet).toSet()
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.from(this, sourceSet) }
|
||||
.toSet()
|
||||
|
||||
cinteropCommonizerDependents.map { cinteropCommonizerDependent ->
|
||||
cinteropCommonizerTask.get().commonizedOutputLibraries(cinteropCommonizerDependent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.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.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.CompilationSourceSetUtil.compilationsBySourceSets
|
||||
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
|
||||
|
||||
internal class CInteropCommonizerDependent @UnsafeApi constructor(
|
||||
val target: SharedCommonizerTarget,
|
||||
val scopes: Set<Scope>,
|
||||
val interops: Set<CInteropIdentifier>
|
||||
) {
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CInteropCommonizerDependent) return false
|
||||
if (this.target != other.target) return false
|
||||
if (this.scopes != other.scopes) return false
|
||||
if (this.interops != other.interops) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = target.hashCode()
|
||||
result = 31 * result + scopes.hashCode()
|
||||
result = 31 * result + interops.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
init {
|
||||
require(target.targets.isNotEmpty()) { "CInteropCommonizerDependent.target.targets.size can't be empty" }
|
||||
require(scopes.isNotEmpty()) { "CInteropCommonizerDependent.scopes can't be empty" }
|
||||
require(interops.isNotEmpty()) { "CInteropCommonizerDependent.interops can't be empty" }
|
||||
}
|
||||
|
||||
companion object Factory
|
||||
}
|
||||
|
||||
@OptIn(UnsafeApi::class)
|
||||
internal fun CInteropCommonizerDependent.Factory.from(
|
||||
target: SharedCommonizerTarget,
|
||||
compilations: Set<KotlinNativeCompilation>
|
||||
): CInteropCommonizerDependent? {
|
||||
target.targets.ifEmpty { return null }
|
||||
|
||||
/*
|
||||
Filter out compilations that have their associate also in the set of compilations
|
||||
e.g. do not include '*test' if their main counterpart is also present.
|
||||
*test and *main compilations will be included when build authors declare a *Test dependsOn *Main source set relationship.
|
||||
This relationship should not be declared, but we try to be lenient towards it here.
|
||||
*/
|
||||
val filteredCompilations = compilations.filter { compilation ->
|
||||
compilation.associateWithTransitiveClosure.none { associateCompilation -> associateCompilation in compilations }
|
||||
}.ifEmpty { return null }.toSet()
|
||||
|
||||
val scopes: Set<Scope> = filteredCompilations
|
||||
.map { compilation -> Scope.create(compilation) }.toSet()
|
||||
.ifEmpty { return null }
|
||||
|
||||
val interops: Set<CInteropIdentifier> = filteredCompilations
|
||||
.flatMap { compilation -> compilation.cinterops.ifEmpty { return null } }
|
||||
.map { cinterop -> cinterop.identifier }.toSet()
|
||||
|
||||
return CInteropCommonizerDependent(target, scopes, interops)
|
||||
}
|
||||
|
||||
internal fun CInteropCommonizerDependent.Factory.from(compilation: KotlinSharedNativeCompilation): CInteropCommonizerDependent? {
|
||||
return from(
|
||||
compilation.project.getCommonizerTarget(compilation) as? SharedCommonizerTarget ?: return null,
|
||||
compilation.findDependingNativeCompilations()
|
||||
)
|
||||
}
|
||||
|
||||
internal fun CInteropCommonizerDependent.Factory.from(project: Project, sourceSet: KotlinSourceSet): CInteropCommonizerDependent? {
|
||||
val target = project.getCommonizerTarget(sourceSet) as? SharedCommonizerTarget ?: return null
|
||||
val compilations = compilationsBySourceSets(project)[sourceSet] ?: return null
|
||||
|
||||
/* Non-native or non 'shared native' source sets can return eagerly */
|
||||
if (compilations.any { compilation -> compilation !is AbstractKotlinNativeCompilation }) {
|
||||
return null
|
||||
}
|
||||
|
||||
return from(
|
||||
target = target,
|
||||
compilations = compilations.filterIsInstance<KotlinNativeCompilation>().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()
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.tasks.Input
|
||||
import org.jetbrains.kotlin.commonizer.SharedCommonizerTarget
|
||||
import org.jetbrains.kotlin.gradle.utils.appendLine
|
||||
|
||||
internal data class CInteropCommonizerGroup(
|
||||
@get:Input val targets: Set<SharedCommonizerTarget>,
|
||||
@get:Input val interops: Set<CInteropIdentifier>
|
||||
) {
|
||||
@Suppress("deprecation")
|
||||
override fun toString(): String {
|
||||
return buildString {
|
||||
appendln("InteropsGroup {")
|
||||
appendln("targets: ")
|
||||
targets.sortedBy { it.targets.size }.forEach { target ->
|
||||
appendln(" $target")
|
||||
}
|
||||
appendln()
|
||||
appendln("interops: ")
|
||||
interops.sortedBy { it.toString() }.forEach { interop ->
|
||||
appendln(" $interop")
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
}
|
||||
}
|
||||
+57
-135
@@ -5,19 +5,18 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.commonizer.*
|
||||
import org.jetbrains.kotlin.commonizer.CommonizerDependency
|
||||
import org.jetbrains.kotlin.commonizer.NonTargetedCommonizerDependency
|
||||
import org.jetbrains.kotlin.commonizer.TargetedCommonizerDependency
|
||||
import org.jetbrains.kotlin.commonizer.allLeaves
|
||||
import org.jetbrains.kotlin.compilerRunner.GradleCliCommonizer
|
||||
import org.jetbrains.kotlin.compilerRunner.konanHome
|
||||
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull
|
||||
import org.jetbrains.kotlin.gradle.plugin.CInteropSettings
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
||||
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.DefaultCInteropSettings
|
||||
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
|
||||
@@ -48,12 +47,12 @@ internal open class CInteropCommonizerTask : AbstractCInteropCommonizerTask() {
|
||||
|
||||
@get:OutputDirectories
|
||||
val allOutputDirectories: Set<File>
|
||||
get() = getCommonizationParameters().map { outputDirectory(it) }.toSet()
|
||||
get() = getAllInteropsGroups().map { outputDirectory(it) }.toSet()
|
||||
|
||||
@Suppress("unused") // Used for UP-TO-DATE check
|
||||
@get:Classpath
|
||||
val commonizedNativeDistributionDependencies: Set<File>
|
||||
get() = getCommonizationParameters().flatMap { parameters -> parameters.targets }
|
||||
get() = getAllInteropsGroups().flatMap { group -> group.targets }
|
||||
.flatMap { target -> project.getNativeDistributionDependencies(target) }
|
||||
.toSet()
|
||||
|
||||
@@ -71,102 +70,80 @@ internal open class CInteropCommonizerTask : AbstractCInteropCommonizerTask() {
|
||||
this.cinterops += cinterops
|
||||
}
|
||||
|
||||
fun exclude(vararg tasks: CInteropProcess) {
|
||||
exclude(tasks.map { it.settings.identifier })
|
||||
}
|
||||
|
||||
fun exclude(vararg settings: CInteropSettings) {
|
||||
exclude(settings.mapNotNull { (it as? DefaultCInteropSettings)?.identifier })
|
||||
}
|
||||
|
||||
internal fun exclude(interopIdentifiers: List<CInteropIdentifier>) {
|
||||
this.cinterops = this.cinterops.filterTo(mutableSetOf()) { it.identifier !in interopIdentifiers }
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
internal fun commonizeCInteropLibraries() {
|
||||
getCommonizationParameters().forEach(::commonize)
|
||||
getAllInteropsGroups().forEach(::commonize)
|
||||
}
|
||||
|
||||
private fun commonize(parameters: CInteropCommonizationParameters) {
|
||||
val cinteropsForTarget = cinterops.filter { cinterop -> cinterop.identifier in parameters.interops }
|
||||
outputDirectory(parameters).deleteRecursively()
|
||||
private fun commonize(group: CInteropCommonizerGroup) {
|
||||
val cinteropsForTarget = cinterops.filter { cinterop -> cinterop.identifier in group.interops }
|
||||
outputDirectory(group).deleteRecursively()
|
||||
if (cinteropsForTarget.isEmpty()) return
|
||||
|
||||
GradleCliCommonizer(project).commonizeLibraries(
|
||||
konanHome = project.file(project.konanHome),
|
||||
outputTargets = parameters.targets,
|
||||
outputTargets = group.targets,
|
||||
inputLibraries = cinteropsForTarget.map { it.libraryFile.get() }.filter { it.exists() }.toSet(),
|
||||
dependencyLibraries = getNativeDistributionDependencies(parameters),
|
||||
outputDirectory = outputDirectory(parameters),
|
||||
dependencyLibraries = getNativeDistributionDependencies(group),
|
||||
outputDirectory = outputDirectory(group),
|
||||
logLevel = project.commonizerLogLevel
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNativeDistributionDependencies(parameters: CInteropCommonizationParameters): Set<CommonizerDependency> {
|
||||
return (parameters.targets + parameters.targets.allLeaves()).flatMapTo(mutableSetOf()) { target ->
|
||||
private fun getNativeDistributionDependencies(group: CInteropCommonizerGroup): Set<CommonizerDependency> {
|
||||
return (group.targets + group.targets.allLeaves()).flatMapTo(mutableSetOf()) { target ->
|
||||
project.getNativeDistributionDependencies(target).map { dependency -> TargetedCommonizerDependency(target, dependency) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSharedNativeCInterops(): Set<SharedNativeCInterops> {
|
||||
val sharedNativeCompilations = (project.multiplatformExtensionOrNull ?: return emptySet())
|
||||
.targets.flatMap { it.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
|
||||
fun buildSharedNativeCInterops(compilation: KotlinSharedNativeCompilation): SharedNativeCInterops? {
|
||||
return SharedNativeCInterops(
|
||||
target = project.getCommonizerTarget(compilation) as? SharedCommonizerTarget ?: return null,
|
||||
interops = project.getDependingNativeCompilations(compilation)
|
||||
/* If any dependee native compilation has no interop, then commonization is useless */
|
||||
.flatMap { nativeCompilation -> nativeCompilation.cinterops.ifEmpty { return null } }
|
||||
.map { interop -> interop.identifier }
|
||||
.toSet()
|
||||
)
|
||||
}
|
||||
|
||||
return sharedNativeCompilations.mapNotNull(::buildSharedNativeCInterops).toSet()
|
||||
.run(::removeNotRegisteredInterops)
|
||||
.run(::removeEmptyInterops)
|
||||
}
|
||||
|
||||
|
||||
@Nested
|
||||
internal fun getCommonizationParameters(): Set<CInteropCommonizationParameters> {
|
||||
val sharedNativeCInterops = getSharedNativeCInterops()
|
||||
if (sharedNativeCInterops.isEmpty()) return emptySet()
|
||||
|
||||
return sharedNativeCInterops.distinct()
|
||||
.filter { potentialRoot -> sharedNativeCInterops.none { other -> potentialRoot isProperSubsetOf other } }
|
||||
.map { root -> root to sharedNativeCInterops.filter { other -> other isProperSubsetOf root } }
|
||||
.mapTo(mutableSetOf()) { (root, subsets) ->
|
||||
CInteropCommonizationParameters(
|
||||
targets = subsets.mapTo(mutableSetOf()) { it.target } + root.target,
|
||||
interops = root.interops
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCommonizationParameters(compilation: KotlinSharedNativeCompilation): CInteropCommonizationParameters? {
|
||||
val supportedParameters = getCommonizationParameters().filter { parameters -> parameters.supports(compilation) }
|
||||
if (supportedParameters.isEmpty()) return null
|
||||
assert(supportedParameters.size == 1) {
|
||||
"Unnecessary work detected: Multiple commonization parameters seem to be doing redundant work"
|
||||
internal fun getAllInteropsGroups(): Set<CInteropCommonizerGroup> {
|
||||
val dependents = getAllDependents()
|
||||
val allScopes = dependents.map { it.scopes }.toSet()
|
||||
val rootScopes = allScopes.filter { scopes ->
|
||||
allScopes.none { otherScopes -> otherScopes != scopes && otherScopes.containsAll(scopes) }
|
||||
}
|
||||
return supportedParameters.first()
|
||||
|
||||
return rootScopes.map { scopes ->
|
||||
val dependentsForScopes = dependents.filter { dependent ->
|
||||
scopes.containsAll(dependent.scopes)
|
||||
}
|
||||
|
||||
CInteropCommonizerGroup(
|
||||
targets = dependentsForScopes.map { it.target }.toSet(),
|
||||
interops = dependentsForScopes.flatMap { it.interops }.toSet()
|
||||
)
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
}
|
||||
override fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? {
|
||||
val suitableGroups = getAllInteropsGroups().filter { group ->
|
||||
group.interops.containsAll(dependent.interops) && group.targets.contains(dependent.target)
|
||||
}
|
||||
|
||||
internal fun CInteropCommonizationParameters.supports(
|
||||
compilation: KotlinSharedNativeCompilation
|
||||
): Boolean {
|
||||
val project = compilation.project
|
||||
val commonizerTargetOfCompilation = project.getCommonizerTarget(compilation) ?: return false
|
||||
val interopsOfCompilation = project.getDependingNativeCompilations(compilation)
|
||||
.flatMap { it.cinterops }.map { it.identifier }
|
||||
assert(suitableGroups.size <= 1) {
|
||||
"CInteropCommonizerTask: Unnecessary work detected: More than one suitable group found for cinterop dependent."
|
||||
}
|
||||
|
||||
return targets.contains(commonizerTargetOfCompilation) && interops.containsAll(interopsOfCompilation)
|
||||
return suitableGroups.firstOrNull()
|
||||
}
|
||||
|
||||
@Internal
|
||||
internal fun getAllDependents(): Set<CInteropCommonizerDependent> {
|
||||
val multiplatformExtension = project.multiplatformExtensionOrNull ?: return emptySet()
|
||||
|
||||
val fromSharedNativeCompilations = multiplatformExtension
|
||||
.targets.flatMap { target -> target.compilations }
|
||||
.filterIsInstance<KotlinSharedNativeCompilation>()
|
||||
.mapNotNull { compilation -> CInteropCommonizerDependent.from(compilation) }
|
||||
.toSet()
|
||||
|
||||
val fromSourceSets = multiplatformExtension.sourceSets
|
||||
.mapNotNull { sourceSet -> CInteropCommonizerDependent.from(project, sourceSet) }
|
||||
.toSet()
|
||||
|
||||
return (fromSharedNativeCompilations + fromSourceSets)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CInteropProcess.toGist(): CInteropGist {
|
||||
@@ -178,58 +155,3 @@ private fun CInteropProcess.toGist(): CInteropGist {
|
||||
libraryFile = outputFileProvider
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single shared native compilation / shared native source set
|
||||
* that would rely on given [interops]
|
||||
*/
|
||||
internal data class SharedNativeCInterops(
|
||||
val target: SharedCommonizerTarget,
|
||||
val interops: Set<CInteropIdentifier>
|
||||
)
|
||||
|
||||
internal infix fun SharedNativeCInterops.isProperSubsetOf(other: SharedNativeCInterops): Boolean {
|
||||
return target.allLeaves() != other.target.allLeaves() && other.target.allLeaves().containsAll(target.allLeaves())
|
||||
&& interops != other.interops && other.interops.containsAll(interops)
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a single invocation to the commonizer
|
||||
*/
|
||||
internal data class CInteropCommonizationParameters(
|
||||
@get:Input val targets: Set<SharedCommonizerTarget>,
|
||||
@get:Input val interops: Set<CInteropIdentifier>
|
||||
)
|
||||
|
||||
private fun CInteropCommonizerTask.removeNotRegisteredInterops(
|
||||
parameters: Set<SharedNativeCInterops>
|
||||
): Set<SharedNativeCInterops> {
|
||||
val registeredInterops = this.cinterops.map { it.identifier }
|
||||
return parameters.mapTo(mutableSetOf()) { params ->
|
||||
params.copy(interops = params.interops.filterTo(mutableSetOf()) { interop -> interop in registeredInterops })
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeEmptyInterops(parameters: Set<SharedNativeCInterops>): Set<SharedNativeCInterops> {
|
||||
return parameters.filterTo(mutableSetOf()) { it.interops.isNotEmpty() }
|
||||
}
|
||||
|
||||
private fun Project.getDependingNativeCompilations(compilation: KotlinSharedNativeCompilation): Set<KotlinNativeCompilation> {
|
||||
/**
|
||||
* Some implementations of [KotlinCompilation] do not contain the default source set in
|
||||
* [KotlinCompilation.kotlinSourceSets] or [KotlinCompilation.allKotlinSourceSets]
|
||||
* see KT-45412
|
||||
*/
|
||||
fun KotlinCompilation<*>.allParticipatingSourceSets(): Set<KotlinSourceSet> {
|
||||
return kotlinSourceSetsIncludingDefault + kotlinSourceSetsIncludingDefault.resolveAllDependsOnSourceSets()
|
||||
}
|
||||
|
||||
val multiplatformExtension = multiplatformExtensionOrNull ?: return emptySet()
|
||||
val allParticipatingSourceSetsOfCompilation = compilation.allParticipatingSourceSets()
|
||||
|
||||
return multiplatformExtension.targets
|
||||
.flatMap { target -> target.compilations }
|
||||
.filterIsInstance<KotlinNativeCompilation>()
|
||||
.filter { nativeCompilation -> nativeCompilation.allParticipatingSourceSets().containsAll(allParticipatingSourceSetsOfCompilation) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
+5
-1
@@ -11,6 +11,7 @@ import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.jetbrains.kotlin.gradle.internal.isInIdeaSync
|
||||
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
|
||||
import org.jetbrains.kotlin.gradle.plugin.whenEvaluated
|
||||
import org.jetbrains.kotlin.gradle.tasks.dependsOn
|
||||
import org.jetbrains.kotlin.gradle.tasks.locateTask
|
||||
import org.jetbrains.kotlin.gradle.tasks.registerTask
|
||||
@@ -50,8 +51,11 @@ internal val Project.commonizeCInteropTask: TaskProvider<CInteropCommonizerTask>
|
||||
return locateOrRegisterTask(
|
||||
"commonizeCInterop",
|
||||
invokeWhenRegistered = {
|
||||
val task = this
|
||||
commonizeTask.dependsOn(this)
|
||||
commonizeNativeDistributionTask?.let(this::dependsOn)
|
||||
whenEvaluated {
|
||||
commonizeNativeDistributionTask?.let(task::dependsOn)
|
||||
}
|
||||
},
|
||||
configureTask = {
|
||||
group = "interop"
|
||||
|
||||
+5
-6
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.gradle.targets.native.internal
|
||||
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinSharedNativeCompilation
|
||||
import java.io.File
|
||||
|
||||
internal open class CopyCommonizeCInteropForIdeTask : AbstractCInteropCommonizerTask() {
|
||||
@@ -25,17 +24,17 @@ internal open class CopyCommonizeCInteropForIdeTask : AbstractCInteropCommonizer
|
||||
override val outputDirectory: File = project.rootDir.resolve(".gradle/kotlin/commonizer")
|
||||
.resolve(project.path.removePrefix(":").replace(":", "/"))
|
||||
|
||||
override fun getCommonizationParameters(compilation: KotlinSharedNativeCompilation): CInteropCommonizationParameters? {
|
||||
return commonizeCInteropTask.get().getCommonizationParameters(compilation)
|
||||
override fun findInteropsGroup(dependent: CInteropCommonizerDependent): CInteropCommonizerGroup? {
|
||||
return commonizeCInteropTask.get().findInteropsGroup(dependent)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected fun copy() {
|
||||
outputDirectory.mkdirs()
|
||||
for (parameters in commonizeCInteropTask.get().getCommonizationParameters()) {
|
||||
val source = commonizeCInteropTask.get().outputDirectory(parameters)
|
||||
for (group in commonizeCInteropTask.get().getAllInteropsGroups()) {
|
||||
val source = commonizeCInteropTask.get().outputDirectory(group)
|
||||
if (!source.exists()) continue
|
||||
val target = outputDirectory(parameters)
|
||||
val target = outputDirectory(group)
|
||||
if (target.exists()) target.deleteRecursively()
|
||||
source.copyRecursively(target, true)
|
||||
}
|
||||
|
||||
+1
@@ -85,6 +85,7 @@ private val Project.isNativeDependencyPropagationEnabled: Boolean
|
||||
internal fun Project.isAllowCommonizer(
|
||||
kotlinVersion: String = getKotlinPluginVersion()
|
||||
): Boolean {
|
||||
assert(state.executed) { "'isAllowCommonizer' can only be called after project evaluation" }
|
||||
multiplatformExtensionOrNull ?: return false
|
||||
|
||||
//register commonizer only for 1.4+, only for HMPP projects
|
||||
|
||||
@@ -18,7 +18,7 @@ public inline fun <reified T> transitiveClosure(seed: T, edges: T.() -> Iterable
|
||||
val initialEdges = seed.edges()
|
||||
if (initialEdges is Collection && initialEdges.isEmpty()) return emptySet()
|
||||
|
||||
val queue = deque<T>()
|
||||
val queue = deque<T>(initialEdges.count() * 2)
|
||||
val results = mutableSetOf<T>()
|
||||
queue.addAll(initialEdges)
|
||||
while (queue.isNotEmpty()) {
|
||||
@@ -34,7 +34,7 @@ public inline fun <reified T> transitiveClosure(seed: T, edges: T.() -> Iterable
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
@PublishedApi
|
||||
internal inline fun <reified T> deque(): MutableList<T> {
|
||||
return if (KotlinVersion.CURRENT.isAtLeast(1, 4)) ArrayDeque()
|
||||
else mutableListOf()
|
||||
internal inline fun <reified T> deque(initialSize: Int): MutableList<T> {
|
||||
return if (KotlinVersion.CURRENT.isAtLeast(1, 4)) ArrayDeque(initialSize)
|
||||
else ArrayList(initialSize)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user