|
| 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 | +} |
0 commit comments