diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e0d53d8 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,3 @@ +.gradle +.idea +build diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bcaca02 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +gradlew text eol=lf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..630295f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,119 @@ +# Define project root inside build container +ARG SOURCE_ROOT=/usr/src/postgres-native-sqldelight-driver + +# Define builder distro: +# "ubuntu" (ubuntu:noble, default) +# "alpine" (alpine:3) +ARG BUILDER_DISTRO=ubuntu + +# Define runtime distro: +# "ubuntu" (ubuntu:noble, default) +# "alpine" (alpine:3) +ARG RUNTIME_DISTRO=ubuntu + +################################################################################ +# STAGE 1: setup builders +################################################################################ + +# Kotlin/Native prebuilt compiler is available for x86_64 Linux only, so Use "linux/amd64" platform for base builder image +FROM --platform=linux/amd64 ubuntu:noble AS ubuntu-builder-base +RUN \ + --mount=type=cache,sharing=locked,target=/var/lib/apt/lists \ + --mount=type=cache,sharing=locked,target=/var/cache/apt \ + apt-get update && \ + apt install -y \ + git \ + cmake \ + pkgconf \ + build-essential \ + crossbuild-essential-arm64 \ + openjdk-17-jdk-headless + +FROM ubuntu-builder-base AS ubuntu-builder-base-amd64 +RUN \ + --mount=type=cache,sharing=locked,target=/var/lib/apt/lists \ + --mount=type=cache,sharing=locked,target=/var/cache/apt \ + apt-get install --no-install-recommends -y libpq-dev:amd64 + +FROM ubuntu-builder-base AS ubuntu-builder-base-arm64 +ADD ubuntu-arm64-sources.list /etc/apt/sources.list.d/ +RUN \ + --mount=type=cache,sharing=locked,target=/var/lib/apt/lists \ + --mount=type=cache,sharing=locked,target=/var/cache/apt \ + apt-get update && \ + dpkg --add-architecture arm64 && \ + apt-get install --no-install-recommends -y libpq-dev:arm64 + +FROM ubuntu-builder-base-${TARGETARCH} AS ubuntu-builder-base + +FROM --platform=linux/amd64 alpine:3 AS alpine-builder-base-amd64 +# TODO +RUN echo "Not implemented: dependencies for building under alpine should be resolved, use ubuntu builder instead" && exit 1 +RUN apk update && apk add --no-cache libstdc++ libpq-dev openjdk17-jdk + +FROM alpine-builder-base-${TARGETARCH} AS alpine-builder-base + +FROM ${BUILDER_DISTRO}-builder-base AS builder + +################################################################################ +# STAGE 2: add project sources and configure gradle build +################################################################################ + +FROM builder AS sources +ARG SOURCE_ROOT +WORKDIR ${SOURCE_ROOT} +ADD . . +RUN chmod +x ./gradlew + +FROM sources AS gradlew +ENV GRADLE_OPTS="-Dorg.gradle.daemon=false" +RUN \ + --mount=type=cache,sharing=private,target=/root/.konan \ + --mount=type=cache,sharing=private,target=/root/.gradle \ + ./gradlew + +################################################################################ +# STAGE 3: compile binaries +################################################################################ + +FROM gradlew AS test-binaries-amd64 +ARG SOURCE_ROOT +RUN ./gradlew :testing:linuxX64TestBinaries --info --stacktrace + +FROM gradlew AS test-binaries-arm64 +ARG SOURCE_ROOT +RUN ./gradlew :testing:linuxArm64TestBinaries --info --stacktrace + +FROM test-binaries-${TARGETARCH} AS test-binaries + +################################################################################ +# STAGE 4: copy binaries & prepare runtime +################################################################################ + +# This target doesn't use "--platform" argument, so the target image will be multiarch +FROM ubuntu:noble AS ubuntu-test-runtime-base +RUN apt-get update && apt-get install --no-install-recommends -y libpq-dev file + +FROM alpine:3 AS alpine-test-runtime-base +RUN \ + apk update && \ + apk add --no-cache \ + gcompat g++ libpq-dev file + +FROM ${RUNTIME_DISTRO}-test-runtime-base AS test-runtime-base +WORKDIR /usr/local/bin + +FROM --platform=linux/amd64 test-runtime-base AS test-runtime-amd64 +ENV KOTLIN_NATIVE_TARGET=linuxX64 + +FROM --platform=linux/arm64 test-runtime-base AS test-runtime-arm64 +ENV KOTLIN_NATIVE_TARGET=linuxArm64 + +FROM test-runtime-${TARGETARCH} AS test-runtime +ARG SOURCE_ROOT +COPY --from=test-binaries ${SOURCE_ROOT}/testing/build/bin/${KOTLIN_NATIVE_TARGET}/debugTest/test.kexe test.kexe +ENTRYPOINT [ "sh", "-c" ] +CMD [ "./test.kexe" ] + +# Default target +FROM test-runtime diff --git a/README.md b/README.md index afa3c89..f4ae92c 100644 --- a/README.md +++ b/README.md @@ -114,12 +114,54 @@ For compilers to find libpq you may need to set: For installation using homebrew, the default path is already added. ### Testing - +#### Using local machine If you install libpq with homebrew, it will install the platform-specific artifact. To test other platforms, eg. linux x64 on macOS, you need to install the platform-specific libpq of linux x64 too. -To start the postgres instance, you can use docker: ```sh docker run -e POSTGRES_PASSWORD=password -p 5432:5432 postgres ``` + +#### Using Docker + +To build and test project run this commands: + +```sh +docker compose up ubuntu-test-runtime +docker compose up alpine-test-runtime +``` + +... and the same for [prebuilt images](https://hub.docker.com/r/myshkouski/kotlin-native-postgres-driver-testing/tags), on either `linux/amd64` or `linux/arm64` hosts. +```sh +docker compose up ubuntu-test-runtime-prebuilt +docker compose up alpine-test-runtime-prebuilt +``` + +The output of `debugTest` binary should be: +``` +alpine-test-runtime-1 | [==========] Running 5 tests from 1 test cases. +alpine-test-runtime-1 | [----------] Global test environment set-up. +alpine-test-runtime-1 | [----------] 5 tests from app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest +alpine-test-runtime-1 | [ RUN ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.simpleTest +alpine-test-runtime-1 | [ OK ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.simpleTest (52 ms) +alpine-test-runtime-1 | [ RUN ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.wrongCredentials +alpine-test-runtime-1 | [ OK ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.wrongCredentials (5015 ms) +alpine-test-runtime-1 | [ RUN ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.copyTest +alpine-test-runtime-1 | [ OK ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.copyTest (16 ms) +alpine-test-runtime-1 | [ RUN ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.remoteListenerTest +alpine-test-runtime-1 | [ OK ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.remoteListenerTest (6016 ms) +alpine-test-runtime-1 | [ RUN ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.localListenerTest +alpine-test-runtime-1 | [ OK ] app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest.localListenerTest (7 ms) +alpine-test-runtime-1 | [----------] 5 tests from app.softwork.sqldelight.postgresdriver.PostgresNativeDriverTest (11109 ms total) +alpine-test-runtime-1 | +alpine-test-runtime-1 | [----------] Global test environment tear-down +alpine-test-runtime-1 | [==========] 5 tests from 1 test cases ran. (11109 ms total) +alpine-test-runtime-1 | [ PASSED ] 5 tests. +alpine-test-runtime-1 exited with code 0 +``` + +#### TODO: +- [ ] add container for zero-config compiling dependent projects +- [ ] setup alpine-based build container + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index b8e4a83..ff9fb68 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,17 +1,7 @@ plugins { - id("io.github.gradle-nexus.publish-plugin") id("org.jetbrains.dokka") } tasks.dokkaHtmlMultiModule { includes.from("README.md") } - -nexusPublishing { - this.repositories { - sonatype { - nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) - snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) - } - } -} diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..12677f1 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,73 @@ +version: "3.8" + +x-services: + test-runtime: &test-runtime + environment: + POSTGRES_HOST: postgres + depends_on: + postgres: + condition: service_healthy + test-runtime-info-cmd: &test-runtime-info-cmd + command: [ "uname -a", "printenv; cat /etc/*release*; ldconfig -p | grep libpq.so; ls -lh; file *;" ] + +services: + postgres: + image: postgres:16-alpine + command: ["postgres", "-c", "log_statement=all", "-c", "log_destination=stderr"] + environment: + POSTGRES_PASSWORD: password + ports: + - "127.0.0.1:5432:5432" + healthcheck: + test: "pg_isready -U postgres" + interval: 10s + timeout: 3s + retries: 3 + ubuntu-test-runtime: + <<: *test-runtime + build: + args: + RUNTIME_DISTRO: ubuntu + tags: + - myshkouski/kotlin-native-postgres-driver-testing:latest + - myshkouski/kotlin-native-postgres-driver-testing:ubuntu + alpine-test-runtime: + <<: *test-runtime + build: + args: + RUNTIME_DISTRO: alpine + tags: + - myshkouski/kotlin-native-postgres-driver-testing:alpine + ubuntu-test-runtime-info: + extends: + service: ubuntu-test-runtime + <<: *test-runtime-info-cmd + alpine-test-runtime-info: + extends: + service: alpine-test-runtime + <<: *test-runtime-info-cmd + ubuntu-test-runtime-prebuilt: + <<: *test-runtime + image: myshkouski/kotlin-native-postgres-driver-testing + alpine-test-runtime-prebuilt: + <<: *test-runtime + image: myshkouski/kotlin-native-postgres-driver-testing:alpine + ubuntu-test-runtime-prebuilt-info: + extends: + service: ubuntu-test-runtime-prebuilt + <<: *test-runtime-info-cmd + alpine-test-runtime-prebuilt-info: + extends: + service: alpine-test-runtime-prebuilt + <<: *test-runtime-info-cmd + ubuntu-sources: + build: + target: gradlew + args: + BUILDER_DISTRO: ubuntu +# TODO: not implemented, see Dockerfile for details +# alpine-sources: +# build: +# target: gradlew +# args: +# BUILDER_DISTRO: alpine diff --git a/gradle.properties b/gradle.properties index 35f98a5..e79ee8e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,5 @@ org.gradle.jvmargs=-Xmx2048m org.gradle.configuration-cache=true org.gradle.configureondemand=true -group=app.softwork +group=io.github.myshkouski.sqldelight +version=0.0.12 diff --git a/gradle/build-logic/src/main/kotlin/publish.gradle.kts b/gradle/build-logic/src/main/kotlin/publish.gradle.kts index cf91166..368ea52 100644 --- a/gradle/build-logic/src/main/kotlin/publish.gradle.kts +++ b/gradle/build-logic/src/main/kotlin/publish.gradle.kts @@ -10,6 +10,9 @@ plugins { val emptyJar by tasks.registering(Jar::class) { } +val Project.isSnapshotVersion: Boolean + get() = version.toString().endsWith("SNAPSHOT") + publishing { publications.configureEach { this as MavenPublication @@ -34,23 +37,35 @@ publishing { name.set("Philip Wedemann") email.set("mybztg+mavencentral@icloud.com") } + developer { + id.set("Myshkouski") + name.set("Alexei Myshkouski") + email.set("alexeimyshkouski@gmail.com") + } } scm { - connection.set("scm:git://github.com/hfhbd/SqlDelightNativePostgres.git") - developerConnection.set("scm:git://github.com/hfhbd/SqlDelightNativePostgres.git") - url.set("https://github.com/hfhbd/SqlDelightNativePostgres") + connection.set("scm:git://github.com/Myshkouski/postgres-native-sqldelight.git") + developerConnection.set("scm:git://github.com/Myshkouski/postgres-native-sqldelight.git") + url.set("https://github.com/Myshkouski/postgres-native-sqldelight") + } + } + } + + repositories { + maven { + name = "sonatype" + url = if (isSnapshotVersion) { + uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + } else { + uri("https://s01.oss.sonatype.org/content/repositories/releases/") } + credentials(PasswordCredentials::class) } } } signing { - val signingKey: String? by project - val signingPassword: String? by project - signingKey?.let { - useInMemoryPgpKeys(String(Base64.getDecoder().decode(it)).trim(), signingPassword) - sign(publishing.publications) - } + sign(publishing.publications) } // https://youtrack.jetbrains.com/issue/KT-46466 diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 diff --git a/postgres-native-sqldelight-driver/build.gradle.kts b/postgres-native-sqldelight-driver/build.gradle.kts index 7f9e579..73d758e 100644 --- a/postgres-native-sqldelight-driver/build.gradle.kts +++ b/postgres-native-sqldelight-driver/build.gradle.kts @@ -16,7 +16,7 @@ kotlin { } } - fun KotlinNativeTarget.config() { + fun KotlinNativeTarget.configure() { compilations.named("main") { cinterops { register("libpq") { @@ -26,10 +26,10 @@ kotlin { } } - macosArm64 { config() } - macosX64 { config() } - linuxX64 { config() } - linuxArm64 { config() } + linuxX64 { configure() } + linuxArm64 { configure() } + macosX64 { configure() } + macosArm64 { configure() } // mingwX64 { config() } sourceSets { diff --git a/postgres-native-sqldelight-driver/src/commonMain/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriver.kt b/postgres-native-sqldelight-driver/src/commonMain/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriver.kt index 971c8b1..e28f3c8 100644 --- a/postgres-native-sqldelight-driver/src/commonMain/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriver.kt +++ b/postgres-native-sqldelight-driver/src/commonMain/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriver.kt @@ -372,7 +372,8 @@ public class PostgresNativeDriver( @ExperimentalForeignApi private fun CPointer?.error(): String { val errorMessage = PQerrorMessage(this)!!.toKString() - PQfinish(this) + // TODO("find out why this lien causes segmentation fault") + // PQfinish(this) return errorMessage } diff --git a/postgres-native-sqldelight-driver/src/nativeInterop/cinterop/libpq.def b/postgres-native-sqldelight-driver/src/nativeInterop/cinterop/libpq.def index 7fe6531..bff81b4 100644 --- a/postgres-native-sqldelight-driver/src/nativeInterop/cinterop/libpq.def +++ b/postgres-native-sqldelight-driver/src/nativeInterop/cinterop/libpq.def @@ -4,5 +4,16 @@ package = libpq #staticLibraries = libpq.a #libraryPaths = /opt/homebrew/opt/libpq/lib -compilerOpts = -I/home/linuxbrew/.linuxbrew/opt/libpq/include -I/opt/homebrew/opt/libpq/include -I/usr/local/opt/libpq/include -linkerOpts = -L/home/linuxbrew/.linuxbrew/opt/libpq/lib -L/opt/homebrew/opt/libpq/lib -L/usr/local/opt/libpq/lib -lpq +linkerOpts = -lpq + +#linkerOpts.linux = -L/usr/lib +#linkerOpts.linux_x64 = -L/usr/lib/x86_64-linux-gnu +# No docs about linux_arm64? linux_aarch64? +#linkerOpts.linux_??? = -L/usr/lib/aarch64-linux-gnu +# Since Koltin/Native compiler for aarch64 is available on linux_x86_64 only, share /usr/lib/x86_64-linux-gnu between x86_64 and aarch64 +linkerOpts.linux = -L/usr/lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/aarch64-linux-gnu + +linkerOpts.osx = -L/opt/homebrew/opt/libpq/lib -L/usr/local/opt/libpq/lib + +compilerOpts.linux = -I/usr/include/postgresql +compilerOpts.osx = -I/opt/homebrew/opt/libpq/include -I/usr/local/opt/libpq/include diff --git a/testing-sqldelight/build.gradle.kts b/testing-sqldelight/build.gradle.kts index 756392b..1ad96a7 100644 --- a/testing-sqldelight/build.gradle.kts +++ b/testing-sqldelight/build.gradle.kts @@ -6,13 +6,10 @@ plugins { } kotlin { - - when (HostManager.host) { - KonanTarget.LINUX_X64 -> linuxX64() - KonanTarget.MACOS_ARM64 -> macosArm64() - KonanTarget.MACOS_X64 -> macosX64() - else -> error("Not supported") - } + linuxX64() + linuxArm64() + macosX64() + macosArm64() sourceSets { commonMain { diff --git a/testing/build.gradle.kts b/testing/build.gradle.kts index 8d14c01..e24db27 100644 --- a/testing/build.gradle.kts +++ b/testing/build.gradle.kts @@ -5,12 +5,10 @@ plugins { } kotlin { - when (HostManager.host) { - KonanTarget.LINUX_X64 -> linuxX64() - KonanTarget.MACOS_ARM64 -> macosArm64() - KonanTarget.MACOS_X64 -> macosX64() - else -> error("Not supported") - } + linuxX64() + linuxArm64() + macosX64() + macosArm64() sourceSets { commonTest { diff --git a/testing/src/commonTest/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriverTest.kt b/testing/src/commonTest/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriverTest.kt index 2c65407..be465b5 100644 --- a/testing/src/commonTest/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriverTest.kt +++ b/testing/src/commonTest/kotlin/app/softwork/sqldelight/postgresdriver/PostgresNativeDriverTest.kt @@ -2,6 +2,8 @@ package app.softwork.sqldelight.postgresdriver import app.cash.sqldelight.Query import app.cash.sqldelight.db.QueryResult +import kotlinx.cinterop.ExperimentalForeignApi +import kotlinx.cinterop.toKStringFromUtf8 import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlinx.coroutines.delay @@ -9,6 +11,8 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest +import kotlinx.datetime.Clock +import platform.posix.getenv import kotlin.test.* import kotlin.time.Duration.Companion.seconds @@ -17,7 +21,7 @@ class PostgresNativeDriverTest { @Test fun simpleTest() = runTest { val driver = PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", port = 5432, user = "postgres", database = "postgres", @@ -189,7 +193,7 @@ class PostgresNativeDriverTest { } assertFailsWith { PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", user = "postgres", database = "postgres", password = "wrongPassword" @@ -197,7 +201,7 @@ class PostgresNativeDriverTest { } assertFailsWith { PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", user = "wrongUser", database = "postgres", password = "password" @@ -208,7 +212,7 @@ class PostgresNativeDriverTest { @Test fun copyTest() { val driver = PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", port = 5432, user = "postgres", database = "postgres", @@ -234,7 +238,7 @@ class PostgresNativeDriverTest { @Test fun remoteListenerTest() = runBlocking { val other = PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", port = 5432, user = "postgres", database = "postgres", @@ -243,7 +247,7 @@ class PostgresNativeDriverTest { ) val driver = PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", port = 5432, user = "postgres", database = "postgres", @@ -283,7 +287,7 @@ class PostgresNativeDriverTest { } val driver = PostgresNativeDriver( - host = "localhost", + host = getEnvAsString("POSTGRES_HOST") ?: "localhost", port = 5432, user = "postgres", database = "postgres", @@ -320,3 +324,10 @@ class PostgresNativeDriverTest { driver.close() } } + +@OptIn(ExperimentalForeignApi::class) +private fun getEnvAsString(name: String): String? { + require(name.isNotBlank()) + require(name.isNotEmpty()) + return getenv(name)?.toKStringFromUtf8()?.takeUnless { it.isEmpty() } +} diff --git a/ubuntu-arm64-sources.list b/ubuntu-arm64-sources.list new file mode 100644 index 0000000..fa03503 --- /dev/null +++ b/ubuntu-arm64-sources.list @@ -0,0 +1,10 @@ +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main restricted +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main restricted +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble universe +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates universe +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble multiverse +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates multiverse +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-backports main restricted universe multiverse +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security main restricted +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security universe +deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-security multiverse