Skip to content

Commit 5678873

Browse files
authored
Merge pull request #869 from icerockdev/copilot/fix-kotlin-2-3-20-error
Fix Kotlin 2.3.20 compatibility: replace removed KotlinLibraryLayoutImpl with filesystem approach
2 parents 3e9ae31 + b541192 commit 5678873

8 files changed

Lines changed: 70 additions & 77 deletions

File tree

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ androidSdkCommonVersion = "31.13.2"
1111
# We use current APIs (available in 8.13.2+) to ensure compatibility with
1212
# the widest range of AGP versions: from the minimum (8.3)
1313
# to the current (9.0+ at this time).
14-
pluginKotlinVersion = "2.3.0"
14+
pluginKotlinVersion = "2.3.20"
1515
pluginAndroidGradleVersion = "8.13.2"
1616

1717

gradle/moko.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[versions]
2-
resourcesVersion = "0.26.1"
2+
resourcesVersion = "0.26.2"
33

44
[libraries]
55
resources = { module = "dev.icerock.moko:resources", version.ref = "resourcesVersion" }

resources-generator/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
77
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
88

99
plugins {
10-
id("org.jetbrains.kotlin.jvm") version ("2.3.0")
10+
id("org.jetbrains.kotlin.jvm") version ("2.3.20")
1111
id("detekt-convention")
1212
id("publication-convention")
1313
id("com.gradle.plugin-publish") version ("1.2.0")
1414
id("java-gradle-plugin")
15-
kotlin("plugin.serialization") version ("2.3.0")
15+
kotlin("plugin.serialization") version ("2.3.20")
1616
id("nexus-publication-convention")
1717
}
1818

resources-generator/src/main/kotlin/dev/icerock/gradle/actions/apple/CopyResourcesFromKLibsAction.kt

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
package dev.icerock.gradle.actions.apple
66

7-
import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
7+
import dev.icerock.gradle.data.getKlibResourcesDir
88
import dev.icerock.gradle.utils.klibs
99
import org.gradle.api.Action
1010
import org.gradle.api.Task
1111
import org.gradle.api.logging.Logger
1212
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
13-
import org.jetbrains.kotlin.library.KotlinLibraryLayout
14-
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
1513
import java.io.File
1614

1715
internal abstract class CopyResourcesFromKLibsAction : Action<Task> {
@@ -68,21 +66,9 @@ internal abstract class CopyResourcesFromKLibsAction : Action<Task> {
6866
private fun getBundlesFromKotlinLibrary(
6967
klibFile: File
7068
): List<File> {
71-
val layout: KotlinLibraryLayout = getKotlinLibraryLayout(klibFile)
72-
return layout.resourcesDir.listFilesOrEmpty
73-
.filter { it.isDirectory && it.extension == "bundle" }
74-
.map { File(it.path) }
75-
}
76-
77-
private fun getKotlinLibraryLayout(file: File): KotlinLibraryLayout {
78-
val klibKonan = org.jetbrains.kotlin.konan.file.File(file.path)
79-
val klib = KotlinLibraryLayoutImpl(klib = klibKonan, component = "default")
80-
81-
// while klib zipped we can't check resources directory, so we should unpack all klibs :(
82-
// maybe will be better if we will write some state in cache as build result file with
83-
// klib path, hash, resources count. to not extract klibs that we already know that not
84-
// contains any resources. BUT maybe extraction will be faster then hashing for this logic.
85-
// so this improvement should be checked in future
86-
return if (klib.isZipped) ExtractingBaseLibraryImpl(klib) else klib
69+
val resourcesDir: File = getKlibResourcesDir(klibFile) ?: return emptyList()
70+
return resourcesDir.listFiles()
71+
?.filter { it.isDirectory && it.extension == "bundle" }
72+
?: emptyList()
8773
}
8874
}

resources-generator/src/main/kotlin/dev/icerock/gradle/actions/js/CopyResourcesToExecutableAction.kt

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
package dev.icerock.gradle.actions.js
66

7-
import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
7+
import dev.icerock.gradle.data.getKlibResourcesDir
88
import dev.icerock.gradle.utils.klibs
99
import org.gradle.api.Action
1010
import org.gradle.api.logging.Logger
1111
import org.gradle.api.provider.Provider
1212
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
13-
import org.jetbrains.kotlin.library.KotlinLibraryLayout
14-
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
1513
import java.io.File
1614

1715
internal class CopyResourcesToExecutableAction(
@@ -127,16 +125,13 @@ internal class CopyResourcesToExecutableAction(
127125
if (inputFile.exists().not()) return
128126

129127
logger.info("copy resources from $inputFile into $outputDir")
130-
val klibKonan = org.jetbrains.kotlin.konan.file.File(inputFile.path)
131-
val klib = KotlinLibraryLayoutImpl(klib = klibKonan, component = "default")
132-
val layout: KotlinLibraryLayout = if (klib.isZipped) {
133-
ExtractingBaseLibraryImpl(klib)
134-
} else {
135-
klib
128+
val resourcesDir: File = getKlibResourcesDir(inputFile) ?: run {
129+
logger.info("resources in $inputFile not found")
130+
return
136131
}
137132

138133
try {
139-
File(layout.resourcesDir.path, "moko-resources-js").copyRecursively(
134+
File(resourcesDir, "moko-resources-js").copyRecursively(
140135
target = outputDir,
141136
overwrite = true
142137
)

resources-generator/src/main/kotlin/dev/icerock/gradle/data/ExtractingKotlinLibraryLayout.kt

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,64 @@
44

55
package dev.icerock.gradle.data
66

7-
import org.jetbrains.kotlin.konan.file.File
8-
import org.jetbrains.kotlin.konan.file.file
9-
import org.jetbrains.kotlin.konan.file.unzipTo
10-
import org.jetbrains.kotlin.konan.file.withZipFileSystem
11-
import org.jetbrains.kotlin.library.KotlinLibraryLayout
12-
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
7+
import java.io.File
8+
import java.nio.file.Files
9+
import java.util.zip.ZipEntry
10+
import java.util.zip.ZipFile
11+
12+
private const val DEFAULT_COMPONENT = "default"
13+
private const val RESOURCES_DIR_NAME = "resources"
14+
private const val RESOURCES_PREFIX = "$DEFAULT_COMPONENT/$RESOURCES_DIR_NAME/"
1315

1416
/**
15-
* The code in this file is pulled from a previous version of Kotlin to replicate
16-
* removed functionality that MR relies on for extracting klibs.
17-
* https://github.com/JetBrains/kotlin/blob/00984f32ac1ebc2e7fb71b440c282be2a8b05f36/compiler/util-klib/src/org/jetbrains/kotlin/library/impl/KotlinLibraryLayoutImpl.kt
17+
* Gets the resources directory from a klib file (packed or unpacked).
18+
*
19+
* For a packed (zipped) klib (single .klib file), extracts the resources
20+
* directory to a temporary location and returns it. Returns null if the
21+
* klib does not contain a resources directory.
22+
*
23+
* For an unpacked klib (directory), returns the direct path to the
24+
* resources directory (which may not exist if the klib has no resources).
25+
*
26+
* The structure of a klib is:
27+
* - Packed: a .klib zip containing `default/resources/`
28+
* - Unpacked: a directory with `default/resources/` subdirectory
1829
*/
19-
20-
internal open class ExtractingKotlinLibraryLayout(zipped: KotlinLibraryLayoutImpl) : KotlinLibraryLayout {
21-
override val libFile: File get() = error("Extracting layout doesn't extract its own root")
22-
override val libraryName = zipped.libraryName
23-
override val component = zipped.component
30+
internal fun getKlibResourcesDir(klibFile: File): File? {
31+
return if (klibFile.isFile) {
32+
// Packed (zipped) klib - extract resources to temp if present
33+
extractResourcesFromPackedKlib(klibFile)
34+
} else {
35+
// Unpacked klib directory - navigate to resources (may not exist)
36+
File(klibFile, "$DEFAULT_COMPONENT/$RESOURCES_DIR_NAME")
37+
}
2438
}
2539

26-
internal class ExtractingBaseLibraryImpl(zipped: KotlinLibraryLayoutImpl) : ExtractingKotlinLibraryLayout(zipped) {
27-
override val manifestFile: File by lazy { zipped.extract(zipped.manifestFile) }
28-
override val resourcesDir: File by lazy { zipped.extractDir(zipped.resourcesDir) }
29-
}
40+
private fun extractResourcesFromPackedKlib(klibFile: File): File? {
41+
ZipFile(klibFile).use { zip ->
42+
val hasResources = zip.entries().asSequence().any { it.name.startsWith(RESOURCES_PREFIX) }
43+
if (!hasResources) return null
3044

31-
private fun KotlinLibraryLayoutImpl.extract(file: File): File = extract(this.klib, file)
32-
33-
private fun extract(zipFile: File, file: File) = zipFile.withZipFileSystem { zipFileSystem ->
34-
val temporary = org.jetbrains.kotlin.konan.file.createTempFile(file.name)
35-
zipFileSystem.file(file).copyTo(temporary)
36-
temporary.deleteOnExit()
37-
temporary
45+
val temporary = Files.createTempDirectory(RESOURCES_DIR_NAME).toFile().also {
46+
it.deleteOnExit()
47+
}
48+
zip.entries().asSequence()
49+
.filter { it.name.startsWith(RESOURCES_PREFIX) }
50+
.forEach { entry ->
51+
val relativeName = entry.name.removePrefix(RESOURCES_PREFIX)
52+
if (relativeName.isNotEmpty()) {
53+
extractZipEntry(zip, entry, File(temporary, relativeName))
54+
}
55+
}
56+
return temporary
57+
}
3858
}
3959

40-
private fun KotlinLibraryLayoutImpl.extractDir(directory: File): File = extractDir(this.klib, directory)
41-
42-
private fun extractDir(zipFile: File, directory: File): File {
43-
val temporary = org.jetbrains.kotlin.konan.file.createTempDir(directory.name)
44-
temporary.deleteOnExitRecursively()
45-
zipFile.unzipTo(temporary, fromSubdirectory = directory)
46-
return temporary
60+
private fun extractZipEntry(zip: ZipFile, entry: ZipEntry, output: File) {
61+
if (entry.isDirectory) {
62+
output.mkdirs()
63+
} else {
64+
output.parentFile?.mkdirs()
65+
zip.getInputStream(entry).use { it.copyTo(output.outputStream()) }
66+
}
4767
}

resources-generator/src/main/kotlin/dev/icerock/gradle/tasks/CopyExecutableResourcesToApp.kt

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,14 @@
44

55
package dev.icerock.gradle.tasks
66

7-
import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
8-
import dev.icerock.gradle.utils.toKonanFile
7+
import dev.icerock.gradle.data.getKlibResourcesDir
98
import org.gradle.api.DefaultTask
109
import org.gradle.api.file.ConfigurableFileCollection
1110
import org.gradle.api.file.DirectoryProperty
1211
import org.gradle.api.tasks.Classpath
1312
import org.gradle.api.tasks.InputFiles
1413
import org.gradle.api.tasks.OutputDirectory
1514
import org.gradle.api.tasks.TaskAction
16-
import org.jetbrains.kotlin.library.KotlinLibraryLayout
17-
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
1815
import java.io.File
1916
import java.io.FileFilter
2017

@@ -39,15 +36,10 @@ abstract class CopyExecutableResourcesToApp : DefaultTask() {
3936
.filter { library -> library.extension == "klib" }
4037
.filter(File::exists)
4138
.forEach { inputFile ->
42-
val klibKonan: org.jetbrains.kotlin.konan.file.File = inputFile.toKonanFile()
43-
val klib = KotlinLibraryLayoutImpl(klib = klibKonan, component = "default")
44-
val layout: KotlinLibraryLayout = ExtractingBaseLibraryImpl(klib)
39+
val resourcesDir: File = getKlibResourcesDir(inputFile) ?: return@forEach
4540

4641
// extracting bundles
47-
layout
48-
.resourcesDir
49-
.absolutePath
50-
.let(::File)
42+
resourcesDir
5143
.listFiles(FileFilter { it.extension == "bundle" })
5244
// copying bundles to app
5345
?.forEach {

samples/kotlin-2-tests/gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ uuid = "0.8.4"
1010
kodein = "7.23.1"
1111
moko-resources = "0.24.5"
1212
wrappers = "1.0.0-pre.839"
13-
kotlin = "2.2.20"
13+
kotlin = "2.3.20"
1414
compose-multiplatform = "1.7.1"
1515
paparazzi = "1.3.4"
1616
kotest = "5.9.1"

0 commit comments

Comments
 (0)