cashapp / sqldelight

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

Kotlin Multiplatform (iOS): not working with Cocoapods plugin #1442

Closed LiewJunTung closed 4 years ago

LiewJunTung commented 4 years ago

Tried to use the kotlin native cocoapod gradle plugin. But it produces the error below whenever sqldelight is included as a dependency.

Undefined symbols for architecture arm64: "_sqlite3_bind_text16", referenced from: _SQLiter_SQLiteStatement_nativeBindString in app(combined.o) "_sqlite3_bind_int64", referenced from: _SQLiter_SQLiteStatement_nativeBindLong in app(combined.o) "_sqlite3_last_insert_rowid", referenced from: _SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o) "_sqlite3_reset", referenced from: _SQLiter_SQLiteConnection_nativeResetStatement in app(combined.o) "_sqlite3_changes", referenced from: _SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in app(combined.o) _SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o) "_sqlite3_open_v2", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_db_config", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_busy_timeout", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_trace", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_bind_parameter_index", referenced from: _SQLiter_SQLiteConnection_nativeBindParameterIndex in app(combined.o) "_sqlite3_column_bytes", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetString in app(combined.o) _SQLiter_SQLiteConnection_nativeColumnGetBlob in app(combined.o) "_sqlite3_finalize", referenced from: _SQLiter_SQLiteStatement_nativeFinalizeStatement in app(combined.o) "_sqlite3_column_text", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetString in app(combined.o) "_sqlite3_column_name", referenced from: _SQLiter_SQLiteConnection_nativeColumnName in app(combined.o) "_sqlite3_bind_double", referenced from: _SQLiter_SQLiteStatement_nativeBindDouble in app(combined.o) "_sqlite3_profile", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_close", referenced from: _SQLiter_SQLiteConnection_nativeClose in app(combined.o) _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_prepare16_v2", referenced from: _SQLiter_SQLiteConnection_nativePrepareStatement in app(combined.o) "_sqlite3_column_type", referenced from: _SQLiter_SQLiteConnection_nativeColumnIsNull in app(combined.o) _SQLiter_SQLiteConnection_nativeColumnType in app(combined.o) "_sqlite3_column_count", referenced from: _SQLiter_SQLiteConnection_nativeColumnCount in app(combined.o) "_sqlite3_bind_blob", referenced from: _SQLiter_SQLiteStatement_nativeBindBlob in app(combined.o) "_sqlite3_db_readonly", referenced from: _SQLiter_SQLiteConnection_nativeOpen in app(combined.o) "_sqlite3_column_int64", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetLong in app(combined.o) "_sqlite3_bind_null", referenced from: _SQLiter_SQLiteStatement_nativeBindNull in app(combined.o) "_sqlite3_extended_errcode", referenced from: android::throw_sqlite3_exception(sqlite3) in app(combined.o) android::throw_sqlite3_exception(sqlite3, char const) in app(combined.o) "_sqlite3_column_double", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetDouble in app(combined.o) "_sqlite3_column_blob", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetBlob in app(combined.o) "_sqlite3_step", referenced from: _SQLiter_SQLiteConnection_nativeStep in app(combined.o) _SQLiter_SQLiteStatement_nativeExecute in app(combined.o) _SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in app(combined.o) _SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in app(combined.o) "_sqlite3_clear_bindings", referenced from: _SQLiter_SQLiteConnection_nativeClearBindings in app(combined.o) "_sqlite3_errmsg", referenced from: android::throw_sqlite3_exception(sqlite3) in app(combined.o) android::throw_sqlite3_exception(sqlite3, char const) in app(combined.o) ld: symbol(s) not found for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)

kpgalligan commented 4 years ago

In xcode, you need to add -lsqlite3 to Other Liker Flags in your Bulid Settings. I generally get that from other included Pods, but it depends what you're using.

image

alfdev commented 4 years ago

Hi,

i have the same problem when build with gradlew assemble, any suggestion? thanks

kpgalligan commented 4 years ago

Stack trace?

alfdev commented 4 years ago

Undefined symbols for architecture x86_64: "_sqlite3_bind_blob", referenced from: _SQLiter_SQLiteStatement_nativeBindBlob in result.o "_sqlite3_bind_double", referenced from: _SQLiter_SQLiteStatement_nativeBindDouble in result.o "_sqlite3_bind_int64", referenced from: _SQLiter_SQLiteStatement_nativeBindLong in result.o "_sqlite3_bind_null", referenced from: _SQLiter_SQLiteStatement_nativeBindNull in result.o "_sqlite3_bind_parameter_index", referenced from: _SQLiter_SQLiteConnection_nativeBindParameterIndex in result.o "_sqlite3_bind_text16", referenced from: _SQLiter_SQLiteStatement_nativeBindString in result.o "_sqlite3_busy_timeout", referenced from: _SQLiter_SQLiteConnection_nativeOpen in result.o "_sqlite3_changes", referenced from: _SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in result.o _SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in result.o "_sqlite3_clear_bindings", referenced from: _SQLiter_SQLiteConnection_nativeClearBindings in result.o "_sqlite3_close", referenced from: _SQLiter_SQLiteConnection_nativeClose in result.o _SQLiter_SQLiteConnection_nativeOpen in result.o "_sqlite3_column_blob", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetBlob in result.o "_sqlite3_column_bytes", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetString in result.o _SQLiter_SQLiteConnection_nativeColumnGetBlob in result.o "_sqlite3_column_count", referenced from: _SQLiter_SQLiteConnection_nativeColumnCount in result.o "_sqlite3_column_double", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetDouble in result.o "_sqlite3_column_int64", referenced from: _SQLiter_SQLiteConnection_nativeColumnGetLong in result.o .. .. "_sqlite3_step", referenced from: _SQLiter_SQLiteConnection_nativeStep in result.o _SQLiter_SQLiteStatement_nativeExecute in result.o _SQLiter_SQLiteStatement_nativeExecuteForChangedRowCount in result.o _SQLiter_SQLiteStatement_nativeExecuteForLastInsertedRowId in result.o "_sqlite3_trace", referenced from: _SQLiter_SQLiteConnection_nativeOpen in result.o ld: symbol(s) not found for architecture x86_64 e: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld invocation reported errors

alfdev commented 4 years ago

In the gradle i have:

        compilations.each {
            it.extraOpts("-linker-options", "-lsqlite3")
        }
RudolfHladik commented 4 years ago

@alfdev where exactly do you have it? do you have some example?

HFabi commented 4 years ago

I have the same problem when I use the Cocoapods plugin. The workaround from kpgalligan (setting the linker options in Xcode) is working but setting the linker options in gradle seems to have no effect.

Am I doing something wrong here?

val isDevice = System.getenv("SDK_NAME")?.startsWith("iphoneos") == true
if (isDevice)
  iosArm64("ios")
else
  iosX64("ios")

targets.getByName<KotlinNativeTarget>("ios").compilations.forEach {
  it.kotlinOptions.freeCompilerArgs += arrayOf("-linker-options", "-lsqlite3")
}

Also I found this issue https://github.com/JetBrains/kotlin-native/issues/3672 . Does anyone know if “extraOpts” or “compilerArgs” work with the Cocoapods plugin in general ?

kpgalligan commented 4 years ago

The linker option in Xcode isn't a workaround. That's when Xcode is linking your kotlin code with the rest of the iOS code, and it needs to explicitly link to sqlite3. You'll also need to do that in gradle if running tests, because that's building an executable without an explicit Xcode project.

On Sun, Mar 29, 2020 at 8:18 AM Fabian notifications@github.com wrote:

I have the same problem when I use the Cocoapods plugin. The workaround from kpgalligan (setting the linker options in Xcode) is working but setting the linker options in gradle seems to have no effect.

Am I doing something wrong here?

val isDevice = System.getenv("SDK_NAME")?.startsWith("iphoneos") == true

if (isDevice)

iosArm64("ios")

else

iosX64("ios")

targets.getByName("ios").compilations.forEach {

it.kotlinOptions.freeCompilerArgs += arrayOf("-linker-options", "-lsqlite3")

}

Also I found this issue JetBrains/kotlin-native#3672 https://github.com/JetBrains/kotlin-native/issues/3672 . Does anyone know if “extraOpts” or “compilerArgs” work with the Cocoapods plugin in general ?

— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/cashapp/sqldelight/issues/1442#issuecomment-605627452, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAQWICSXJANRGBECOSTZRTRJ44CTANCNFSM4IOB6XDA .

-- Kevin Galligan https://twitter.com/kpgalligan

j4GGy commented 4 years ago

@HFabi I ran into the same issue although I'm not using the Cocoapods plugin but the packForXCode gradle taks as in the Kotlin MPP for Android-iOS example and then linking the produced framework directly in Xcode. I was able to fix the issue by adding the linker options as follows:

OSTarget("ios") {
        binaries {
            framework {
                baseName = "sharedDatabase"
                linkerOpts.add("-lsqlite3")
            }
        }
    }

Maybe adding the linker options to the binaries may help in your case, too.

targets.getByName<KotlinNativeTarget>("ios").binaries.forEach {
        it.linkerOpts.add("-lsqlite3")
}
Yeeeeeeah commented 4 years ago

@HFabi I ran into the same issue although I'm not using the Cocoapods plugin but the packForXCode gradle taks as in the Kotlin MPP for Android-iOS example and then linking the produced framework directly in Xcode. I was able to fix the issue by adding the linker options as follows:

OSTarget("ios") {
        binaries {
            framework {
                baseName = "sharedDatabase"
                linkerOpts.add("-lsqlite3")
            }
        }
    }

Maybe adding the linker options to the binaries may help in your case, too.

targets.getByName<KotlinNativeTarget>("ios").binaries.forEach {
        it.linkerOpts.add("-lsqlite3")
}

Hello, I try to add linkerOpts.add("-lsqlite3") But I have this problem : > Could not get unknown property 'linkerOpts' for object of type org.jetbrains.kotlin.gradle.dsl.KotlinNativeBinaryContainer.

This is my build.gradle :

    targets {
        final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") ? presets.iosArm64 : presets.iosX64
        fromPreset(iOSTarget, 'ios') {
            binaries {
                framework('iosFramework') // nom du framework
            }
        }
    }
HFabi commented 4 years ago

@Yeeeeeeah before I switched to the cocoapods plugin, I also used the packForXCode approach from the tutorial. There I could set linkSqlite = true, perhaps that solves your problem.

// build.gradle.kts
sqldelight {
  database("...") {
  // ...
  }
  linkSqlite = true  // <-
}
luca992 commented 4 years ago

Adding -lsqlite3 to Other Liker Flags in your Bulid Settings like @kpgalligan suggested worked for me. But I also got it working with the cocoapods plugin using a dynamic library with the linker option added:

    targets.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().forEach{
        it.binaries.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>()
            .forEach { lib ->
                lib.isStatic = false
                lib.linkerOpts.add("-lsqlite3")
            }
    }

Referenced: https://github.com/JetBrains/kotlin-native/issues/3059#issuecomment-577041551

trancongdanh commented 2 years ago

@Yeeeeeeah before I switched to the cocoapods plugin, I also used the packForXCode approach from the tutorial. There I could set linkSqlite = true, perhaps that solves your problem.

// build.gradle.kts
sqldelight {
  database("...") {
  // ...
  }
  linkSqlite = true  // <-
}

Thank you very much. It helped me very much.

atonamy commented 2 years ago

Adding -lsqlite3 to Other Liker Flags in your Bulid Settings like @kpgalligan suggested worked for me. But I also got it working with the cocoapods plugin using a dynamic library with the linker option added:

    targets.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().forEach{
        it.binaries.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>()
            .forEach { lib ->
                lib.isStatic = false
                lib.linkerOpts.add("-lsqlite3")
            }
    }

Referenced: JetBrains/kotlin-native#3059 (comment)

Not working :( I having this error if I apply your snippets

 dyld: Library not loaded: @rpath/shared.framework/shared
  Referenced from: /Users/xxx/Library/Developer/CoreSimulator/Devices/C45D96EE-5100-44C1-BF8D-B78342FDE4AA/data/Containers/Bundle/Application/221A60F3-33D8-465B-B627-29C1EDA3D82A/iosApp.app/iosApp
  Reason: image not found

The only working solution is -lsqlite3 flag in Other Liker Flags of Xcode which is suggested by @kpgalligan

kpgalligan commented 2 years ago

This is kind of old, but the above message isn't really correct. This error:

 dyld: Library not loaded: @rpath/shared.framework/shared
  Referenced from: /Users/xxx/Library/Developer/CoreSimulator/Devices/C45D96EE-5100-44C1-BF8D-B78342FDE4AA/data/Containers/Bundle/Application/221A60F3-33D8-465B-B627-29C1EDA3D82A/iosApp.app/iosApp
  Reason: image not found

is totally unrelated. This happens occasionally when running a fresh build of a Kotlin framework built with the cocoapods plugin. You essentially need to run pod install again, then build the Kotlin again, and it should work.

Here's the summary as best I can explain it.

The sqldelight driver needs to have an instance of the sqlite library to link against for everything to work. A version ships with iOS, and you can use that by adding -lsqlite3 to the linker args. As stated above, you may need to do that in your kotlin config (generally for tests), or if you're building a dynamic framework, you can let sqldelight add it. It's been a while, but I'm pretty sure dynamic frameworks carry their linker args, static do not.

I will add to the statement above that we almost always use static frameworks. There are reasons why they tend to be the better option, so I wouldn't pick dynamic frameworks just because you can avoid -lsqlite3 :)

I think the confusion here is more around how iOS (or most native builds) link to things. It's just kind of different than what we might be used to in the JVM.

There are benefits. If you want to use SQLCIpher in your production build, but regular sqlite in your test build(s), that's pretty easy to support. You just link to one or the other when it's time to output a binary. That's kind of beyond the scope for today, but doable (you can also ship your own builds of sqlite from source. Android, on the other hand, makes this more complicated as you also need to maintain and ship an implementation of the driver, but I digress).

TL;DR linking to the sqlite3 library isn't a workaround. It's just one of the things you need to do. In some contexts, it happens automatically, and in others explicitly, but if you're hitting sqlite on iOS, the linking happened somewhere.

rohitpawar2811 commented 2 years ago

hello can anyone make a just an registration page and Connect with database and when data inserted it shows in List-View by Using KMM ,its urgent rohitpawar28112000@gmail.com

kpgalligan commented 2 years ago

@Rohit-pawar902 Are you asking for a sample app using Kotlin native? There are many. Ours: https://github.com/touchlab/KaMPKit

ArtRoman commented 2 years ago

I've got Undefined symbols for architecture arm64: error on SwiftUI previews on M1, spent hours on this. Solution was found in #2512

mobinyardim commented 1 year ago

@ArtRoman thank you after 2 days of struggle it works for me 👌🏻👌🏻

Shahroz16 commented 1 year ago

@ArtRoman I am not using coco-pods but rather SPM and I am still not able to get my preview working. Even though I have added isStatic = false

cocoapods {
        summary = "Some description for the Shared Module"
        homepage = "Link to the Shared Module homepage"
        version = "1.0"
        ios.deploymentTarget = "14.1"
        framework {
            baseName = "shared"
            isStatic = false
        }
    }

 targets.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>()
            .forEach {
                it.binaries.filterIsInstance<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>()
                    .forEach { lib ->
                        lib.isStatic = false
                        lib.linkerOpts.add("-lsqlite3")
                    }
            }

any insights what might be the problem? getting this error

 HumanReadableSwiftError

    SettingsError: noExecutablePath(<IDESwiftPackageStaticLibraryProductBuildable:ObjectIdentifier(0x00006000005a18c0):'shared'>)
ArtRoman commented 1 year ago

@ArtRoman I am not using coco-pods but rather SPM and I am still not able to get my preview working. Even though I have added isStatic = false


cocoapods {
        summary = "Some description for the Shared Module"
        homepage = "Link to the Shared Module homepage"
        version = "1.0"
        ios.deploymentTarget = "14.1"
        framework {
            baseName = "shared"
            isStatic = false
        }
    }

Try to use the full format:

cocoapods {
    …
    framework((Action) {
        it.isStatic = false
    })
}
kpgalligan commented 1 year ago

Swift UI previews, AFAIK, require dynamic frameworks. That has nothing to do with the sqlite linking errors. SPM support in KMP doesn't carry along the linking params.

I believe making the framework dynamic (isStatic = false) inadvertently "fixed" the sqlite linking issue because dynamic frameworks also carry along some extra linking info that static frameworks do not. The sqldelight Gradle plugin silently adds -lsqlite3, and dynamic frameworks carry that along.

Again, when you see:

Undefined symbols for architecture arm64:
"_sqlite3_bind_text16", referenced from:
// etc

It's a linking issue, and it means you need -lsqlite3 somewhere.

I ran into this exact problem, live, less than a week ago: https://youtu.be/CIZU_NNAZsA?t=5665

As you can see in the video (if you maximize it, the text is small), you go into Xcode settings for your target, find "Other linker flags" and add -lsqlite3.

See this horribly compressed screenshot:

Screen Shot 2022-10-29 at 4 34 51 PM

Again, whenever you see Undefined symbols for architecture (whatever) followed by "_sqlite3_(something something)", referenced from:, that always means you need -lsqlite3 in your linker settings, and you do not have it.

Update: I have since confirmed, if you have a static framework, you need to add -lsqlite3 to your linker flags directly in Xcode. You can just use dynamic frameworks, but for a number of reasons I would generally suggest static, but that's a choice everybody needs to make for themselves...

Shahroz16 commented 1 year ago

@kpgalligan adding the -lsqlite3 linker flag was my first action too, but unfortunately, this hasn't fixed my preview issues.

I am now inclining towards, maybe it's an Xcode issue with Swift preview, regardless of Static or Dynamic framework, SwiftUI just breaks with KMM.

Don't think it's down to SQL even anymore, its just any other framework causes preview to fail

kpgalligan commented 1 year ago

For SwiftUI previews, I'm told you need dynamic frameworks, although I'd definitely say I'm no expert there. If you use SQLDelight and a dynamic framework from Kotlin, you shouldn't need to add -lsqlite3 as SQLDelight adds it to the build, and the dynamic framework carries that along. At least in prod builds, I'd say static frameworks are generally better, but if you can't do SwiftUI previews in dev with static, then in dev certainly you'll want dynamic. It's kind of a toss up on which type to use, really, but besides the SwiftUI previews, I find most of the benefits to be in favor of static.

charlee-dev commented 1 year ago

I just had this issue in the new project I started, and all I had to do was what Kevin said, add -lsqlite3 in Other Linker Flags in the Xcode 🍺

terrakok commented 1 year ago

@kpgalligan FYI since Kotlin 1.8.0 kotlin frameworks produced by cocoapods plugin is dynamic by default: https://kotlinlang.org/docs/whatsnew18.html#dynamic-frameworks-by-default-in-the-cocoapods-gradle-plugin

You can set up it as static if you know what it means under the hood. But the recommended way is to pass linker flags to the compiler in an ios app build setting.

whataa commented 1 year ago

Add sqlite3 to your iosApp's podfile:

target 'iosApp' do
  pod 'sqlite3', '~> 3.42.0' // choose your version
end

If you encounter the following problems:

ld: file not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a

Change your IPHONEOS_DEPLOYMENT_TARGET to at least 9.0, for example:

# iosApp's podfile
post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |config|
            config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '14.1'
        end
    end
end
charlee-dev commented 1 year ago

Or add it to your ios target:

    ios {
        binaries.framework {
            baseName = project.name

            compilations.all {
                kotlinOptions.freeCompilerArgs += arrayOf("-linker-options", "-lsqlite3")
            }
        }
    }
neelratanguria commented 5 months ago

Solution mentioned here worked for me:

https://stackoverflow.com/questions/31363714/undefined-symbols-for-architecture-i386-using-libsqlite3-dylib-with-fmdb-xcode-7

I added libsqlite3.tbd in the Target Setting->Build Phase->Link Binary with Libraries

kpgalligan commented 5 months ago

My annual reminder that this is just a linker issue. It is expected. Adding the linker flag isn't a "workaround". It's how linking works.

In reference to the solution from @whataa

target 'iosApp' do
  pod 'sqlite3', '~> 3.42.0' // choose your version
end

I haven't tried this, but you should check if you're actually building your own sqlite binary. The iPhone has a sqlite library that you can link to. Building your own will "work", but you'll have a fair bit of binary in your app that you don't need.

Anyway, wherever you see:

Undefined symbols for architecture x86_64: "_sqlite3_bind_blob", referenced from: _SQLiter_SQLiteStatement_nativeBindBlob in result.o "_sqlite3_bind_double", referenced from: _SQLiter_SQLiteStatement_nativeBindDouble in result.o "_sqlite3_bind_int64", referenced from: 
etc...

It's the linker saying it can't find sqlite3. That is always the issue, and the most straightforward answer is always to tell the linker to use the system sqlite3 library by adding -lsqlite3 to the linker flags. The difficult part is finding who is running the linker and where the flag needs to be added. If the Kotlin compiler is failing, then you need to add that to Kotlin config. If Xcode and the iOS build is failing, then it needs to be added in Xcode, for whatever target is failing. Generally that's the app target, and the field is "Other Linker Flags" in build settings.

Erlan14 commented 2 months ago

XCode > Build Phases > Link Binary With Libraries > and adding the libsqlite3.tbd

kpgalligan commented 2 months ago

(Taps the sign...)

https://github.com/cashapp/sqldelight/issues/1442#issuecomment-1938897368

XCode > Build Settings > "Other Linker Flags"

Add -lsqlite3.

That is the answer. If you're using the sqlite shipped with all Apple platforms, you need to link to it. Period.