Skip to content

Commit a2c18ce

Browse files
timofey-soloninSpace Team
authored andcommitted
Publish and consume cinterop fragments in UKlibs
Relax some checks because cinterops will cause multiple fragments with same attributes ^KT-77006
1 parent 0012580 commit a2c18ce

File tree

10 files changed

+211
-57
lines changed

10 files changed

+211
-57
lines changed

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/uklibs/UklibConsumptionIT.kt

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
1616
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
1717
import org.jetbrains.kotlin.gradle.dsl.multiplatformExtension
1818
import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency
19+
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
20+
import org.jetbrains.kotlin.gradle.plugin.extraProperties
1921
import org.jetbrains.kotlin.gradle.plugin.mpp.locateOrRegisterMetadataDependencyTransformationTask
2022
import org.jetbrains.kotlin.gradle.testbase.*
2123
import org.jetbrains.kotlin.gradle.testing.*
@@ -1143,4 +1145,82 @@ class UklibConsumptionIT : KGPBaseTest() {
11431145
)
11441146
}
11451147

1148+
@GradleTest
1149+
fun `uklib consumption - linkage with cinterops`(version: GradleVersion) {
1150+
val producer = project(
1151+
"empty",
1152+
version,
1153+
) {
1154+
addKgpToBuildScriptCompilationClasspath()
1155+
buildScriptInjection {
1156+
// Commonizer is not supported yet which will be captured by this test
1157+
project.extraProperties.set(PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION, true)
1158+
project.setUklibPublicationStrategy()
1159+
project.applyMultiplatform {
1160+
listOf(
1161+
linuxArm64(),
1162+
linuxX64(),
1163+
).forEach {
1164+
val foo = project.layout.projectDirectory.file("foo.def")
1165+
val bar = project.layout.projectDirectory.file("bar.def")
1166+
1167+
foo.asFile.writeText(
1168+
"""
1169+
language = C
1170+
---
1171+
void foo(void);
1172+
""".trimIndent()
1173+
)
1174+
bar.asFile.writeText(
1175+
"""
1176+
language = C
1177+
---
1178+
void bar(void);
1179+
""".trimIndent()
1180+
)
1181+
1182+
it.compilations.getByName("main").cinterops.create("foo") {
1183+
it.definitionFile.set(foo)
1184+
}
1185+
it.compilations.getByName("main").cinterops.create("bar") {
1186+
it.definitionFile.set(bar)
1187+
}
1188+
}
1189+
1190+
sourceSets.commonMain.get().compileSource("class Common")
1191+
}
1192+
}
1193+
}.publish()
1194+
1195+
project("empty", version) {
1196+
addKgpToBuildScriptCompilationClasspath()
1197+
addPublishedProjectToRepositories(producer)
1198+
buildScriptInjection {
1199+
project.setUklibResolutionStrategy()
1200+
project.applyMultiplatform {
1201+
linuxArm64 {
1202+
binaries.staticLib { }
1203+
}
1204+
sourceSets.commonMain.get().compileSource(
1205+
"""
1206+
@file:OptIn(ExperimentalForeignApi::class)
1207+
1208+
import kotlinx.cinterop.ExperimentalForeignApi
1209+
1210+
fun consumeCinterops() {
1211+
bar.bar()
1212+
foo.foo()
1213+
}
1214+
""".trimIndent()
1215+
)
1216+
sourceSets.commonMain.get().dependencies {
1217+
api(producer.rootCoordinate)
1218+
}
1219+
}
1220+
}
1221+
1222+
build("linkDebugStaticLinuxArm64")
1223+
}
1224+
}
1225+
11461226
}

libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/uklibs/UklibPublicationIT.kt

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import org.gradle.util.GradleVersion
1515
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
1616
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
1717
import org.jetbrains.kotlin.gradle.mpp.resources.unzip
18+
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider
19+
import org.jetbrains.kotlin.gradle.plugin.extraProperties
1820
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.Uklib
1921
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.publication.ArchiveUklibTask
2022
import org.jetbrains.kotlin.gradle.testbase.*
23+
import org.jetbrains.kotlin.gradle.testing.prettyPrinted
2124
import org.jetbrains.kotlin.gradle.util.MavenModule
2225
import org.jetbrains.kotlin.gradle.util.parsePom
2326
import org.junit.jupiter.api.DisplayName
@@ -182,6 +185,84 @@ class UklibPublicationIT : KGPBaseTest() {
182185
)
183186
}
184187

188+
@GradleTest
189+
fun `uklib contents - publication with cinterops`(
190+
gradleVersion: GradleVersion,
191+
) {
192+
val project = project(
193+
"empty",
194+
gradleVersion,
195+
) {
196+
addKgpToBuildScriptCompilationClasspath()
197+
buildScriptInjection {
198+
// Commonizer is not supported yet which will be captured by this test
199+
project.extraProperties.set(PropertiesProvider.PropertyNames.KOTLIN_MPP_ENABLE_CINTEROP_COMMONIZATION, true)
200+
project.setUklibPublicationStrategy()
201+
project.applyMultiplatform {
202+
listOf(
203+
linuxArm64(),
204+
linuxX64(),
205+
).forEach {
206+
val foo = project.layout.projectDirectory.file("foo.def")
207+
val bar = project.layout.projectDirectory.file("bar.def")
208+
209+
foo.asFile.writeText(
210+
"""
211+
language = C
212+
---
213+
void foo(void);
214+
""".trimIndent()
215+
)
216+
bar.asFile.writeText(
217+
"""
218+
language = C
219+
---
220+
void bar(void);
221+
""".trimIndent()
222+
)
223+
224+
it.compilations.getByName("main").cinterops.create("foo") {
225+
it.definitionFile.set(foo)
226+
}
227+
it.compilations.getByName("main").cinterops.create("bar") {
228+
it.definitionFile.set(bar)
229+
}
230+
}
231+
232+
sourceSets.commonMain.get().compileSource("class Common")
233+
}
234+
}
235+
}
236+
237+
val publication = project.publish()
238+
assertPublishedFragments(
239+
setOf(
240+
Fragment(
241+
identifier = "commonMain", targets = mutableListOf("linux_arm64", "linux_x64"),
242+
),
243+
Fragment(
244+
identifier = "linuxArm64Main", targets = mutableListOf("linux_arm64"),
245+
),
246+
Fragment(
247+
identifier = "linuxArm64Main_cinterop_bar", targets = mutableListOf("linux_arm64"),
248+
),
249+
Fragment(
250+
identifier = "linuxArm64Main_cinterop_foo", targets = mutableListOf("linux_arm64"),
251+
),
252+
Fragment(
253+
identifier = "linuxX64Main", targets = mutableListOf("linux_x64"),
254+
),
255+
Fragment(
256+
identifier = "linuxX64Main_cinterop_bar", targets = mutableListOf("linux_x64"),
257+
),
258+
Fragment(
259+
identifier = "linuxX64Main_cinterop_foo", targets = mutableListOf("linux_x64"),
260+
),
261+
),
262+
readProducedUklib(publication)
263+
)
264+
}
265+
185266
@GradleTest
186267
fun `uklib contents - bamboo metadata publication`(
187268
gradleVersion: GradleVersion,
@@ -268,14 +349,14 @@ class UklibPublicationIT : KGPBaseTest() {
268349
publisher: ProducedUklib,
269350
) {
270351
assertEquals(
271-
Umanifest(expectedFragments),
272-
publisher.umanifest,
352+
Umanifest(expectedFragments).prettyPrinted,
353+
publisher.umanifest.prettyPrinted,
273354
)
274355
assertEquals(
275-
expectedFragments.map { it.identifier }.toSet(),
356+
expectedFragments.map { it.identifier }.toSet().prettyPrinted,
276357
publisher.uklibContents.listDirectoryEntries().map {
277358
it.name
278-
}.filterNot { it == Uklib.UMANIFEST_FILE_NAME }.toSet(),
359+
}.filterNot { it == Uklib.UMANIFEST_FILE_NAME }.toSet().prettyPrinted,
279360
)
280361
}
281362

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/diagnostics/KotlinToolingDiagnostics.kt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,6 @@ internal object KotlinToolingDiagnostics {
8181
}
8282
}
8383

84-
object UklibPublicationWithCinterops : ToolingDiagnosticFactory(ERROR, DiagnosticGroup.Kgp.Misconfiguration) {
85-
operator fun invoke(target: String, interopName: String) = build {
86-
title("Uklib Publication With Cinterops")
87-
.description("Publication of ${Uklib.UKLIB_NAME} with cinterops is not yet supported. Target '$target' declares cinterop '$interopName'")
88-
.solution("Cinterop publication is not yet supported (https://kotl.in/uklib-with-cinterops). Please disable ${Uklib.UKLIB_NAME} publication in projects with cinterops")
89-
}
90-
}
91-
92-
// FIXME: This check was demoved to warning. Figure out proper handling in KT-77005
9384
object UklibSourceSetStructureUnderRefinementViolation : ToolingDiagnosticFactory(WARNING, DiagnosticGroup.Kgp.Misconfiguration) {
9485
operator fun invoke(sourceSet: KotlinSourceSet, missingRefinements: List<KotlinSourceSet>) = build {
9586
title("Uklib Incompatible Source Set Structure")

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/UklibCompositeMetadataArtifact.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,12 @@ private class UklibCompositeMetadataBinary(
103103
override val checksum: String
104104
get() = if (computeChecksum) {
105105
val md5 = MessageDigest.getInstance("MD5")
106-
md5.digest(fragment.files.single().path.encodeToByteArray())
106+
md5.digest(fragment.singleExpectedFileFromModularUklib.path.encodeToByteArray())
107107
.joinToString(separator = "") { byte -> "%02x".format(byte) }
108108
} else ""
109109

110110
override fun copyTo(file: File): Boolean {
111-
val metadataSlice = fragment.files.single()
111+
val metadataSlice = fragment.singleExpectedFileFromModularUklib
112112
if (!metadataSlice.exists()) return false
113113
return metadataSlice.copyRecursively(
114114
file,

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/uklibs/UklibFragment.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.plugin.mpp.uklibs
77

88
import org.gradle.api.tasks.Input
99
import org.gradle.api.tasks.InputFiles
10+
import org.gradle.api.tasks.Internal
1011
import org.gradle.api.tasks.PathSensitive
1112
import org.gradle.api.tasks.PathSensitivity
1213
import java.io.File
@@ -29,4 +30,7 @@ internal data class UklibFragment(
2930
attributes,
3031
listOf(file),
3132
)
33+
34+
@get:Internal
35+
val singleExpectedFileFromModularUklib: File get() = files.single()
3236
}

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/uklibs/consumption/UnzippedUklibToPlatformCompilationTransform.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,8 @@ internal abstract class UnzippedUklibToPlatformCompilationTransform :
4343
return
4444
}
4545

46-
if (platformFragments.size > 1) {
47-
error("Matched multiple fragments from ${unzippedUklib}, but was expecting to find exactly one. Found fragments: $platformFragments")
46+
platformFragments.forEach {
47+
outputs.dir(it.files.single())
4848
}
49-
50-
outputs.dir(platformFragments.singleOrNull()!!.files.single())
5149
}
5250
}

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/uklibs/publication/ArchiveUklibTask.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ internal abstract class ArchiveUklibTask : DefaultTask() {
6262
* Filter out compilations that were skipped. They should be omitted from the umanifest
6363
*/
6464
val compiledFragments = fragments.get().filter { fragment ->
65-
val isMetadata = fragment.attributes.count() > 1
66-
val isASkippedMetadataCompilation = isMetadata && !fragment.files.single().exists()
67-
!isASkippedMetadataCompilation
65+
fragment.singleExpectedFileFromModularUklib.exists()
6866
}
6967

7068
if (checkForBamboosInUklib.get()) {

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/uklibs/publication/UklibFromKGPModel.kt

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ import org.jetbrains.kotlin.gradle.plugin.diagnostics.*
2727
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.UklibFragmentPlatformAttribute
2828
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.diagnostics.UklibFragmentsChecker
2929
import org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.uklibFragmentPlatformAttribute
30+
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
31+
import org.jetbrains.kotlin.gradle.tasks.locateTask
32+
import org.jetbrains.kotlin.gradle.utils.named
3033
import java.io.File
3134

3235
internal data class KGPUklibFragment(
@@ -57,6 +60,17 @@ internal suspend fun KotlinMultiplatformExtension.validateKgpModelIsUklibComplia
5760
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
5861
val file = mainCompilation.compileTaskProvider.map { it.klibOutput.get() }
5962
fragments.add(kgpUklibFragment(mainCompilation, file))
63+
mainCompilation.cinterops.forEach {
64+
fragments.add(
65+
kgpUklibFragment(
66+
mainCompilation,
67+
project.tasks.named<CInteropProcess>(it.interopProcessingTaskName).map {
68+
it.klibOutput.get()
69+
},
70+
mainCompilation.uklibFragmentIdentifier + "_cinterop_${it.name}"
71+
)
72+
)
73+
}
6074
}
6175
is KotlinJvmTarget -> {
6276
val mainCompilation = target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME)
@@ -117,7 +131,7 @@ internal suspend fun KotlinMultiplatformExtension.validateKgpModelIsUklibComplia
117131
val allPublishedCompilations = publishedMetadataCompilations + uklibPublishedPlatformCompilations
118132
if (allPublishedCompilations.size > 1) {
119133
project.ensureSourceSetStructureIsUklibCompliant(allPublishedCompilations)
120-
} else if (publishedMetadataCompilations.isEmpty() && uklibPublishedPlatformCompilations.size == 1 && fragments.size == 1) {
134+
} else if (publishedMetadataCompilations.isEmpty() && uklibPublishedPlatformCompilations.size == 1) {
121135
/**
122136
* Do not validate anything. Uklib will contain a single platform slice and fragment structure validations don't make sense
123137
*/
@@ -153,8 +167,8 @@ internal suspend fun KotlinMultiplatformExtension.uklibPublishedPlatformCompilat
153167
private fun kgpUklibFragment(
154168
mainCompilation: KotlinCompilation<*>,
155169
fileProvider: Provider<File>,
170+
fragmentIdentifier: String = mainCompilation.uklibFragmentIdentifier
156171
): KGPUklibFragment {
157-
val fragmentIdentifier = mainCompilation.uklibFragmentIdentifier
158172
val fragmentAttribute = mainCompilation.uklibFragmentPlatformAttribute.convertToStringForPublicationInUmanifest()
159173
return KGPUklibFragment(
160174
fragment = fileProvider.map {

libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/uklibs/publication/UklibPublicationSetupAction.kt

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal val UklibPublicationSetupAction = KotlinProjectSetupAction {
3737
*/
3838
project.reportDiagnostic(
3939
KotlinToolingDiagnostics.UklibPublicationWithoutCrossCompilation(
40-
severity = if (HostManager.hostIsMac) WARNING else ERROR
40+
severity = WARNING
4141
).get()
4242
)
4343
}
@@ -51,23 +51,6 @@ internal val UklibPublicationSetupAction = KotlinProjectSetupAction {
5151
project.launch {
5252
project.multiplatformExtension.validateKgpModelIsUklibCompliantAndCreateKgpFragments()
5353
}
54-
55-
/**
56-
* Cinterop and commonized metadata uklib publication is not yet ready. For now prohibit publishing uklibs if cinterops were declared
57-
*/
58-
project.multiplatformExtension.targets.matching {
59-
it is KotlinNativeTarget
60-
}.all { target ->
61-
target as KotlinNativeTarget
62-
target.compilations.getByName(KotlinCompilation.MAIN_COMPILATION_NAME).cinterops.all { interop ->
63-
project.reportDiagnostic(
64-
KotlinToolingDiagnostics.UklibPublicationWithCinterops(
65-
target.targetName,
66-
interop.name,
67-
)
68-
)
69-
}
70-
}
7154
}
7255

7356
private fun Project.registerOutgoingUklibVariants(rootComponent: KotlinSoftwareComponent) {

0 commit comments

Comments
 (0)