Unverified Commit 38370178 authored by Nikolay Igotti's avatar Nikolay Igotti Committed by GitHub

Unpack native lib only once. (#6)

parent 6afb0da1
import kotlin.text.capitalize import kotlin.text.capitalize
import org.gradle.crypto.checksum.Checksum
plugins { plugins {
kotlin("multiplatform") version "1.3.72" kotlin("multiplatform") version "1.3.72"
`cpp-library` `cpp-library`
`maven-publish` `maven-publish`
id("org.gradle.crypto.checksum") version "1.1.0"
} }
val isCIBuild = project.hasProperty("teamcity") val isCIBuild = project.hasProperty("teamcity")
...@@ -246,8 +248,18 @@ val skikoJvmJar: Provider<Jar> by tasks.registering(Jar::class) { ...@@ -246,8 +248,18 @@ val skikoJvmJar: Provider<Jar> by tasks.registering(Jar::class) {
from(kotlin.jvm().compilations["main"].output.allOutputs) from(kotlin.jvm().compilations["main"].output.allOutputs)
} }
val createChecksums by project.tasks.registering(org.gradle.crypto.checksum.Checksum::class) {
val linkTask = project.tasks.named("linkRelease${target.capitalize()}")
dependsOn(linkTask)
files = linkTask.get().outputs.files.filter { it.isFile } +
if (target == "windows") files("$skiaDir/out/Release-x64/icudtl.dat") else files()
algorithm = Checksum.Algorithm.SHA256
outputDir = file("$buildDir/checksums")
}
val skikoJvmRuntimeJar by project.tasks.registering(Jar::class) { val skikoJvmRuntimeJar by project.tasks.registering(Jar::class) {
archiveBaseName.set("skiko-$target") archiveBaseName.set("skiko-$target")
dependsOn(createChecksums)
from(skikoJvmJar.map { zipTree(it.archiveFile) }) from(skikoJvmJar.map { zipTree(it.archiveFile) })
from(project.tasks.named("linkRelease${target.capitalize()}").map { from(project.tasks.named("linkRelease${target.capitalize()}").map {
it.outputs.files.filter { it.isFile } it.outputs.files.filter { it.isFile }
...@@ -255,6 +267,7 @@ val skikoJvmRuntimeJar by project.tasks.registering(Jar::class) { ...@@ -255,6 +267,7 @@ val skikoJvmRuntimeJar by project.tasks.registering(Jar::class) {
if (target == "windows") { if (target == "windows") {
from(files("$skiaDir/out/Release-x64/icudtl.dat")) from(files("$skiaDir/out/Release-x64/icudtl.dat"))
} }
from(createChecksums.get().outputs.files)
} }
project.tasks.register<JavaExec>("run") { project.tasks.register<JavaExec>("run") {
......
package org.jetbrains.skiko package org.jetbrains.skiko
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.File import java.io.File
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
// based on https://github.com/JetBrains/skija/blob/bc6e0531021e4ff839c07196e0336d6456ed7520/src/main/java/org/jetbrains/skija/Library.java
object Library { object Library {
private var loaded = false private var loaded = false
// https://github.com/adamheinrich/native-utils/blob/e6a39489662846a77504634b6fafa4995ede3b1d/src/main/java/cz/adamh/utils/NativeUtils.java private val cacheDir = "${System.getProperty("user.home")}/.skiko/"
private fun loadOrGet(path: String, resource: String, isLibrary: Boolean) {
File(cacheDir).mkdirs()
val resourceName = if (isLibrary) System.mapLibraryName(resource) else resource
val hash = Library::class.java.getResourceAsStream("$path$resourceName.sha256").use {
BufferedReader(InputStreamReader(it)).lines().toArray()[0] as String
}
val fileName = if (isLibrary) System.mapLibraryName(hash) else hash
val file = File(cacheDir, fileName)
// TODO: small race change when multiple Compose apps are started first time, can handle with atomic rename.
if (!file.exists()) {
Library::class.java.getResourceAsStream("$path$resourceName").use { input ->
Files.copy(input, file.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
if (isLibrary) {
System.load(file.absolutePath)
}
}
@Synchronized @Synchronized
fun load(resourcePath: String, name: String) { fun load(resourcePath: String, name: String) {
if (loaded) return if (loaded) return
val libFileName = System.mapLibraryName(name) loadOrGet(resourcePath, name, true)
val url = Library::class.java.getResource(resourcePath + libFileName)
val tempRoot = File(System.getProperty("java.io.tmpdir"))
val tempDir = tempRoot.resolve("skiko_" + System.nanoTime()).apply { mkdirs() }
val libFile = when {
url == null -> error("Library $libFileName is not found in $resourcePath")
url.protocol == "file" -> File(url.toURI())
else -> url.openStream().use { input ->
File(tempDir, libFileName).also {
it.deleteOnExit()
Files.copy(input, it.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
}
val loadIcu = System.getProperty("os.name").toLowerCase().startsWith("win") val loadIcu = System.getProperty("os.name").toLowerCase().startsWith("win")
val icuData = "icudtl.dat"
if (loadIcu) { if (loadIcu) {
val icuUrl = Library::class.java.getResource(resourcePath + icuData) loadOrGet(resourcePath, "icudtl.dat", false)
icuUrl.openStream().use { input ->
File(tempDir, icuData).also {
it.deleteOnExit()
Files.copy(input, it.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
}
} }
System.load(libFile.absolutePath)
loaded = true loaded = true
// we have to set this property to avoid render flickering. // we have to set this property to avoid render flickering.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment