@@ -3,6 +3,11 @@ package com.ensody.nativebuilds.loader
33import java.io.File
44import kotlin.uuid.Uuid
55
6+ /* *
7+ * JNI native .so (shared library) loading mechanism for JVM and Android.
8+ *
9+ * This works with the NativeBuilds project out of the box and can also be customized via [overrideLoader].
10+ */
611public object NativeBuildsJvmLoader {
712 private val loadedLibs = mutableSetOf<NativeBuildsJvmLib >()
813 private val tempDir by lazy {
@@ -13,46 +18,73 @@ public object NativeBuildsJvmLoader {
1318 }
1419 }
1520
21+ /* *
22+ * Allows customizing the loading mechanism.
23+ *
24+ * The default value is the default loading mechanism. To delegate to the default loading mechanism you can save
25+ * the old value before overriding:
26+ *
27+ * ```kotlin
28+ * val original = NativeBuildsJvmLoader.overrideLoader
29+ * NativeBuildsJvmLoader.overrideLoader = {
30+ * if (...) {
31+ * original(it)
32+ * } else {
33+ * // custom logic
34+ * }
35+ * }
36+ * ```
37+ */
38+ public var overrideLoader: (lib: NativeBuildsJvmLib ) -> Unit = ::realLoad
39+
40+ /* *
41+ * Loads the given [lib] for use with JNI code.
42+ */
1643 public fun load (lib : NativeBuildsJvmLib ) {
1744 synchronized(lib) {
1845 if (lib !in loadedLibs) {
19- val vendor = System .getProperty(" java.vendor" )
20- if (vendor == " The Android Project" ) {
21- System .loadLibrary(lib.libName.removePrefix(" lib" ))
22- loadedLibs.add(lib)
23- return
24- }
25- val osName = System .getProperty(" os.name" )!! .lowercase()
26- val osArch = when (val osArch = System .getProperty(" os.arch" )!! .lowercase()) {
27- " x86_64" , " amd64" -> " X64"
28- " aarch64" , " arm64" -> " Arm64"
29- else -> error(" Unsupported arch: $osArch (os=$osName )" )
30- }
31- val platform = when {
32- " windows" in osName -> " mingw$osArch "
33- " linux" in osName -> " linux$osArch "
34- " mac" in osName || " darwin" in osName -> " macos$osArch "
35- else -> error(" Unsupported OS: $osName (arch=$osArch )" )
36- }
37- val libFileName = lib.platformFileName[platform]
38- ? : error(" Could not find library ${lib.libName} for platform $platform " )
39- val path = " /jni/$platform /$libFileName "
40- val tempFile = File (tempDir, libFileName).apply {
41- deleteOnExit()
42- }
46+ overrideLoader(lib)
47+ loadedLibs.add(lib)
48+ }
49+ }
50+ }
4351
44- val resource = checkNotNull(lib::class .java.getResourceAsStream(path)) {
45- " Could not find shared library: $path "
46- }
47- resource.use { inputStream ->
48- tempFile.outputStream().use {
49- inputStream.copyTo(it)
50- }
51- }
52+ @Suppress(" UnsafeDynamicallyLoadedCode" )
53+ private fun realLoad (lib : NativeBuildsJvmLib ) {
54+ val vendor = System .getProperty(" java.vendor" )
55+ if (vendor == " The Android Project" ) {
56+ System .loadLibrary(lib.libName.removePrefix(" lib" ))
57+ loadedLibs.add(lib)
58+ return
59+ }
60+ val osName = System .getProperty(" os.name" )!! .lowercase()
61+ val osArch = when (val osArch = System .getProperty(" os.arch" )!! .lowercase()) {
62+ " x86_64" , " amd64" -> " X64"
63+ " aarch64" , " arm64" -> " Arm64"
64+ else -> error(" Unsupported arch: $osArch (os=$osName )" )
65+ }
66+ val platform = when {
67+ " windows" in osName -> " mingw$osArch "
68+ " linux" in osName -> " linux$osArch "
69+ " mac" in osName || " darwin" in osName -> " macos$osArch "
70+ else -> error(" Unsupported OS: $osName (arch=$osArch )" )
71+ }
72+ val libFileName = lib.platformFileName[platform]
73+ ? : error(" Could not find library ${lib.libName} for platform $platform " )
74+ val path = " /jni/$platform /$libFileName "
75+ val tempFile = File (tempDir, libFileName).apply {
76+ deleteOnExit()
77+ }
5278
53- System .load(tempFile.absolutePath)
54- loadedLibs.add(lib)
79+ val resource = checkNotNull(lib::class .java.getResourceAsStream(path)) {
80+ " Could not find shared library: $path "
81+ }
82+ resource.use { inputStream ->
83+ tempFile.outputStream().use {
84+ inputStream.copyTo(it)
5585 }
5686 }
87+
88+ System .load(tempFile.absolutePath)
5789 }
5890}
0 commit comments