|
| 1 | +import com.powersync.plugins.utils.powersyncTargets |
| 2 | +import de.undercouch.gradle.tasks.download.Download |
| 3 | +import org.gradle.api.tasks.testing.logging.TestExceptionFormat |
| 4 | +import org.gradle.internal.os.OperatingSystem |
| 5 | +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget |
| 6 | +import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest |
| 7 | +import org.jetbrains.kotlin.gradle.tasks.KotlinTest |
| 8 | +import org.jetbrains.kotlin.konan.target.Family |
| 9 | +import java.nio.file.Path |
| 10 | +import kotlin.io.path.createDirectories |
| 11 | +import kotlin.io.path.writeText |
| 12 | + |
| 13 | +plugins { |
| 14 | + alias(libs.plugins.kotlinMultiplatform) |
| 15 | + alias(libs.plugins.kotlinSerialization) |
| 16 | + alias(libs.plugins.android.library) |
| 17 | + alias(libs.plugins.mavenPublishPlugin) |
| 18 | + alias(libs.plugins.downloadPlugin) |
| 19 | + alias(libs.plugins.kotlinter) |
| 20 | + id("com.powersync.plugins.sonatype") |
| 21 | + id("com.powersync.plugins.sharedbuild") |
| 22 | + alias(libs.plugins.mokkery) |
| 23 | + alias(libs.plugins.kotlin.atomicfu) |
| 24 | + id("dokka-convention") |
| 25 | +} |
| 26 | + |
| 27 | +val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop") |
| 28 | +val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) { |
| 29 | + description = "Download PowerSync core extensions for JVM builds and releases" |
| 30 | + |
| 31 | + val coreVersion = |
| 32 | + libs.versions.powersync.core |
| 33 | + .get() |
| 34 | + val linux_aarch64 = |
| 35 | + "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so" |
| 36 | + val linux_x64 = |
| 37 | + "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so" |
| 38 | + val macos_aarch64 = |
| 39 | + "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib" |
| 40 | + val macos_x64 = |
| 41 | + "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib" |
| 42 | + val windows_x64 = |
| 43 | + "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll" |
| 44 | + |
| 45 | + val includeAllPlatformsForJvmBuild = |
| 46 | + project.findProperty("powersync.binaries.allPlatforms") == "true" |
| 47 | + val os = OperatingSystem.current() |
| 48 | + |
| 49 | + // The jar we're releasing for JVM clients needs to include the core extension. For local tests, it's enough to only |
| 50 | + // download the extension for the OS running the build. For releases, we want to include them all. |
| 51 | + // We're not compiling native code for JVM builds here (we're doing that for Android only), so we just have to |
| 52 | + // fetch prebuilt binaries from the powersync-sqlite-core repository. |
| 53 | + if (includeAllPlatformsForJvmBuild) { |
| 54 | + src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64)) |
| 55 | + } else { |
| 56 | + val (aarch64, x64) = |
| 57 | + when { |
| 58 | + os.isLinux -> linux_aarch64 to linux_x64 |
| 59 | + os.isMacOsX -> macos_aarch64 to macos_x64 |
| 60 | + os.isWindows -> null to windows_x64 |
| 61 | + else -> error("Unknown operating system: $os") |
| 62 | + } |
| 63 | + val arch = System.getProperty("os.arch") |
| 64 | + src( |
| 65 | + when (arch) { |
| 66 | + "aarch64" -> listOfNotNull(aarch64) |
| 67 | + "amd64", "x86_64" -> listOfNotNull(x64) |
| 68 | + else -> error("Unsupported architecture: $arch") |
| 69 | + }, |
| 70 | + ) |
| 71 | + } |
| 72 | + dest(binariesFolder.map { it.dir("powersync") }) |
| 73 | + onlyIfModified(true) |
| 74 | +} |
| 75 | + |
| 76 | +val generateVersionConstant by tasks.registering { |
| 77 | + val target = project.layout.buildDirectory.dir("generated/constants") |
| 78 | + val packageName = "com.powersync.build" |
| 79 | + |
| 80 | + outputs.dir(target) |
| 81 | + val currentVersion = version.toString() |
| 82 | + |
| 83 | + doLast { |
| 84 | + val dir = target.get().asFile |
| 85 | + dir.mkdir() |
| 86 | + val rootPath = dir.toPath() |
| 87 | + |
| 88 | + val source = |
| 89 | + """ |
| 90 | + package $packageName |
| 91 | + |
| 92 | + internal const val LIBRARY_VERSION: String = "$currentVersion" |
| 93 | +
|
| 94 | + """.trimIndent() |
| 95 | + |
| 96 | + val packageRoot = packageName.split('.').fold(rootPath, Path::resolve) |
| 97 | + packageRoot.createDirectories() |
| 98 | + |
| 99 | + packageRoot.resolve("BuildConstants.kt").writeText(source) |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +kotlin { |
| 104 | + powersyncTargets() |
| 105 | + |
| 106 | + targets.withType<KotlinNativeTarget> { |
| 107 | + compilations.named("main") { |
| 108 | + compileTaskProvider { |
| 109 | + compilerOptions.freeCompilerArgs.add("-Xexport-kdoc") |
| 110 | + } |
| 111 | + |
| 112 | + if (target.konanTarget.family == Family.WATCHOS) { |
| 113 | + // We're linking the core extension statically, which means that we need a cinterop |
| 114 | + // to call powersync_init_static |
| 115 | + cinterops.create("powersync_static") { |
| 116 | + packageName("com.powersync.static") |
| 117 | + headers(file("src/watchosMain/powersync_static.h")) |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + cinterops.create("sqlite3") { |
| 122 | + // We're not linking SQLite here (to allow using this package with e.g. SQLCipher or |
| 123 | + // SQLite3MultipleCiphers), :core depends on this package and is responsible for bundling SQLite. |
| 124 | + packageName("com.powersync.internal.sqlite3") |
| 125 | + includeDirs.allHeaders("src/nativeMain/interop/") |
| 126 | + definitionFile.set(project.file("src/nativeMain/interop/sqlite3.def")) |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + explicitApi() |
| 132 | + |
| 133 | + applyDefaultHierarchyTemplate() |
| 134 | + sourceSets { |
| 135 | + all { |
| 136 | + languageSettings { |
| 137 | + optIn("kotlinx.cinterop.ExperimentalForeignApi") |
| 138 | + optIn("kotlin.time.ExperimentalTime") |
| 139 | + optIn("kotlin.experimental.ExperimentalObjCRefinement") |
| 140 | + } |
| 141 | + } |
| 142 | + |
| 143 | + val commonIntegrationTest by creating { |
| 144 | + dependsOn(commonTest.get()) |
| 145 | + } |
| 146 | + |
| 147 | + val commonJava by creating { |
| 148 | + dependsOn(commonMain.get()) |
| 149 | + } |
| 150 | + |
| 151 | + commonMain.configure { |
| 152 | + kotlin { |
| 153 | + srcDir(generateVersionConstant) |
| 154 | + } |
| 155 | + |
| 156 | + dependencies { |
| 157 | + api(libs.androidx.sqlite.sqlite) |
| 158 | + |
| 159 | + implementation(libs.uuid) |
| 160 | + implementation(libs.kotlin.stdlib) |
| 161 | + implementation(libs.ktor.client.contentnegotiation) |
| 162 | + implementation(libs.ktor.serialization.json) |
| 163 | + implementation(libs.kotlinx.io) |
| 164 | + implementation(libs.kotlinx.coroutines.core) |
| 165 | + implementation(libs.kotlinx.datetime) |
| 166 | + implementation(libs.stately.concurrency) |
| 167 | + implementation(libs.configuration.annotations) |
| 168 | + api(libs.ktor.client.core) |
| 169 | + api(libs.kermit) |
| 170 | + } |
| 171 | + } |
| 172 | + |
| 173 | + androidMain { |
| 174 | + dependsOn(commonJava) |
| 175 | + dependencies { |
| 176 | + api(libs.powersync.sqlite.core.android) |
| 177 | + } |
| 178 | + } |
| 179 | + |
| 180 | + jvmMain { |
| 181 | + dependsOn(commonJava) |
| 182 | + } |
| 183 | + |
| 184 | + // Common apple targets where we link the core extension dynamically |
| 185 | + val appleNonWatchOsMain by creating { |
| 186 | + dependsOn(appleMain.get()) |
| 187 | + } |
| 188 | + |
| 189 | + macosMain.orNull?.dependsOn(appleNonWatchOsMain) |
| 190 | + iosMain.orNull?.dependsOn(appleNonWatchOsMain) |
| 191 | + tvosMain.orNull?.dependsOn(appleNonWatchOsMain) |
| 192 | + |
| 193 | + commonTest.dependencies { |
| 194 | + implementation(projects.internal.testutils) |
| 195 | + implementation(libs.kotlin.test) |
| 196 | + } |
| 197 | + |
| 198 | + // We're putting the native libraries into our JAR, so integration tests for the JVM can run as part of the unit |
| 199 | + // tests. |
| 200 | + jvmTest { |
| 201 | + dependsOn(commonIntegrationTest) |
| 202 | + } |
| 203 | + |
| 204 | + // We have special setup in this build configuration to make these tests link the PowerSync extension, so they |
| 205 | + // can run integration tests along with the executable for unit testing. |
| 206 | + appleTest.orNull?.dependsOn(commonIntegrationTest) |
| 207 | + } |
| 208 | +} |
| 209 | + |
| 210 | +android { |
| 211 | + compileOptions { |
| 212 | + targetCompatibility = JavaVersion.VERSION_17 |
| 213 | + } |
| 214 | + |
| 215 | + buildFeatures { |
| 216 | + buildConfig = true |
| 217 | + } |
| 218 | + |
| 219 | + buildTypes { |
| 220 | + release { |
| 221 | + buildConfigField("boolean", "DEBUG", "false") |
| 222 | + } |
| 223 | + debug { |
| 224 | + buildConfigField("boolean", "DEBUG", "true") |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + namespace = "com.powersync" |
| 229 | + compileSdk = |
| 230 | + libs.versions.android.compileSdk |
| 231 | + .get() |
| 232 | + .toInt() |
| 233 | + defaultConfig { |
| 234 | + minSdk = |
| 235 | + libs.versions.android.minSdk |
| 236 | + .get() |
| 237 | + .toInt() |
| 238 | + consumerProguardFiles("proguard-rules.pro") |
| 239 | + } |
| 240 | + |
| 241 | + ndkVersion = "27.1.12297006" |
| 242 | +} |
| 243 | + |
| 244 | +tasks.named<ProcessResources>(kotlin.jvm().compilations["main"].processResourcesTaskName) { |
| 245 | + from(downloadPowersyncDesktopBinaries) |
| 246 | +} |
| 247 | + |
| 248 | +// We want to build with recent JDKs, but need to make sure we support Java 8. https://jakewharton.com/build-on-latest-java-test-through-lowest-java/ |
| 249 | +val testWithJava8 by tasks.registering(KotlinJvmTest::class) { |
| 250 | + javaLauncher = |
| 251 | + javaToolchains.launcherFor { |
| 252 | + languageVersion = JavaLanguageVersion.of(8) |
| 253 | + } |
| 254 | + |
| 255 | + description = "Run tests with Java 8" |
| 256 | + group = LifecycleBasePlugin.VERIFICATION_GROUP |
| 257 | + |
| 258 | + // Copy inputs from the normal test task |
| 259 | + val testTask = tasks.getByName("jvmTest") as KotlinJvmTest |
| 260 | + classpath = testTask.classpath |
| 261 | + testClassesDirs = testTask.testClassesDirs |
| 262 | +} |
| 263 | +tasks.named("check").configure { dependsOn(testWithJava8) } |
| 264 | + |
| 265 | +tasks.withType<KotlinTest> { |
| 266 | + testLogging { |
| 267 | + events("PASSED", "FAILED", "SKIPPED") |
| 268 | + exceptionFormat = TestExceptionFormat.FULL |
| 269 | + showCauses = true |
| 270 | + showStandardStreams = true |
| 271 | + showStackTraces = true |
| 272 | + } |
| 273 | +} |
| 274 | + |
| 275 | +dokka { |
| 276 | + moduleName.set("PowerSync Common") |
| 277 | +} |
0 commit comments