Skip to content

Commit 4e2e64e

Browse files
timofey-soloninSpace Team
authored and
Space Team
committed
Support lenient KMP resolution and consumption of Uklibs
With the new resolution rules: - KMP components can resolve leniently by falling back to metadata variant for dependencies - The Uklib variant is preferred when it is available - Resolution of platform compilation artifacts within Uklib is supported through a transform. Consumption during GMT is not supported in this change. ^KT-74005
1 parent a27a4db commit 4e2e64e

File tree

11 files changed

+1852
-0
lines changed

11 files changed

+1852
-0
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ object KotlinUsages {
2222
const val KOTLIN_RUNTIME = "kotlin-runtime"
2323
const val KOTLIN_METADATA = "kotlin-metadata"
2424

25+
internal const val KOTLIN_UKLIB_API = "kotlin-uklib-api"
26+
internal const val KOTLIN_UKLIB_RUNTIME = "kotlin-uklib-runtime"
27+
internal const val KOTLIN_UKLIB_METADATA = "kotlin-uklib-metadata"
28+
2529
// This type is required to distinguish metadata jar configuration from a psm secondary variant.
2630
// At the same time, disambiguation and compatibility rules should count them as equivalent
2731
// to be possible to apply a transform actions chain to `kotlin-metadata` artifact to get psm.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
package org.jetbrains.kotlin.gradle.plugin.mpp.uklibs
7+
8+
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
9+
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
10+
import org.jetbrains.kotlin.gradle.plugin.KotlinTarget
11+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget
12+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget
13+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
14+
import org.jetbrains.kotlin.gradle.targets.js.KotlinWasmTargetType
15+
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
16+
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
17+
18+
/**
19+
* This sealed class is intended to help in the mapping between KotlinTarget and the Uklib attribute that will be recorded in the Umanifest
20+
*/
21+
internal sealed class UklibFragmentPlatformAttribute {
22+
/**
23+
* Jvm, native and JS targets are published
24+
*/
25+
data class ConsumeInPlatformAndMetadataCompilationsAndPublishInUmanifest(val attribute: String) : UklibFragmentPlatformAttribute()
26+
27+
/**
28+
* Android target consumption is supported; however in platform compile dependency configuration we must resolve aar and the platform
29+
* attribute should only be used during GMT and in publication of an Umanifest
30+
*/
31+
data class ConsumeInMetadataCompilationsAndPublishInUmanifest(val attribute: String) : UklibFragmentPlatformAttribute()
32+
33+
/**
34+
* External target is not currently supported. It is not ever published in Umanifest, but we must use some unique attribute during GMT
35+
* to produce a correct metadata classpath.
36+
*/
37+
data class ConsumeInMetadataCompilationsAndFailOnPublication(val unsupportedTargetName: String) : UklibFragmentPlatformAttribute()
38+
39+
/**
40+
* This case should only be used for metadata target
41+
*/
42+
data class FailOnConsumptionAndPublication(val metadataTarget: KotlinMetadataTarget) : UklibFragmentPlatformAttribute()
43+
44+
fun convertToStringForPublicationInUmanifest(): String = when (this) {
45+
is ConsumeInPlatformAndMetadataCompilationsAndPublishInUmanifest -> attribute
46+
is ConsumeInMetadataCompilationsAndPublishInUmanifest -> attribute
47+
is ConsumeInMetadataCompilationsAndFailOnPublication -> error("Publication with ${unsupportedTargetName} is not supported")
48+
is FailOnConsumptionAndPublication -> error("${metadataTarget} doesn't have a platform attribute for publication")
49+
}
50+
51+
/**
52+
* Convert the attribute for consumption in transforms or GMT
53+
*/
54+
fun convertToStringForConsumption(): String = when (this) {
55+
is ConsumeInPlatformAndMetadataCompilationsAndPublishInUmanifest -> attribute
56+
is ConsumeInMetadataCompilationsAndPublishInUmanifest -> attribute
57+
is ConsumeInMetadataCompilationsAndFailOnPublication -> unsupportedTargetName
58+
is FailOnConsumptionAndPublication -> error("${metadataTarget} doesn't have a platform attribute for consumption")
59+
}
60+
}
61+
62+
/**
63+
* This is the per-target attribute that will be recorded within the fragment of the umanifest
64+
*
65+
* When resolving the uklib we will use this attribute
66+
* - In the transform for platform compilation
67+
* - In GMT for metadata classpath formation
68+
*/
69+
internal val KotlinCompilation<*>.uklibFragmentPlatformAttribute: UklibFragmentPlatformAttribute
70+
get() = this.target.uklibFragmentPlatformAttribute
71+
internal val KotlinTarget.uklibFragmentPlatformAttribute: UklibFragmentPlatformAttribute
72+
get() {
73+
if (this is KotlinMetadataTarget) {
74+
return UklibFragmentPlatformAttribute.FailOnConsumptionAndPublication(this)
75+
}
76+
77+
/**
78+
* FIXME: Android configurations currently do not resolve consistently with the targets below
79+
* FIXME: Request jvm transform in Android?
80+
*/
81+
if (this is KotlinAndroidTarget) {
82+
return UklibFragmentPlatformAttribute.ConsumeInMetadataCompilationsAndPublishInUmanifest(
83+
UklibTargetFragmentAttribute.android.name
84+
)
85+
}
86+
87+
val supportedUklibTarget = when (this) {
88+
is KotlinNativeTarget -> konanTarget.name
89+
is KotlinJsIrTarget -> when (platformType) {
90+
KotlinPlatformType.js -> UklibTargetFragmentAttribute.js_ir.name
91+
KotlinPlatformType.wasm -> when (wasmTargetType ?: error("${KotlinJsIrTarget::class} missing wasm type in wasm platform ")) {
92+
KotlinWasmTargetType.JS -> UklibTargetFragmentAttribute.wasm_js.name
93+
KotlinWasmTargetType.WASI -> UklibTargetFragmentAttribute.wasm_wasi.name
94+
}
95+
else -> error("${KotlinJsIrTarget::class} unexpected platform type $platformType")
96+
}
97+
is KotlinJvmTarget -> UklibTargetFragmentAttribute.jvm.name
98+
else -> null
99+
}
100+
if (supportedUklibTarget != null) {
101+
return UklibFragmentPlatformAttribute.ConsumeInPlatformAndMetadataCompilationsAndPublishInUmanifest(supportedUklibTarget)
102+
}
103+
104+
return UklibFragmentPlatformAttribute.ConsumeInMetadataCompilationsAndFailOnPublication(targetName)
105+
}
106+
107+
/**
108+
* These attribute names will be recorded in and resolved from the Umanifest
109+
*/
110+
private enum class UklibTargetFragmentAttribute {
111+
js_ir,
112+
wasm_js,
113+
wasm_wasi,
114+
jvm,
115+
android;
116+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
3+
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
4+
*/
5+
6+
package org.jetbrains.kotlin.gradle.plugin.mpp.uklibs.consumption
7+
8+
import org.gradle.api.artifacts.transform.CacheableTransform
9+
import org.gradle.api.artifacts.transform.InputArtifact
10+
import org.gradle.api.artifacts.transform.TransformAction
11+
import org.gradle.api.artifacts.transform.TransformOutputs
12+
import org.gradle.api.artifacts.transform.TransformParameters
13+
import org.gradle.api.file.FileSystemLocation
14+
import org.gradle.api.provider.Provider
15+
import org.gradle.api.tasks.PathSensitive
16+
import org.gradle.api.tasks.PathSensitivity
17+
import org.gradle.work.DisableCachingByDefault
18+
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME
19+
import java.util.zip.ZipFile
20+
21+
/**
22+
* Platform compile dependency configurations can inherit dependencies from metadata variant. We remove the resolved metadata jars here
23+
*/
24+
@CacheableTransform
25+
internal abstract class ThrowAwayMetadataJarsTransform : TransformAction<TransformParameters.None> {
26+
@get:PathSensitive(PathSensitivity.RELATIVE)
27+
@get:InputArtifact
28+
abstract val inputArtifact: Provider<FileSystemLocation>
29+
30+
override fun transform(outputs: TransformOutputs) {
31+
val jar = inputArtifact.get().asFile
32+
// Sanity check
33+
if (jar.extension != "jar") {
34+
// Just return whatever this is
35+
outputs.file(jar)
36+
return
37+
}
38+
39+
val isMetadataJar: Boolean = ZipFile(jar).use { zip ->
40+
zip.entries().asSequence().any {
41+
it.name.endsWith("META-INF/$MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME")
42+
}
43+
}
44+
// Return nothing on metadata jar
45+
if (isMetadataJar) return
46+
outputs.file(jar)
47+
}
48+
}

0 commit comments

Comments
 (0)