Skip to content

Commit 5b402a7

Browse files
committed
Cross-compile setup
1 parent 216abca commit 5b402a7

File tree

6 files changed

+124
-3944
lines changed

6 files changed

+124
-3944
lines changed

sqlite3multipleciphers/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
## Building
2+
3+
Please note that the build currently only runs on macOS.
4+
We use cross-compilation to be able to compile for Windows and Linux.
5+
6+
For Linux, we use a Docker container to build the extension. To run the build,
7+
first build that image:
8+
9+
```shell
10+
docker build -t powersync_kotlin_sqlite3mc_build_helper --load src/jni
11+
```
12+
13+
To compile for Windows, we use [llvm-mingw](https://github.com/mstorsjo/llvm-mingw),
14+
which needs to be downloaded.

sqlite3multipleciphers/build.gradle.kts

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -169,54 +169,127 @@ val xCodeInstallation = ClangCompile.resolveXcode(providers)
169169
// Tasks to build the JNI shared library for multiple operating systems.
170170
// Since the JNI sources rarely change, we don't run these tasks on every build. Instead,
171171
// we'll publish these sources as one-off releases when needed, and then reference that URL.
172+
enum class JniTarget {
173+
LINUX_ARM,
174+
LINUX_X64,
175+
MACOS_ARM,
176+
MACOS_X64,
177+
WINDOWS_ARM,
178+
WINDOWS_X64,
179+
}
180+
181+
fun Exec.registerCompileOnHostTask(target: JniTarget, clang: String = "clang", toolchain: String? = null) {
182+
val outputDirectory = layout.buildDirectory.dir("jni-build")
183+
val outputFile = outputDirectory.map {
184+
it.file(when (target) {
185+
JniTarget.LINUX_ARM -> "libsqlite3mc_jni_aarch64.linux.so"
186+
JniTarget.LINUX_X64 -> "libsqlite3mc_jni_x64.linux.so"
187+
JniTarget.MACOS_ARM -> "libsqlite3mc_jni_aarch64.macos.dylib"
188+
JniTarget.MACOS_X64 -> "libsqlite3mc_jni_x64.macos.dylib"
189+
JniTarget.WINDOWS_ARM -> "sqlite3mc_jni_aarch64.dll"
190+
JniTarget.WINDOWS_X64 -> "sqlite3mc_jni_x64.dll"
191+
})
192+
}
193+
outputs.file(outputFile)
194+
195+
dependsOn(unzipSQLiteSources)
196+
val sqlite3McSources = unzipSQLiteSources.map { it.destinationDir }
197+
inputs.dir(sqlite3McSources)
198+
199+
inputs.dir(layout.projectDirectory.dir("src/jni/"))
200+
201+
doFirst {
202+
outputDirectory.get().asFile.mkdirs()
203+
}
204+
205+
if (target == JniTarget.LINUX_X64 || target == JniTarget.LINUX_ARM) {
206+
executable = "/opt/homebrew/bin/docker"
207+
args(
208+
"run",
209+
"-v", "./src:/src",
210+
"-v", "./build:/build",
211+
"powersync_kotlin_sqlite3mc_build_helper",
212+
"clang",
213+
"-fuse-ld=lld"
214+
)
215+
} else {
216+
executable = clang
217+
}
218+
219+
val outputFilePath = outputFile.get().asFile.toRelativeString(project.projectDir)
220+
val sourceRoot = sqlite3McSources.get().toRelativeString(project.projectDir)
221+
val amalgamation = File(sourceRoot, "sqlite3mc_amalgamation.c").path
222+
223+
args(
224+
"-shared",
225+
"-fPIC",
226+
when (target) {
227+
JniTarget.LINUX_ARM -> "--target=aarch64-pc-linux"
228+
JniTarget.LINUX_X64 -> "--target=x86_64-pc-linux"
229+
JniTarget.MACOS_ARM -> "--target=aarch64-apple-macos"
230+
JniTarget.MACOS_X64 -> "--target=x86_64-apple-macos"
231+
JniTarget.WINDOWS_ARM -> "--target=aarch64-w64-mingw32uwp"
232+
JniTarget.WINDOWS_X64 -> "--target=x86_64-w64-mingw32uwp"
233+
},
234+
"-o",
235+
outputFilePath,
236+
"src/jni/sqlite_bindings.cpp",
237+
amalgamation,
238+
"-I",
239+
sourceRoot,
240+
"-I",
241+
"src/jni/headers/common",
242+
"-I",
243+
when (target) {
244+
JniTarget.LINUX_X64, JniTarget.LINUX_ARM -> "src/jni/headers/inc_linux"
245+
JniTarget.MACOS_X64, JniTarget.MACOS_ARM -> "src/jni/headers/inc_mac"
246+
JniTarget.WINDOWS_X64, JniTarget.WINDOWS_ARM -> "src/jni/headers/inc_win"
247+
},
248+
"-O3",
249+
*ClangCompile.sqlite3ClangOptions,
250+
)
251+
252+
toolchain?.let { args.add(it) }
253+
}
254+
172255
fun registerCompileMacOsHostTask(arm: Boolean): TaskProvider<Exec> {
173-
val architectureName = if (arm) { "aarch64" } else { "x64" }
256+
val architecture = if (arm) JniTarget.MACOS_ARM else JniTarget.MACOS_X64
174257

175-
return tasks.register<Exec>("jniCompileMacos$architectureName") {
258+
return tasks.register<Exec>("jniCompile${architecture.name}") {
176259
val xcode = Path(xCodeInstallation.get())
177260
val toolchain =
178261
xcode.resolve("Toolchains/XcodeDefault.xctoolchain/usr/bin").absolutePathString()
262+
registerCompileOnHostTask(architecture, toolchain = toolchain)
263+
}
264+
}
179265

180-
val outputDirectory = layout.buildDirectory.dir("jni-build")
181-
val outputFile = outputDirectory.map {
182-
it.file("libsqlite3mc_jni_$architectureName.macos.dylib")
183-
}
184-
outputs.file(outputFile)
185-
186-
dependsOn(unzipSQLiteSources)
187-
val sqlite3McSources = unzipSQLiteSources.map { it.destinationDir }
188-
inputs.dir(sqlite3McSources)
266+
fun registerCompileWindowsOnMacOsTask(arm: Boolean): TaskProvider<Exec> {
267+
val architecture = if (arm) JniTarget.WINDOWS_ARM else JniTarget.WINDOWS_X64
189268

190-
inputs.dir(layout.projectDirectory.dir("src/jni/"))
269+
return tasks.register<Exec>("jniCompile${architecture.name}") {
270+
registerCompileOnHostTask(architecture, clang = "/Users/simon/Downloads/llvm-mingw-20251104-ucrt-macos-universal/bin/clang")
271+
}
272+
}
191273

192-
doFirst {
193-
outputDirectory.get().asFile.mkdirs()
194-
}
274+
fun registerCompileLinuxOnMacOsTask(arm: Boolean): TaskProvider<Exec> {
275+
val architecture = if (arm) JniTarget.LINUX_ARM else JniTarget.LINUX_X64
195276

196-
executable = "clang"
197-
args(
198-
"-B$toolchain",
199-
"-dynamiclib",
200-
"-fPIC",
201-
if (arm) "--target=aarch64-apple-macos" else "--target=x86_64-apple-macos",
202-
"-o",
203-
outputFile.get().asFile.path,
204-
"src/jni/sqlite_bindings.cpp",
205-
File(sqlite3McSources.get(), "sqlite3mc_amalgamation.c").path,
206-
"-I",
207-
sqlite3McSources.get().path,
208-
"-I",
209-
"src/jni/headers/inc_mac",
210-
"-O3",
211-
*ClangCompile.sqlite3ClangOptions,
212-
)
277+
return tasks.register<Exec>("jniCompile${architecture.name}") {
278+
registerCompileOnHostTask(architecture)
213279
}
214280
}
215281

282+
val linuxArm64 = registerCompileLinuxOnMacOsTask(true)
283+
val linuxX64 = registerCompileLinuxOnMacOsTask(false)
284+
216285
val macosArm64 = registerCompileMacOsHostTask(true)
217286
val macosX64 = registerCompileMacOsHostTask(false)
218287

288+
val windowsArm64 = registerCompileWindowsOnMacOsTask(true)
289+
val windowsX64 = registerCompileWindowsOnMacOsTask(false)
290+
219291
tasks.register("jniCompile") {
220-
dependsOn(macosArm64)
221-
dependsOn(macosX64)
292+
dependsOn(linuxX64, linuxArm64)
293+
dependsOn(macosX64, macosArm64)
294+
dependsOn(windowsX64, macosArm64)
222295
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM ubuntu:latest
2+
3+
RUN apt update && apt install -y gcc-x86-64-linux-gnu gcc-aarch64-linux-gnu g++-x86-64-linux-gnu g++-aarch64-linux-gnu clang lld

0 commit comments

Comments
 (0)