Compile BundledSQLiteDriver's JNI for Windows
Adds MINGW_X64 as a compilation target for sqlite-bundled. Fixes androidx Gradle logic to find Windows's JNI and refactor the location of the native artifacts to the '/natives/<platform>' folder in the jar (instead of top-level platform folders). Finally, fix a bit NativeLibraryLoader to support Window's '.dll' files.
Bug: 335521661
Test: Validated sqlite-bundled usage in sample JVM app in a Windows host.
Change-Id: I03c10d3437b5d16bcb42520b9f5d0f3807718b23
diff --git a/sqlite/sqlite-bundled/build.gradle b/sqlite/sqlite-bundled/build.gradle
index 8df8903..1f3d1d3 100644
--- a/sqlite/sqlite-bundled/build.gradle
+++ b/sqlite/sqlite-bundled/build.gradle
@@ -106,6 +106,7 @@
KonanTarget.ANDROID_X86,
KonanTarget.MACOS_ARM64,
KonanTarget.MACOS_X64,
+ KonanTarget.MINGW_X64,
KonanTarget.LINUX_X64,
].collect { it.INSTANCE } // Use INSTANCE to get object class instance from kotlin
@@ -144,7 +145,7 @@
)
// Define C++ compilation of JNI
- def jvmArtJniImplementation = createNativeCompilation("jvmArtJniImplementation") {
+ def jvmArtJniImplementation = createNativeCompilation("sqliteJni") {
configureEachTarget { nativeCompilation ->
// add JNI headers as sources
nativeCompilation.addJniHeaders()
diff --git a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
index 4b0ec4b..22b981a 100644
--- a/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
+++ b/sqlite/sqlite-bundled/src/androidJvmCommonMain/kotlin/androidx/sqlite/driver/bundled/BundledSQLiteDriver.androidJvmCommon.kt
@@ -33,7 +33,7 @@
private companion object {
init {
- NativeLibraryLoader.loadLibrary("jvmArtJniImplementation")
+ NativeLibraryLoader.loadLibrary("sqliteJni")
}
}
}
diff --git a/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt b/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
index c54b9a6..9edb7a3 100644
--- a/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
+++ b/sqlite/sqlite-bundled/src/androidMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.android.kt
@@ -16,8 +16,11 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
- actual fun loadLibrary(name: String) {
+ actual fun loadLibrary(name: String): Unit = synchronized(this) {
System.loadLibrary(name)
}
}
diff --git a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
index 6930b5f..5482ca3 100644
--- a/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
+++ b/sqlite/sqlite-bundled/src/commonMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.kt
@@ -16,6 +16,9 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal expect object NativeLibraryLoader {
/**
* Loads the given native library via JNI (if running on JVM or Android).
diff --git a/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt b/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
index cae82a8..79eb81a 100644
--- a/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
+++ b/sqlite/sqlite-bundled/src/jvmMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.jvm.kt
@@ -20,16 +20,40 @@
import java.nio.file.StandardCopyOption
import java.util.Locale
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
// TODO(b/304281116): Generate this via Gradle so it is consistent.
- actual fun loadLibrary(name: String) {
+ actual fun loadLibrary(name: String): Unit = synchronized(this) {
try {
System.loadLibrary(name)
return
- } catch (error: Throwable) {
- // looks like we are not on Android, continue
+ } catch (error: UnsatisfiedLinkError) {
+ // Likely not on Android, continue...
}
- // TODO(b/304281116): Temporary loading implementation
+ // TODO(b/304281116): Improve loading implementation
+ val libResourceName = getResourceName(name)
+ val libTempCopy = Files.createTempFile("androidx_$name", null)
+ .apply { toFile().deleteOnExit() }
+ NativeLibraryLoader::class.java.classLoader!!.getResourceAsStream(
+ libResourceName
+ ).use { resourceStream ->
+ checkNotNull(resourceStream) {
+ "Cannot find a suitable SQLite binary for ${System.getProperty("os.name")} | " +
+ "${System.getProperty("os.arch")}. Please file a bug at " +
+ "https://issuetracker.google.com/issues/new?component=460784"
+ }
+ Files.copy(resourceStream, libTempCopy, StandardCopyOption.REPLACE_EXISTING)
+ }
+ @Suppress("UnsafeDynamicallyLoadedCode")
+ System.load(libTempCopy.toFile().canonicalPath)
+ }
+
+ /**
+ * Gets the JAR's resource file path to the native library.
+ */
+ private fun getResourceName(name: String): String {
val osName =
System.getProperty("os.name")?.lowercase(Locale.US) ?: error("Cannot read osName")
val osArch =
@@ -37,6 +61,7 @@
val osPrefix = when {
osName.contains("linux") -> "linux"
osName.contains("mac") || osName.contains("osx") -> "osx"
+ osName.contains("windows") -> "windows"
else -> error("Unsupported operating system: $osName")
}
val archSuffix = when {
@@ -50,19 +75,12 @@
else -> error("Unsupported architecture: $osArch")
}
val resourceFolder = "${osPrefix}_$archSuffix"
- val ext = if (osPrefix == "linux") { "so" } else { "dylib" }
- val resourceName = "$resourceFolder/lib$name.$ext"
- val nativeLibCopy = Files.createTempFile("androidx_$name", null)
- nativeLibCopy.toFile().deleteOnExit()
- NativeLibraryLoader::class.java.classLoader!!.getResourceAsStream(
- resourceName
- ).use { resourceStream ->
- checkNotNull(resourceStream) {
- "Cannot find resource $resourceName"
- }
- Files.copy(resourceStream, nativeLibCopy, StandardCopyOption.REPLACE_EXISTING)
+ val extension = when (osPrefix) {
+ "linux" -> "so"
+ "osx" -> "dylib"
+ "windows" -> "dll"
+ else -> error("Unsupported operating system: $osName")
}
- @Suppress("UnsafeDynamicallyLoadedCode")
- System.load(nativeLibCopy.toFile().canonicalPath)
+ return "natives/$resourceFolder/lib$name.$extension"
}
}
diff --git a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
index 1ae2bf8..c1cf339 100644
--- a/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
+++ b/sqlite/sqlite-bundled/src/nativeMain/kotlin/androidx/sqlite/driver/bundled/NativeLibraryLoader.nativeCommon.kt
@@ -16,6 +16,9 @@
package androidx.sqlite.driver.bundled
+/**
+ * Helper class to load native libraries based on the host platform.
+ */
internal actual object NativeLibraryLoader {
actual fun loadLibrary(name: String) {
// no-op, we are already in native code