Skip to content

Commit 458c84d

Browse files
authored
Merge pull request #147 from powersync-ja/native-static-link-sqlite
Link SQLite statically on native platforms
2 parents d81122a + 4aee79d commit 458c84d

File tree

9 files changed

+225
-139
lines changed

9 files changed

+225
-139
lines changed

core/build.gradle.kts

Lines changed: 2 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import com.android.build.gradle.internal.tasks.factory.dependsOn
21
import com.powersync.plugins.sonatype.setupGithubRepository
32
import de.undercouch.gradle.tasks.download.Download
43
import org.gradle.internal.os.OperatingSystem
@@ -22,63 +21,6 @@ plugins {
2221
alias(libs.plugins.kotlin.atomicfu)
2322
}
2423

25-
val sqliteVersion = "3450200"
26-
val sqliteReleaseYear = "2024"
27-
28-
val sqliteSrcFolder =
29-
project.layout.buildDirectory
30-
.dir("native/sqlite")
31-
.get()
32-
33-
val downloadSQLiteSources by tasks.registering(Download::class) {
34-
val zipFileName = "sqlite-amalgamation-$sqliteVersion.zip"
35-
val destination = sqliteSrcFolder.file(zipFileName).asFile
36-
src("https://www.sqlite.org/$sqliteReleaseYear/$zipFileName")
37-
dest(destination)
38-
onlyIfNewer(true)
39-
overwrite(false)
40-
}
41-
42-
val unzipSQLiteSources by tasks.registering(Copy::class) {
43-
dependsOn(downloadSQLiteSources)
44-
45-
from(
46-
zipTree(downloadSQLiteSources.get().dest).matching {
47-
include("*/sqlite3.*")
48-
exclude {
49-
it.isDirectory
50-
}
51-
eachFile {
52-
this.path = this.name
53-
}
54-
},
55-
)
56-
into(sqliteSrcFolder)
57-
}
58-
59-
val buildCInteropDef by tasks.registering {
60-
dependsOn(unzipSQLiteSources)
61-
62-
val interopFolder =
63-
project.layout.buildDirectory
64-
.dir("interop/sqlite")
65-
.get()
66-
67-
val cFile = sqliteSrcFolder.file("sqlite3.c").asFile
68-
val defFile = interopFolder.file("sqlite3.def").asFile
69-
70-
doFirst {
71-
defFile.writeText(
72-
"""
73-
package = com.powersync.sqlite3
74-
---
75-
76-
""".trimIndent() + cFile.readText(),
77-
)
78-
}
79-
outputs.files(defFile)
80-
}
81-
8224
val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop")
8325
val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) {
8426
description = "Download PowerSync core extensions for JVM builds and releases"
@@ -233,27 +175,11 @@ kotlin {
233175
compileTaskProvider {
234176
compilerOptions.freeCompilerArgs.add("-Xexport-kdoc")
235177
}
236-
237-
cinterops.create("sqlite") {
238-
val cInteropTask = tasks[interopProcessingTaskName]
239-
cInteropTask.dependsOn(buildCInteropDef)
240-
definitionFile =
241-
buildCInteropDef
242-
.get()
243-
.outputs.files.singleFile
244-
compilerOpts.addAll(
245-
listOf(
246-
"-DHAVE_GETHOSTUUID=0",
247-
"-DSQLITE_ENABLE_LOAD_EXTENSION=1",
248-
"-DSQLITE_ENABLE_FTS5",
249-
),
250-
)
251-
}
252178
}
253179

254180
if (konanTarget.family == Family.IOS && konanTarget.name.contains("simulator")) {
255181
binaries.withType<TestExecutable>().configureEach {
256-
linkTaskProvider.dependsOn(unzipPowersyncFramework)
182+
linkTaskProvider.configure { dependsOn(unzipPowersyncFramework) }
257183
linkerOpts("-framework", "powersync-sqlite-core")
258184
val frameworkRoot =
259185
binariesFolder
@@ -310,7 +236,7 @@ kotlin {
310236
implementation(libs.kotlinx.datetime)
311237
implementation(libs.stately.concurrency)
312238
implementation(libs.configuration.annotations)
313-
api(project(":persistence"))
239+
api(projects.persistence)
314240
api(libs.kermit)
315241
}
316242

@@ -378,17 +304,6 @@ android {
378304
.get()
379305
.toInt()
380306
consumerProguardFiles("proguard-rules.pro")
381-
382-
@Suppress("UnstableApiUsage")
383-
externalNativeBuild {
384-
cmake {
385-
arguments.addAll(
386-
listOf(
387-
"-DSQLITE3_SRC_DIR=${sqliteSrcFolder.asFile.absolutePath}",
388-
),
389-
)
390-
}
391-
}
392307
}
393308

394309
sourceSets {
@@ -428,27 +343,4 @@ val testWithJava8 by tasks.registering(KotlinJvmTest::class) {
428343
}
429344
tasks.named("check").configure { dependsOn(testWithJava8) }
430345

431-
afterEvaluate {
432-
val buildTasks =
433-
tasks.matching {
434-
val taskName = it.name
435-
if (taskName.contains("Clean")) {
436-
return@matching false
437-
}
438-
if (taskName.contains("externalNative") ||
439-
taskName.contains("CMake") ||
440-
taskName.contains(
441-
"generateJsonModel",
442-
)
443-
) {
444-
return@matching true
445-
}
446-
return@matching false
447-
}
448-
449-
buildTasks.forEach {
450-
it.dependsOn(buildCInteropDef)
451-
}
452-
}
453-
454346
setupGithubRepository()

core/src/iosMain/kotlin/com/powersync/DatabaseDriverFactory.ios.kt

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ import co.touchlab.sqliter.DatabaseConfiguration.Logging
55
import co.touchlab.sqliter.DatabaseConnection
66
import co.touchlab.sqliter.interop.Logger
77
import co.touchlab.sqliter.interop.SqliteErrorType
8+
import co.touchlab.sqliter.sqlite3.sqlite3_commit_hook
9+
import co.touchlab.sqliter.sqlite3.sqlite3_enable_load_extension
10+
import co.touchlab.sqliter.sqlite3.sqlite3_load_extension
11+
import co.touchlab.sqliter.sqlite3.sqlite3_rollback_hook
12+
import co.touchlab.sqliter.sqlite3.sqlite3_update_hook
813
import com.powersync.db.internal.InternalSchema
914
import com.powersync.persistence.driver.NativeSqliteDriver
1015
import com.powersync.persistence.driver.wrapConnection
11-
import com.powersync.sqlite3.sqlite3
12-
import com.powersync.sqlite3.sqlite3_commit_hook
13-
import com.powersync.sqlite3.sqlite3_enable_load_extension
14-
import com.powersync.sqlite3.sqlite3_load_extension
15-
import com.powersync.sqlite3.sqlite3_rollback_hook
16-
import com.powersync.sqlite3.sqlite3_update_hook
1716
import kotlinx.cinterop.ByteVar
1817
import kotlinx.cinterop.CPointerVar
1918
import kotlinx.cinterop.ExperimentalForeignApi
@@ -23,7 +22,6 @@ import kotlinx.cinterop.alloc
2322
import kotlinx.cinterop.asStableRef
2423
import kotlinx.cinterop.nativeHeap
2524
import kotlinx.cinterop.ptr
26-
import kotlinx.cinterop.reinterpret
2725
import kotlinx.cinterop.staticCFunction
2826
import kotlinx.cinterop.toKString
2927
import kotlinx.cinterop.value
@@ -103,7 +101,7 @@ public actual class DatabaseDriverFactory {
103101
connection: DatabaseConnection,
104102
driver: DeferredDriver,
105103
) {
106-
val basePointer = connection.getDbPointer().getPointer(MemScope())
104+
val ptr = connection.getDbPointer().getPointer(MemScope())
107105
// Try and find the bundle path for the SQLite core extension.
108106
val bundlePath =
109107
NSBundle.bundleWithIdentifier("co.powersync.sqlitecore")?.bundlePath
@@ -116,10 +114,6 @@ public actual class DatabaseDriverFactory {
116114
// Construct full path to the shared library inside the bundle
117115
val extensionPath = bundlePath.let { "$it/powersync-sqlite-core" }
118116

119-
// We have a mix of SQLite operations. The SQliteR lib links to the system SQLite with `-lsqlite3`
120-
// However we also include our own build of SQLite which is statically linked.
121-
// Loading of extensions is only available using our version of SQLite's API
122-
val ptr = basePointer.reinterpret<sqlite3>()
123117
// Enable extension loading
124118
// We don't disable this after the fact, this should allow users to load their own extensions
125119
// in future.
@@ -183,13 +177,8 @@ public actual class DatabaseDriverFactory {
183177
private fun deregisterSqliteBinding(connection: DatabaseConnection) {
184178
val basePtr = connection.getDbPointer().getPointer(MemScope())
185179

186-
// We have a mix of SQLite operations. The SQliteR lib links to the system SQLite with `-lsqlite3`
187-
// However we also include our own build of SQLite which is statically linked.
188-
// Loading of extensions is only available using our version of SQLite's API
189-
val ptr = basePtr.reinterpret<sqlite3>()
190-
191180
sqlite3_update_hook(
192-
ptr,
181+
basePtr,
193182
null,
194183
null,
195184
)

demos/supabase-todolist/iosApp/iosApp.xcodeproj/project.pbxproj

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -397,10 +397,7 @@
397397
"$(inherited)",
398398
"@executable_path/Frameworks",
399399
);
400-
OTHER_LDFLAGS = (
401-
"$(inherited)",
402-
"-lsqlite3",
403-
);
400+
OTHER_LDFLAGS = "$(inherited)";
404401
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
405402
PRODUCT_NAME = "${APP_NAME}";
406403
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -425,10 +422,7 @@
425422
"$(inherited)",
426423
"@executable_path/Frameworks",
427424
);
428-
OTHER_LDFLAGS = (
429-
"$(inherited)",
430-
"-lsqlite3",
431-
);
425+
OTHER_LDFLAGS = "$(inherited)";
432426
PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
433427
PRODUCT_NAME = "${APP_NAME}";
434428
PROVISIONING_PROFILE_SPECIFIER = "";

gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ uuid = "0.8.2"
1818
powersync-core = "0.3.12"
1919
sqlite-android = "3.45.0"
2020
sqlite-jdbc = "3.45.2.0"
21+
sqliter = "1.3.1"
2122
turbine = "1.2.0"
2223

2324
sqlDelight = "2.0.2"
@@ -81,7 +82,8 @@ ktor-client-mock = { module = "io.ktor:ktor-client-mock", version.ref = "ktor" }
8182
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
8283
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
8384

84-
sqldelight-driver-ios = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" }
85+
sqldelight-driver-native = { module = "app.cash.sqldelight:native-driver", version.ref = "sqlDelight" }
86+
sqliter = { module = "co.touchlab:sqliter-driver", version.ref = "sqliter" }
8587
sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqlDelight" }
8688
sqldelight-driver-jdbc = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "sqlDelight" }
8789
requery-sqlite-android = { module = "com.github.requery:sqlite-android", version.ref = "sqlite-android" }

persistence/build.gradle.kts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ kotlin {
3636
explicitApi()
3737

3838
sourceSets {
39-
4039
commonMain.dependencies {
4140
api(libs.bundles.sqldelight)
4241
}
@@ -53,7 +52,8 @@ kotlin {
5352
}
5453

5554
iosMain.dependencies {
56-
api(libs.sqldelight.driver.ios)
55+
api(libs.sqldelight.driver.native)
56+
api(projects.staticSqliteDriver)
5757
}
5858
}
5959
}
@@ -90,6 +90,8 @@ android {
9090
}
9191

9292
sqldelight {
93+
linkSqlite = false
94+
9395
databases {
9496
create("PsDatabase") {
9597
packageName.set("com.powersync.persistence")

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ rootProject.name = "powersync-root"
2323
include(":core")
2424
include(":core-tests-android")
2525
include(":connectors:supabase")
26+
include("static-sqlite-driver")
2627

2728
include(":dialect")
2829
include(":persistence")

static-sqlite-driver/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This project builds a `.klib` linking sqlite3 statically, without containing other Kotlin sources.

0 commit comments

Comments
 (0)