Skip to content
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ androidSdkCommonVersion = "31.13.2"
# We use current APIs (available in 8.13.2+) to ensure compatibility with
# the widest range of AGP versions: from the minimum (8.3)
# to the current (9.0+ at this time).
pluginKotlinVersion = "2.3.0"
pluginKotlinVersion = "2.3.20"
pluginAndroidGradleVersion = "8.13.2"


Expand Down
2 changes: 1 addition & 1 deletion gradle/moko.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
resourcesVersion = "0.26.1"
resourcesVersion = "0.26.2"

[libraries]
resources = { module = "dev.icerock.moko:resources", version.ref = "resourcesVersion" }
Expand Down
4 changes: 2 additions & 2 deletions resources-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

plugins {
id("org.jetbrains.kotlin.jvm") version ("2.3.0")
id("org.jetbrains.kotlin.jvm") version ("2.3.20")
id("detekt-convention")
id("publication-convention")
id("com.gradle.plugin-publish") version ("1.2.0")
id("java-gradle-plugin")
kotlin("plugin.serialization") version ("2.3.0")
kotlin("plugin.serialization") version ("2.3.20")
id("nexus-publication-convention")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

package dev.icerock.gradle.actions.apple

import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
import dev.icerock.gradle.data.getKlibResourcesDir
import dev.icerock.gradle.utils.klibs
import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.gradle.tasks.KotlinNativeLink
import org.jetbrains.kotlin.library.KotlinLibraryLayout
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
import java.io.File

internal abstract class CopyResourcesFromKLibsAction : Action<Task> {
Expand Down Expand Up @@ -68,21 +66,9 @@ internal abstract class CopyResourcesFromKLibsAction : Action<Task> {
private fun getBundlesFromKotlinLibrary(
klibFile: File
): List<File> {
val layout: KotlinLibraryLayout = getKotlinLibraryLayout(klibFile)
return layout.resourcesDir.listFilesOrEmpty
.filter { it.isDirectory && it.extension == "bundle" }
.map { File(it.path) }
}

private fun getKotlinLibraryLayout(file: File): KotlinLibraryLayout {
val klibKonan = org.jetbrains.kotlin.konan.file.File(file.path)
val klib = KotlinLibraryLayoutImpl(klib = klibKonan, component = "default")

// while klib zipped we can't check resources directory, so we should unpack all klibs :(
// maybe will be better if we will write some state in cache as build result file with
// klib path, hash, resources count. to not extract klibs that we already know that not
// contains any resources. BUT maybe extraction will be faster then hashing for this logic.
// so this improvement should be checked in future
return if (klib.isZipped) ExtractingBaseLibraryImpl(klib) else klib
val resourcesDir: File = getKlibResourcesDir(klibFile) ?: return emptyList()
return resourcesDir.listFiles()
?.filter { it.isDirectory && it.extension == "bundle" }
?: emptyList()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@

package dev.icerock.gradle.actions.js

import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
import dev.icerock.gradle.data.getKlibResourcesDir
import dev.icerock.gradle.utils.klibs
import org.gradle.api.Action
import org.gradle.api.logging.Logger
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
import org.jetbrains.kotlin.library.KotlinLibraryLayout
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
import java.io.File

internal class CopyResourcesToExecutableAction(
Expand Down Expand Up @@ -127,16 +125,13 @@ internal class CopyResourcesToExecutableAction(
if (inputFile.exists().not()) return

logger.info("copy resources from $inputFile into $outputDir")
val klibKonan = org.jetbrains.kotlin.konan.file.File(inputFile.path)
val klib = KotlinLibraryLayoutImpl(klib = klibKonan, component = "default")
val layout: KotlinLibraryLayout = if (klib.isZipped) {
ExtractingBaseLibraryImpl(klib)
} else {
klib
val resourcesDir: File = getKlibResourcesDir(inputFile) ?: run {
logger.info("resources in $inputFile not found")
return
}

try {
File(layout.resourcesDir.path, "moko-resources-js").copyRecursively(
File(resourcesDir, "moko-resources-js").copyRecursively(
target = outputDir,
overwrite = true
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,64 @@

package dev.icerock.gradle.data

import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.file.file
import org.jetbrains.kotlin.konan.file.unzipTo
import org.jetbrains.kotlin.konan.file.withZipFileSystem
import org.jetbrains.kotlin.library.KotlinLibraryLayout
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
import java.io.File
import java.nio.file.Files
import java.util.zip.ZipEntry
import java.util.zip.ZipFile

private const val DEFAULT_COMPONENT = "default"
private const val RESOURCES_DIR_NAME = "resources"
private const val RESOURCES_PREFIX = "$DEFAULT_COMPONENT/$RESOURCES_DIR_NAME/"

/**
* The code in this file is pulled from a previous version of Kotlin to replicate
* removed functionality that MR relies on for extracting klibs.
* https://github.com/JetBrains/kotlin/blob/00984f32ac1ebc2e7fb71b440c282be2a8b05f36/compiler/util-klib/src/org/jetbrains/kotlin/library/impl/KotlinLibraryLayoutImpl.kt
* Gets the resources directory from a klib file (packed or unpacked).
*
* For a packed (zipped) klib (single .klib file), extracts the resources
* directory to a temporary location and returns it. Returns null if the
* klib does not contain a resources directory.
*
* For an unpacked klib (directory), returns the direct path to the
* resources directory (which may not exist if the klib has no resources).
*
* The structure of a klib is:
* - Packed: a .klib zip containing `default/resources/`
* - Unpacked: a directory with `default/resources/` subdirectory
*/

internal open class ExtractingKotlinLibraryLayout(zipped: KotlinLibraryLayoutImpl) : KotlinLibraryLayout {
override val libFile: File get() = error("Extracting layout doesn't extract its own root")
override val libraryName = zipped.libraryName
override val component = zipped.component
internal fun getKlibResourcesDir(klibFile: File): File? {
return if (klibFile.isFile) {
// Packed (zipped) klib - extract resources to temp if present
extractResourcesFromPackedKlib(klibFile)
} else {
// Unpacked klib directory - navigate to resources (may not exist)
File(klibFile, "$DEFAULT_COMPONENT/$RESOURCES_DIR_NAME")
}
}

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

private fun KotlinLibraryLayoutImpl.extract(file: File): File = extract(this.klib, file)

private fun extract(zipFile: File, file: File) = zipFile.withZipFileSystem { zipFileSystem ->
val temporary = org.jetbrains.kotlin.konan.file.createTempFile(file.name)
zipFileSystem.file(file).copyTo(temporary)
temporary.deleteOnExit()
temporary
val temporary = Files.createTempDirectory(RESOURCES_DIR_NAME).toFile().also {
it.deleteOnExit()
}
zip.entries().asSequence()
.filter { it.name.startsWith(RESOURCES_PREFIX) }
.forEach { entry ->
val relativeName = entry.name.removePrefix(RESOURCES_PREFIX)
if (relativeName.isNotEmpty()) {
extractZipEntry(zip, entry, File(temporary, relativeName))
}
}
return temporary
}
}

private fun KotlinLibraryLayoutImpl.extractDir(directory: File): File = extractDir(this.klib, directory)

private fun extractDir(zipFile: File, directory: File): File {
val temporary = org.jetbrains.kotlin.konan.file.createTempDir(directory.name)
temporary.deleteOnExitRecursively()
zipFile.unzipTo(temporary, fromSubdirectory = directory)
return temporary
private fun extractZipEntry(zip: ZipFile, entry: ZipEntry, output: File) {
if (entry.isDirectory) {
output.mkdirs()
} else {
output.parentFile?.mkdirs()
zip.getInputStream(entry).use { it.copyTo(output.outputStream()) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

package dev.icerock.gradle.tasks

import dev.icerock.gradle.data.ExtractingBaseLibraryImpl
import dev.icerock.gradle.utils.toKonanFile
import dev.icerock.gradle.data.getKlibResourcesDir
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.tasks.Classpath
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.library.KotlinLibraryLayout
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutImpl
import java.io.File
import java.io.FileFilter

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

// extracting bundles
layout
.resourcesDir
.absolutePath
.let(::File)
resourcesDir
.listFiles(FileFilter { it.extension == "bundle" })
// copying bundles to app
?.forEach {
Expand Down
2 changes: 1 addition & 1 deletion samples/kotlin-2-tests/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ uuid = "0.8.4"
kodein = "7.23.1"
moko-resources = "0.24.5"
wrappers = "1.0.0-pre.839"
kotlin = "2.2.20"
kotlin = "2.3.20"
compose-multiplatform = "1.7.1"
paparazzi = "1.3.4"
kotest = "5.9.1"
Expand Down
Loading