Skip to content

Commit 704865c

Browse files
committed
Add macOS sqlite3mc build
1 parent 8682730 commit 704865c

File tree

20 files changed

+6979
-16
lines changed

20 files changed

+6979
-16
lines changed

plugins/build-plugin/src/main/kotlin/com/powersync/compile/ClangCompile.kt

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,7 @@ abstract class ClangCompile : DefaultTask() {
4242

4343
@get:Input
4444
val xcodeInstallation: Provider<String>
45-
get() =
46-
providers
47-
.exec {
48-
executable("xcode-select")
49-
args("-p")
50-
}.standardOutput.asText
45+
get() = resolveXcode(providers)
5146

5247
@TaskAction
5348
fun run() {
@@ -95,15 +90,7 @@ abstract class ClangCompile : DefaultTask() {
9590
"--compile",
9691
"-I${include.get().asFile.absolutePath}",
9792
inputFile.get().asFile.absolutePath,
98-
"-DHAVE_GETHOSTUUID=0",
99-
"-DSQLITE_ENABLE_DBSTAT_VTAB",
100-
"-DSQLITE_ENABLE_FTS5",
101-
"-DSQLITE_ENABLE_RTREE",
102-
// Used by GRDB
103-
"-DSQLITE_ENABLE_SNAPSHOT",
104-
// Used for GRDB update hook like functionality
105-
"-DSQLITE_ENABLE_SESSION",
106-
"-DSQLITE_ENABLE_PREUPDATE_HOOK",
93+
*sqlite3ClangOptions,
10794
//
10895
"-O3",
10996
"-o",
@@ -126,5 +113,26 @@ abstract class ClangCompile : DefaultTask() {
126113
const val TVOS_SIMULATOR_SDK =
127114
"Platforms/AppleTVSimulator.platform/Developer/SDKs/AppleTVSimulator.sdk"
128115
const val MACOS_SDK = "Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/"
116+
117+
val sqlite3ClangOptions = arrayOf(
118+
// Note: Keep in sync with sqlite3multiplecipers/src/jni/CMakeLists.txt
119+
"-DHAVE_GETHOSTUUID=0",
120+
"-DSQLITE_ENABLE_DBSTAT_VTAB",
121+
"-DSQLITE_ENABLE_FTS5",
122+
"-DSQLITE_ENABLE_RTREE",
123+
// Used by GRDB
124+
"-DSQLITE_ENABLE_SNAPSHOT",
125+
// Used for GRDB update hook like functionality
126+
"-DSQLITE_ENABLE_SESSION",
127+
"-DSQLITE_ENABLE_PREUPDATE_HOOK",
128+
)
129+
130+
fun resolveXcode(factory: ProviderFactory): Provider<String> {
131+
return factory
132+
.exec {
133+
executable("xcode-select")
134+
args("-p")
135+
}.standardOutput.asText
136+
}
129137
}
130138
}

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ include(":core")
3737
include(":core-tests-android")
3838
include(":integrations:room")
3939
include(":static-sqlite-driver")
40+
include(":sqlite3multipleciphers")
4041

4142
include(":integrations:sqldelight")
4243
include(":integrations:sqldelight-test-database")
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import com.android.build.gradle.tasks.ExternalNativeBuildTask
2+
import com.powersync.compile.ClangCompile
3+
import com.powersync.compile.CreateSqliteCInterop
4+
import com.powersync.compile.UnzipSqlite
5+
import com.powersync.plugins.utils.powersyncTargets
6+
import de.undercouch.gradle.tasks.download.Download
7+
import org.gradle.kotlin.dsl.register
8+
import org.gradle.kotlin.dsl.withType
9+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
10+
import org.jetbrains.kotlin.konan.target.HostManager
11+
import kotlin.io.path.Path
12+
import kotlin.io.path.absolutePathString
13+
14+
plugins {
15+
alias(libs.plugins.kotlinMultiplatform)
16+
alias(libs.plugins.android.library)
17+
alias(libs.plugins.downloadPlugin)
18+
alias(libs.plugins.kotlinter)
19+
id("com.powersync.plugins.sonatype")
20+
id("com.powersync.plugins.sharedbuild")
21+
}
22+
23+
val sqlite3McVersion = "2.2.4"
24+
val sqlite3BaseVersion = "3.50.4"
25+
26+
val downloadSqlite3Mc by tasks.registering(Download::class) {
27+
val zipFileName = "sqlite3mc-$sqlite3McVersion.zip"
28+
src("https://github.com/utelle/SQLite3MultipleCiphers/releases/download/v$sqlite3McVersion/sqlite3mc-$sqlite3McVersion-sqlite-$sqlite3BaseVersion-amalgamation.zip")
29+
dest(layout.buildDirectory.dir("downloads").map { it.file(zipFileName) })
30+
onlyIfNewer(true)
31+
overwrite(false)
32+
}
33+
34+
val unzipSQLiteSources by tasks.registering(UnzipSqlite::class) {
35+
val zip = downloadSqlite3Mc.map { it.outputs.files.singleFile }
36+
inputs.file(zip)
37+
38+
unzipSqlite(
39+
src = zipTree(zip),
40+
dir = layout.buildDirectory.dir("downloads/sqlite3mc"),
41+
filter = null,
42+
)
43+
}
44+
45+
val hostManager = HostManager()
46+
47+
fun compileSqlite3McForKotlinNative(target: KotlinNativeTarget): TaskProvider<CreateSqliteCInterop> {
48+
val name = target.targetName
49+
val outputDir = layout.buildDirectory.dir("c/$name")
50+
51+
val sqlite3Obj = outputDir.map { it.file("sqlite3mc.o") }
52+
val archive = outputDir.map { it.file("libsqlite3mc.a") }
53+
54+
val compileSqlite = tasks.register("${name}CompileSqlite", ClangCompile::class) {
55+
dependsOn(unzipSQLiteSources)
56+
inputs.dir(unzipSQLiteSources.map { it.destinationDir })
57+
58+
inputFile.set(unzipSQLiteSources.flatMap { it.destination.file("sqlite3mc_amalgamation.c") })
59+
konanTarget.set(target.konanTarget.name)
60+
include.set(unzipSQLiteSources.flatMap { it.destination })
61+
objectFile.set(sqlite3Obj)
62+
}
63+
64+
val createStaticLibrary = tasks.register("${name}ArchiveSqlite", com.powersync.compile.CreateStaticLibrary::class) {
65+
inputs.file(compileSqlite.map { it.objectFile })
66+
objects.from(sqlite3Obj)
67+
staticLibrary.set(archive)
68+
}
69+
70+
val buildCInteropDef = tasks.register("${name}CinteropSqlite", CreateSqliteCInterop::class) {
71+
inputs.file(createStaticLibrary.map { it.staticLibrary })
72+
73+
archiveFile.set(archive)
74+
definitionFile.fileProvider(archive.map { File(it.asFile.parentFile, "sqlite3mc.def") })
75+
}
76+
77+
return buildCInteropDef
78+
}
79+
80+
kotlin {
81+
powersyncTargets()
82+
83+
applyDefaultHierarchyTemplate()
84+
explicitApi()
85+
86+
sourceSets {
87+
all {
88+
languageSettings.apply {
89+
optIn("kotlin.experimental.ExperimentalNativeApi")
90+
optIn("kotlinx.cinterop.ExperimentalForeignApi")
91+
optIn("kotlinx.cinterop.BetaInteropApi")
92+
}
93+
}
94+
95+
val jvmAndroidMain by creating {
96+
dependsOn(commonMain.get())
97+
}
98+
99+
androidMain {
100+
dependsOn(jvmAndroidMain)
101+
}
102+
103+
jvmMain {
104+
dependsOn(jvmAndroidMain)
105+
}
106+
107+
commonMain.dependencies {
108+
api(projects.common)
109+
implementation(libs.androidx.sqlite.sqlite)
110+
}
111+
112+
commonTest.dependencies {
113+
implementation(libs.kotlin.test)
114+
api(libs.test.kotest.assertions)
115+
}
116+
}
117+
118+
targets.withType<KotlinNativeTarget> {
119+
if (hostManager.isEnabled(konanTarget)) {
120+
val compileSqlite3 = compileSqlite3McForKotlinNative(this)
121+
122+
compilations.named("main") {
123+
cinterops.create("sqlite3mc") {
124+
definitionFile.set(compileSqlite3.flatMap { it.definitionFile })
125+
}
126+
}
127+
}
128+
}
129+
}
130+
131+
android {
132+
compileOptions {
133+
targetCompatibility = JavaVersion.VERSION_17
134+
}
135+
136+
namespace = "com.powersync.encryption"
137+
compileSdk =
138+
libs.versions.android.compileSdk
139+
.get()
140+
.toInt()
141+
defaultConfig {
142+
minSdk =
143+
libs.versions.android.minSdk
144+
.get()
145+
.toInt()
146+
// consumerProguardFiles("proguard-rules.pro")
147+
}
148+
149+
externalNativeBuild {
150+
cmake {
151+
path("src/jni/CMakeLists.txt")
152+
}
153+
}
154+
155+
ndkVersion = "27.1.12297006"
156+
}
157+
158+
tasks.withType<ExternalNativeBuildTask> {
159+
dependsOn(unzipSQLiteSources)
160+
}
161+
162+
val xCodeInstallation = ClangCompile.resolveXcode(providers)
163+
164+
// Tasks to build the JNI shared library for multiple operating systems.
165+
// Since the JNI sources rarely change, we don't run these tasks on every build. Instead,
166+
// we'll publish these sources as one-off releases when needed, and then reference that URL.
167+
fun registerCompileMacOsHostTask(architecture: String): TaskProvider<Exec> {
168+
return tasks.register<Exec>("jniCompileMacos$architecture") {
169+
val xcode = Path(xCodeInstallation.get())
170+
val toolchain =
171+
xcode.resolve("Toolchains/XcodeDefault.xctoolchain/usr/bin").absolutePathString()
172+
173+
val outputDirectory = layout.buildDirectory.dir("jni-build/macos")
174+
val outputFile = outputDirectory.map { it.file("libsqlite3mc_jni.$architecture.dylib") }
175+
outputs.file(outputFile)
176+
177+
dependsOn(unzipSQLiteSources)
178+
val sqlite3McSources = unzipSQLiteSources.map { it.destinationDir }
179+
inputs.dir(sqlite3McSources)
180+
181+
val headers = layout.projectDirectory.dir("src/jni/headers/inc_mac")
182+
inputs.dir(headers)
183+
184+
doFirst {
185+
outputDirectory.get().asFile.mkdirs()
186+
}
187+
188+
executable = "clang"
189+
args(
190+
"-B$toolchain",
191+
"-dynamiclib",
192+
"-fPIC",
193+
"--target=${architecture}-apple-macos",
194+
"-o",
195+
outputFile.get().asFile.path,
196+
"src/jni/sqlite_bindings.cpp",
197+
File(sqlite3McSources.get(), "sqlite3mc_amalgamation.c").path,
198+
"-I",
199+
sqlite3McSources.get().path,
200+
"-I",
201+
headers.asFile.path,
202+
"-O3",
203+
*ClangCompile.sqlite3ClangOptions,
204+
)
205+
}
206+
}
207+
208+
val macosArm64 = registerCompileMacOsHostTask("aarch64")
209+
val macosX64 = registerCompileMacOsHostTask("x86_64")
210+
211+
tasks.register("jniCompile") {
212+
dependsOn(macosArm64)
213+
dependsOn(macosX64)
214+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.powersync.encryption
2+
3+
internal actual fun ensureJniLibraryLoaded() {
4+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.powersync.encryption
2+
3+
import androidx.sqlite.SQLiteConnection
4+
import androidx.sqlite.execSQL
5+
6+
public sealed interface Key {
7+
public class Passphrase(public val passphrase: String) : Key
8+
// TODO: Add raw key api
9+
}
10+
11+
internal fun SQLiteConnection.encryptOrClose(key: Key) {
12+
try {
13+
when (key) {
14+
is Key.Passphrase -> {
15+
val escaped = key.passphrase.replace("'", "''")
16+
execSQL("pragma key='$escaped'")
17+
}
18+
}
19+
} catch (e: Exception) {
20+
close()
21+
throw e
22+
}
23+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
project(
3+
powersync_sqlite3mc_bundled
4+
VERSION 3
5+
LANGUAGES C CXX
6+
)
7+
8+
set(CMAKE_C_FLAGS "-O3")
9+
10+
add_library(sqlite3mc_bundled SHARED "../../build/downloads/sqlite3mc/sqlite3mc_amalgamation.c" "sqlite_bindings.cpp")
11+
target_include_directories(sqlite3mc_bundled PRIVATE "../../build/downloads/sqlite3mc")
12+
13+
# Note: Keep in sync with the ClangCompile task used for static-sqlite-driver
14+
target_compile_definitions(sqlite3mc_bundled PUBLIC
15+
HAVE_GETHOSTUUID=0
16+
SQLITE_ENABLE_DBSTAT_VTAB
17+
SQLITE_ENABLE_FTS5
18+
SQLITE_ENABLE_RTREE
19+
SQLITE_ENABLE_SNAPSHOT
20+
SQLITE_ENABLE_SESSION
21+
SQLITE_ENABLE_PREUPDATE_HOOK
22+
)

0 commit comments

Comments
 (0)