Unverified Commit a7763616 authored by Igor Demin's avatar Igor Demin Committed by GitHub

System property to extract binaries to a different folder (#891)

Skiko extracts binaries to `~/.skiko` by default, but it is not always
possible. This PR adds a way to override the folder where to extract
binaries:
```
System.setProperty("skiko.data.path", File(System.getProperty("java.io.tmpdir")).resolve(".skiko").toString())
```

Fixes https://github.com/JetBrains/skiko/issues/885

The case seems rare - we only have a crash in tests reported. If we have
reports from real users, we have to change the default `~/.skiko` to
something else.

## Testing 1 (manual)
1. Run
```
import org.jetbrains.skia.Bitmap
import java.io.File

fun main() {
    val path = File(System.getProperty("java.io.tmpdir")).resolve(".skiko").toString()
    println(path)
    System.setProperty("skiko.data.path", path)
    Bitmap() // loads the library
}
```
2. See that `path` is created
## Testing 2 (manual)
The default way works:
```
import org.jetbrains.skia.Bitmap

fun main() {
    Bitmap() // loads the library
}
```
parent 47215871
...@@ -12,7 +12,7 @@ internal class RenderExceptionsHandler { ...@@ -12,7 +12,7 @@ internal class RenderExceptionsHandler {
fun throwException(message: String) { fun throwException(message: String) {
if (output == null) { if (output == null) {
output = File( output = File(
"${Library.cacheRoot}/skiko-render-exception-${ProcessHandle.current().pid()}.log" "${SkikoProperties.dataPath}/skiko-render-exception-${ProcessHandle.current().pid()}.log"
) )
} }
val exception = RenderException(message) val exception = RenderException(message)
......
...@@ -71,31 +71,21 @@ private fun newInstance(loader: ClassLoader, fqName: String, vararg args: Any): ...@@ -71,31 +71,21 @@ private fun newInstance(loader: ClassLoader, fqName: String, vararg args: Any):
class SeveralClassloadersTest { class SeveralClassloadersTest {
@Test @Test
fun `load skiko in several classloaders (with skiko path)`() { fun `load skiko in several classloaders (with skiko path)`() {
check(skikoLibraryPath != null) check(SkikoProperties.libraryPath != null)
doTest() doTest()
} }
@Test @Test
fun `load skiko in several classloaders (without skiko path)`() { fun `load skiko in several classloaders (without skiko path)`() {
val oldValue = skikoLibraryPath!! val oldValue = SkikoProperties.libraryPath!!
skikoLibraryPath = null SkikoProperties.libraryPath = null
try { try {
doTest() doTest()
} finally { } finally {
skikoLibraryPath = oldValue SkikoProperties.libraryPath = oldValue
} }
} }
private var skikoLibraryPath: String?
get() = System.getProperty(Library.SKIKO_LIBRARY_PATH_PROPERTY)
set(value) {
if (value != null) {
System.setProperty(Library.SKIKO_LIBRARY_PATH_PROPERTY, value)
} else {
System.clearProperty(Library.SKIKO_LIBRARY_PATH_PROPERTY)
}
}
private fun doTest() { private fun doTest() {
val threaded = false val threaded = false
val jar = System.getProperty("skiko.jar.path") val jar = System.getProperty("skiko.jar.path")
......
...@@ -7,9 +7,6 @@ import java.nio.file.StandardCopyOption ...@@ -7,9 +7,6 @@ import java.nio.file.StandardCopyOption
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
object Library { object Library {
internal const val SKIKO_LIBRARY_PATH_PROPERTY = "skiko.library.path"
internal val cacheRoot = "${System.getProperty("user.home")}/.skiko/"
private val skikoLibraryPath = System.getProperty(SKIKO_LIBRARY_PATH_PROPERTY)
private var copyDir: File? = null private var copyDir: File? = null
// A native library cannot be loaded in several classloaders, so we have to clone // A native library cannot be loaded in several classloaders, so we have to clone
...@@ -80,6 +77,7 @@ object Library { ...@@ -80,6 +77,7 @@ object Library {
} }
// First try: system property is set. // First try: system property is set.
val skikoLibraryPath = SkikoProperties.libraryPath
if (skikoLibraryPath != null) { if (skikoLibraryPath != null) {
val library = File(File(skikoLibraryPath), platformName) val library = File(File(skikoLibraryPath), platformName)
loadLibraryOrCopy(library) loadLibraryOrCopy(library)
...@@ -107,9 +105,9 @@ object Library { ...@@ -107,9 +105,9 @@ object Library {
) )
val hash = hashResourceStream.use { it.bufferedReader().readLine() } val hash = hashResourceStream.use { it.bufferedReader().readLine() }
val cacheDir = File(File(cacheRoot), hash) val dataDir = File(File(SkikoProperties.dataPath), hash)
cacheDir.mkdirs() dataDir.mkdirs()
val library = unpackIfNeeded(cacheDir, platformName, false) val library = unpackIfNeeded(dataDir, platformName, false)
loadLibraryOrCopy(library) loadLibraryOrCopy(library)
if (icu != null) { if (icu != null) {
if (copyDir != null) { if (copyDir != null) {
...@@ -117,7 +115,7 @@ object Library { ...@@ -117,7 +115,7 @@ object Library {
unpackIfNeeded(copyDir!!, icu, true) unpackIfNeeded(copyDir!!, icu, true)
} else { } else {
// Normal path where Skiko is loaded only once. // Normal path where Skiko is loaded only once.
unpackIfNeeded(cacheDir, icu, false) unpackIfNeeded(dataDir, icu, false)
} }
} }
} }
......
...@@ -8,6 +8,30 @@ import java.lang.System.getProperty ...@@ -8,6 +8,30 @@ import java.lang.System.getProperty
* Global Skiko properties, which are read from system JDK variables orr from environment variables * Global Skiko properties, which are read from system JDK variables orr from environment variables
*/ */
object SkikoProperties { object SkikoProperties {
/**
* Path where the Skiko binaries (dll/so/dylib, depending on OS) are placed.
*
* If defined, SKiko doesn't extract binaries from `jar` files to external folder.
*
* If null (default), it extracts them to `libraryCachePath`
*/
var libraryPath: String?
get() = getProperty("skiko.library.path")
internal set(value) {
if (value != null) {
System.setProperty("skiko.library.path", value)
} else {
System.clearProperty("skiko.library.path")
}
}
/**
* The path where to store data files.
*
* It is used for extracting the Skiko binaries (if `libraryPath` isn't null) and logging.
*/
val dataPath: String get() = getProperty("skiko.data.path") ?: "${getProperty("user.home")}/.skiko/"
val vsyncEnabled: Boolean get() = getProperty("skiko.vsync.enabled")?.toBoolean() ?: true val vsyncEnabled: Boolean get() = getProperty("skiko.vsync.enabled")?.toBoolean() ?: true
/** /**
......
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