cashapp / sqldelight

SQLDelight - Generates typesafe Kotlin APIs from SQL
https://cashapp.github.io/sqldelight/
Apache License 2.0
6.01k stars 500 forks source link

Kotlin Multiplatform build fails on macOS #5305

Open JulianBissekkou opened 1 week ago

JulianBissekkou commented 1 week ago

SQLDelight Version

2.0.2

Operating System

Mac OS 14.3.1

Gradle Version

Gradle 8.7 Revision: 650af14d7653aa949fce5e886e685efc9cf97c10

Kotlin Version

1.9.23

Dialect

SQLite

AGP Version

No response

Describe the Bug

I have an iOS App that is supports macOS.

Screenshot 2024-06-15 at 13 18 06

After the build failed I added the linker flag -lsqlite3

Screenshot 2024-06-15 at 13 19 17

on iOS I am able to run the App and everything works as expected, however, on macOS the build fails.

Stacktrace

Undefined symbols for architecture arm64:
  "_sqlite3_enable_load_extension", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_enable_load_extension_wrapper190 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_load_extension", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_load_extension_wrapper189 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_mutex_held", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_mutex_held_wrapper213 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_mutex_notheld", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_mutex_notheld_wrapper214 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_unlock_notify", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_unlock_notify_wrapper239 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_win32_set_directory", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_win32_set_directory_wrapper171 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_win32_set_directory16", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_win32_set_directory16_wrapper173 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
  "_sqlite3_win32_set_directory8", referenced from:
      _co_touchlab_sqliter_sqlite3_sqlite3_win32_set_directory8_wrapper172 in shared[12](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

### Gradle Build Script

```gradle
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
    alias(libs.plugins.kotlinxSerialization)
    alias(libs.plugins.ksp)
    alias(libs.plugins.kmpNativeCoroutines)
    alias(libs.plugins.sqldelight)
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions {
                jvmTarget = "11"
            }
        }
    }

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64(),
        macosArm64(),
        macosX64(),
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "Shared"
            isStatic = true
        }
    }

    sourceSets {
        androidMain.dependencies {
            implementation(libs.ktor.client.okhttp)
        }
        iosMain.dependencies {
            implementation(libs.native.driver)
        }
        macosMain.dependencies {
            implementation(libs.native.driver)
        }
        commonMain.dependencies {
            implementation(libs.ktor.client.core)
            implementation(libs.ktor.client.content.negotiation)
            implementation(libs.ktor.serialization.kotlinx.json)
            implementation(libs.koin.core)
            implementation(libs.runtime)
            implementation(libs.sqldelight.coroutine.extension)
            api(libs.kmm.viewmodel)
        }

        // Required by KMM-ViewModel
        all {
            languageSettings.optIn("kotlinx.cinterop.ExperimentalForeignApi")
            languageSettings.optIn("kotlin.experimental.ExperimentalObjCName")
        }
    }
}

android {
    namespace = "com.jetbrains.kmpapp.shared"
    compileSdk = 34
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
    defaultConfig {
        minSdk = 24
    }
}

sqldelight {
    databases {
        create("AppDatabase") {
            packageName.set("com.jetbrains.kmpapp")
        }
    }
    linkSqlite = true
}
JulianBissekkou commented 1 week ago

I am pretty new to kmp, so I might be wrong here but I assume that the sqlite3 library on the mac is somehow "outdated" and does not contain the symbols.

I assume that the libary that is linked is a system library stored at /System/Library/Tcl/sqlite3/libtclsqlite3.dylib. When I try to search for the first missing symbol I get no result:

nm /System/Library/Tcl/sqlite3/libtclsqlite3.dylib | grep load_extension  

However, when I do the same with sqllite installed by homebrew I get some results:

nm /opt/homebrew/Cellar/sqlite/3.42.0/lib/libsqlite3.dylib | grep load_extension
00000000000116b4 T _sqlite3_enable_load_extension
000000000001122c T _sqlite3_load_extension

Does anybody maybe know a workaround? This issue is currently blocking for us.

kpgalligan commented 1 week ago

I haven't built a macos app with sqldelight personally (I wrote most of the driver, or at least the original version). The tests run fine with -lsqlite3 without specifying the library search path, so I assume it's using a sqlite3 library from Xcode tools. libtclsqlite3 kind of seems like it's specific to tcl, but I've never used it. I would guess you'll need to include your own sqlite implementation.

Since you're building with Xcode, the easy solution would probably be to grab and build sqlite source directly in Xcode, and drop -lsqlite3. To do that just for iOS, I assume you'll need some target slight-of-hand in Xcode config.

JulianBissekkou commented 1 week ago

I compiled the sql lib from the mentioned sources and added the dylib file to the project. It was able to find some symbols but still failed to compile since some of them are unknown. Since I am building just a small POC i want to keep things simple. I was wondering if anything else is configued on my side. Is there an example project with a macOS app that I can try out and compare?

JulianBissekkou commented 1 week ago

@kpgalligan If you run the tests are you compiling an MacosArm64 binary?