Skip to content

Commit a121436

Browse files
authored
Merge pull request #129 from powersync-ja/testing
Simplify JVM build, start with integration tests
2 parents bbe0ecc + 548dec6 commit a121436

File tree

11 files changed

+254
-252
lines changed

11 files changed

+254
-252
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
matrix:
1717
include:
1818
- os: macos-latest
19-
targets: iosSimulatorArm64Test jvmTest
19+
targets: iosSimulatorArm64Test jvmTest lintKotlin
2020
- os: ubuntu-latest
2121
targets: testDebugUnitTest testReleaseUnitTest jvmTest
2222
- os: windows-latest

core/build.gradle.kts

Lines changed: 88 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1-
import app.cash.sqldelight.core.capitalize
1+
import com.android.build.gradle.internal.tasks.factory.dependsOn
22
import com.powersync.plugins.sonatype.setupGithubRepository
33
import de.undercouch.gradle.tasks.download.Download
44
import org.gradle.internal.os.OperatingSystem
55
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
66
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
77
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
8+
import org.jetbrains.kotlin.gradle.plugin.mpp.TestExecutable
89
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
9-
import java.util.*
10+
import org.jetbrains.kotlin.konan.target.Family
1011

1112
plugins {
1213
alias(libs.plugins.kotlinMultiplatform)
@@ -76,6 +77,56 @@ val buildCInteropDef by tasks.registering {
7677
outputs.files(defFile)
7778
}
7879

80+
val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop")
81+
val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) {
82+
val coreVersion = libs.versions.powersync.core.get()
83+
val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so"
84+
val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so"
85+
val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib"
86+
val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib"
87+
val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll"
88+
89+
if (binariesAreProvided) {
90+
src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64))
91+
} else {
92+
val (aarch64, x64) = when {
93+
os.isLinux -> linux_aarch64 to linux_x64
94+
os.isMacOsX -> macos_aarch64 to macos_x64
95+
os.isWindows -> null to windows_x64
96+
else -> error("Unknown operating system: $os")
97+
}
98+
val arch = System.getProperty("os.arch")
99+
src(when {
100+
crossArch -> listOfNotNull(aarch64, x64)
101+
arch == "aarch64" -> listOfNotNull(aarch64)
102+
arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64)
103+
else -> error("Unsupported architecture: $arch")
104+
})
105+
}
106+
dest(binariesFolder.map { it.dir("powersync") })
107+
onlyIfModified(true)
108+
}
109+
110+
val downloadPowersyncFramework by tasks.registering(Download::class) {
111+
val coreVersion = libs.versions.powersync.core.get()
112+
val framework = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync-sqlite-core.xcframework.zip"
113+
114+
src(framework)
115+
dest(binariesFolder.map { it.file("framework/powersync-sqlite-core.xcframework.zip") })
116+
onlyIfModified(true)
117+
}
118+
119+
val unzipPowersyncFramework by tasks.registering(Copy::class) {
120+
dependsOn(downloadPowersyncFramework)
121+
122+
from(
123+
zipTree(downloadPowersyncFramework.get().dest).matching {
124+
include("powersync-sqlite-core.xcframework/**")
125+
},
126+
)
127+
into(binariesFolder.map { it.dir("framework") })
128+
}
129+
79130
kotlin {
80131
androidTarget {
81132
publishLibraryVariants("release", "debug")
@@ -114,17 +165,43 @@ kotlin {
114165
}
115166
cinterops.create("powersync-sqlite-core")
116167
}
168+
169+
if (konanTarget.family == Family.IOS && konanTarget.name.contains("simulator")) {
170+
binaries.withType<TestExecutable>().configureEach {
171+
linkTaskProvider.dependsOn(unzipPowersyncFramework)
172+
linkerOpts("-framework", "powersync-sqlite-core")
173+
val frameworkRoot = binariesFolder.map { it.dir("framework/powersync-sqlite-core.xcframework/ios-arm64_x86_64-simulator") }.get().asFile.path
174+
175+
linkerOpts("-F", frameworkRoot)
176+
linkerOpts("-rpath", frameworkRoot)
177+
}
178+
}
179+
/*
180+
If we ever need macOS support:
181+
{
182+
binaries.withType<TestExecutable>().configureEach {
183+
linkTaskProvider.dependsOn(downloadPowersyncDesktopBinaries)
184+
linkerOpts("-lpowersync")
185+
linkerOpts("-L", binariesFolder.map { it.dir("powersync") }.get().asFile.path)
186+
}
187+
}
188+
*/
117189
}
118190

119191
explicitApi()
120192

193+
applyDefaultHierarchyTemplate()
121194
sourceSets {
122195
all {
123196
languageSettings {
124197
optIn("kotlinx.cinterop.ExperimentalForeignApi")
125198
}
126199
}
127200

201+
val commonIntegrationTest by creating {
202+
dependsOn(commonTest.get())
203+
}
204+
128205
commonMain.dependencies {
129206
implementation(libs.uuid)
130207
implementation(libs.kotlin.stdlib)
@@ -156,8 +233,16 @@ kotlin {
156233
commonTest.dependencies {
157234
implementation(libs.kotlin.test)
158235
implementation(libs.test.coroutines)
236+
implementation(libs.test.turbine)
159237
implementation(libs.kermit.test)
160238
}
239+
240+
// We're putting the native libraries into our JAR, so integration tests for the JVM can run as part of the unit
241+
// tests.
242+
jvmTest.get().dependsOn(commonIntegrationTest)
243+
244+
// We're linking the xcframework for the simulator tests, so they can use integration tests too
245+
iosSimulatorArm64Test.orNull?.dependsOn(commonIntegrationTest)
161246
}
162247
}
163248

@@ -212,146 +297,13 @@ android {
212297
val os = OperatingSystem.current()
213298
val binariesAreProvided = project.findProperty("powersync.binaries.provided") == "true"
214299
val crossArch = project.findProperty("powersync.binaries.cross-arch") == "true"
215-
val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop")
216300

217301
if (binariesAreProvided && crossArch) {
218302
error("powersync.binaries.provided and powersync.binaries.cross-arch must not be both defined.")
219303
}
220304

221-
val getBinaries = if (binariesAreProvided) {
222-
// Binaries for all OS must be provided (manually or by the CI) in binaries/desktop
223-
224-
val verifyPowersyncBinaries = tasks.register("verifyPowersyncBinaries") {
225-
val directory = projectDir.resolve("binaries/desktop")
226-
val binaries = listOf(
227-
directory.resolve("libpowersync-sqlite_aarch64.so"),
228-
directory.resolve("libpowersync-sqlite_x64.so"),
229-
directory.resolve("libpowersync-sqlite_aarch64.dylib"),
230-
directory.resolve("libpowersync-sqlite_x64.dylib"),
231-
directory.resolve("powersync-sqlite_x64.dll"),
232-
)
233-
doLast {
234-
binaries.forEach {
235-
if (!it.exists()) error("File $it does not exist")
236-
if (!it.isFile) error("File $it is not a regular file")
237-
}
238-
}
239-
outputs.files(*binaries.toTypedArray())
240-
}
241-
verifyPowersyncBinaries
242-
} else {
243-
// Building locally for the current OS
244-
245-
val localProperties = Properties()
246-
val localPropertiesFile = rootProject.file("local.properties")
247-
if (localPropertiesFile.exists()) {
248-
localPropertiesFile.inputStream().use { localProperties.load(it) }
249-
}
250-
val cmakeExecutable = localProperties.getProperty("cmake.path") ?: "cmake"
251-
252-
fun registerCMakeTasks(
253-
suffix: String,
254-
vararg defines: String,
255-
): TaskProvider<Exec> {
256-
val cmakeConfigure = tasks.register<Exec>("cmakeJvmConfigure${suffix.capitalize()}") {
257-
dependsOn(unzipSQLiteSources)
258-
group = "cmake"
259-
workingDir = layout.buildDirectory.dir("cmake/$suffix").get().asFile
260-
inputs.files(
261-
"src/jvmMain/cpp",
262-
"src/jvmNative/cpp",
263-
sqliteSrcFolder,
264-
)
265-
outputs.dir(workingDir)
266-
executable = cmakeExecutable
267-
args(listOf(file("src/jvmMain/cpp/CMakeLists.txt").absolutePath, "-DSUFFIX=$suffix", "-DCMAKE_BUILD_TYPE=Release") + defines.map { "-D$it" })
268-
doFirst {
269-
workingDir.mkdirs()
270-
}
271-
}
272-
273-
val cmakeBuild = tasks.register<Exec>("cmakeJvmBuild${suffix.capitalize()}") {
274-
dependsOn(cmakeConfigure)
275-
group = "cmake"
276-
workingDir = layout.buildDirectory.dir("cmake/$suffix").get().asFile
277-
inputs.files(
278-
"src/jvmMain/cpp",
279-
"src/jvmNative/cpp",
280-
sqliteSrcFolder,
281-
workingDir,
282-
)
283-
outputs.dir(workingDir.resolve(if (os.isWindows) "output/Release" else "output"))
284-
executable = cmakeExecutable
285-
args("--build", ".", "--config", "Release")
286-
}
287-
288-
return cmakeBuild
289-
}
290-
291-
val (aarch64, x64) = when {
292-
os.isMacOsX -> {
293-
val aarch64 = registerCMakeTasks("aarch64", "CMAKE_OSX_ARCHITECTURES=arm64")
294-
val x64 = registerCMakeTasks("x64", "CMAKE_OSX_ARCHITECTURES=x86_64")
295-
aarch64 to x64
296-
}
297-
os.isLinux -> {
298-
val aarch64 = registerCMakeTasks("aarch64", "CMAKE_C_COMPILER=aarch64-linux-gnu-gcc", "CMAKE_CXX_COMPILER=aarch64-linux-gnu-g++")
299-
val x64 = registerCMakeTasks("x64", "CMAKE_C_COMPILER=x86_64-linux-gnu-gcc", "CMAKE_CXX_COMPILER=x86_64-linux-gnu-g++")
300-
aarch64 to x64
301-
}
302-
os.isWindows -> {
303-
val x64 = registerCMakeTasks("x64")
304-
null to x64
305-
}
306-
else -> error("Unknown operating system: $os")
307-
}
308-
309-
val arch = System.getProperty("os.arch")
310-
val cmakeJvmBuilds = when {
311-
crossArch -> listOfNotNull(aarch64, x64)
312-
arch == "aarch64" -> listOfNotNull(aarch64)
313-
arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64)
314-
else -> error("Unsupported architecture: $arch")
315-
}
316-
317-
tasks.register<Copy>("cmakeJvmBuild") {
318-
dependsOn(cmakeJvmBuilds)
319-
group = "cmake"
320-
from(cmakeJvmBuilds)
321-
into(binariesFolder.map { it.dir("sqlite") })
322-
}
323-
}
324-
325-
val downloadPowersyncDesktopBinaries = tasks.register<Download>("downloadPowersyncDesktopBinaries") {
326-
val coreVersion = libs.versions.powersync.core.get()
327-
val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so"
328-
val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so"
329-
val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib"
330-
val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib"
331-
val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll"
332-
if (binariesAreProvided) {
333-
src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64))
334-
} else {
335-
val (aarch64, x64) = when {
336-
os.isLinux -> linux_aarch64 to linux_x64
337-
os.isMacOsX -> macos_aarch64 to macos_x64
338-
os.isWindows -> null to windows_x64
339-
else -> error("Unknown operating system: $os")
340-
}
341-
val arch = System.getProperty("os.arch")
342-
src(when {
343-
crossArch -> listOfNotNull(aarch64, x64)
344-
arch == "aarch64" -> listOfNotNull(aarch64)
345-
arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64)
346-
else -> error("Unsupported architecture: $arch")
347-
})
348-
}
349-
dest(binariesFolder.map { it.dir("powersync") })
350-
onlyIfModified(true)
351-
}
352-
353305
tasks.named<ProcessResources>(kotlin.jvm().compilations["main"].processResourcesTaskName) {
354-
from(getBinaries, downloadPowersyncDesktopBinaries)
306+
from(downloadPowersyncDesktopBinaries)
355307
}
356308

357309
// 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/

0 commit comments

Comments
 (0)