Closed GuilhE closed 1 month ago
I've followed this setup: https://cashapp.github.io/sqldelight/2.0.2/js_sqlite/gradle/#schema-dependencies and now it works 👍🏼
I seem to have the same issue with only one module. SQLDelight 2.0.2, Kotlin 2.0.20.
Undefined symbols for architecture arm64:
"_sqlite3_bind_blob", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_blob_wrapper69 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_double", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_double_wrapper71 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_int64", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_int64_wrapper73 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_null", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_null_wrapper74 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_parameter_index", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_parameter_index_wrapper84 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_text", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_text_wrapper75 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_bind_zeroblob", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_bind_zeroblob_wrapper80 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_busy_timeout", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_busy_timeout_wrapper22 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_changes", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_changes_wrapper16 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_clear_bindings", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_clear_bindings_wrapper85 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_close", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_close_wrapper6 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_close_v2", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_close_v2_wrapper7 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_blob", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_blob_wrapper99 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_bytes", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_bytes_wrapper106 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_count", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_count_wrapper86 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_double", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_double_wrapper100 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_int64", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_int64_wrapper102 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_name", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_name_wrapper87 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_text", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_text_wrapper103 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_column_type", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_column_type_wrapper108 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_db_config", referenced from:
knifunptr_co_touchlab_sqliter_sqlite314_sqlite3_db_config in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_db_readonly", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_db_readonly_wrapper177 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_errmsg", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_errmsg_wrapper53 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_exec", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_exec_wrapper8 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_finalize", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_finalize_wrapper109 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_last_insert_rowid", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_last_insert_rowid_wrapper14 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_open_v2", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_open_v2_wrapper43 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_prepare16_v2", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_prepare16_v2_wrapper61 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_reset", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_reset_wrapper110 in ComposeApp[5](libco.touchlab:sqliter-driver-cinterop-sqlite3-cache.a.o)
"_sqlite3_step", referenced from:
_co_touchlab_sqliter_sqlite3_sqlite3_step_wrapper97 in ComposeApp[5](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)
It's a sqlite linking issue: https://github.com/cashapp/sqldelight/issues/1442#issuecomment-1938897368
By "issue" I don't mean a "bug". You need to add -lsqlite3
to your linker flags in whatever module is building your framework.
For @GuilhE the build fails in :core-di:linkDebugFrameworkIosArm64
. I assume you're building a dynamic framework. You'll need to add the linker flags.
Why this?
kotlin.native.cacheKind.iosArm64=none
kotlin.native.cacheKind.iosSimulatorArm64=none
I wouldn't expect it to do anything useful.
@baruchn same root issue. Not sure of your config. Your whole KMP project is one module? That's a bit different, as the sqldelight plugin should add the flag.
For @GuilhE the build fails in
:core-di:linkDebugFrameworkIosArm64
. I assume you're building a dynamic framework. You'll need to add the linker flags.
I've configured a schema dependency on another module like documented here and it worked. No need for XCode > Build Settings > "Other Linker Flags"
I've configured a schema dependency on another module like documented here and it worked. No need for XCode > Build Settings > "Other Linker Flags"
So core-di
has a schema dependency that points at core-database
? Where is the db code generated? I assume in core-di
. If that's what you did, a couple things:
1) The code is being generated in core-di
, which possibly means you're exporting lots of files you don't need to be into your framework header.
2) If you make your framework static instead of dynamic, you'll probably need to do XCode > Build Settings > "Other Linker Flags"
anyway, unless something has changed with how those are packaged.
@kpgalligan Yes, one module. Static framework. Adding -lsqlite3
through XCode seems to resolve the issue.
@kpgalligan ok, let me help and clarify. This is a multi-module KMP project, and the module core-di
is "the shared framework exposed" entry point (it will depend on the other modules). It configures Koin and calls expect fun <moduleX>Module(): Module
functions to include their DI setup:
object DependencyInjection {
/**
* DI engine initialization.
* This function must be called by the iOS app inside the respective App struct.
*/
@Suppress("unused")
fun initKoin(enableNetworkLogs: Boolean) = initKoin(enableNetworkLogs = enableNetworkLogs) {}
/**
* DI engine initialization.
* This function must be called by the Android app inside the respective Application class.
*/
@HiddenFromObjC
fun initKoin(enableNetworkLogs: Boolean = false, appDeclaration: KoinAppDeclaration = {}) {
startKoin {
appDeclaration()
modules(
moduleAModule(),
moduleBModule(),
moduleCModule(),
...
)
}
}
}
build.gradle
:
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64()).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "MyFramework"
export(...) //only those that I need to call on iOS to avoid same objects with diff. namespaces problem
}
}
}
sqldelight {
//https://cashapp.github.io/sqldelight/2.0.2/js_sqlite/gradle/#schema-dependencies
databases {
create("MyDatabase") {
packageName.set("com.core.di")
dependency(projects.coreDatabase)
}
}
}
Module core-database
has all the DB logic (and code generation):
kotlin {
androidTarget()
listOf(iosArm64(), iosSimulatorArm64()).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "CoreDatabase" //although configured like this only core-di will be exported: ./gradlew :core-di:embedAndSignAppleFrameworkForXcode
}
}
}
sqldelight {
databases {
create("MyDatabase") {
packageName.set("com.core.database")
}
}
}
If I include the sqldelight
configuration on core-di
it works. If not, even with -lsqlite3
I was having this problem 🤷🏼♂️
note: to avoid export unnecessary info to objc-headers, everything is mostly internal or @HiddenFromObjC...
If not, even with -lsqlite3 I was having this problem
-lsqlite3
wasn't in the right place. It is always the solution. The linker can't find those symbols. In your original post, the failure was in :core-di:linkDebugFrameworkIosArm64
. That means code-di needs to add -lsqlite3
to its linker flags. The schema dependency now adds the linker flag in the sqldelight plugin.
note: to avoid export unnecessary info to objc-headers, everything is mostly internal or @HiddenFromObjC...
That's your code, not the code generated by sqldelight. If core-di winds up generating the whole db schema, then you're exporting all of your table-related code. I don't remember how schema dependencies work. If core-di is simply using code generated by core-database, you're not exporting as much. However, sqldelight will generate some accessor and boilerplate code, which will expose sqldelight dependencies, and possibly some coroutines. It's all part of the public Kotlin API.
-lsqlite3
wasn't in the right place. It is always the solution.
I don't doubt it.
The linker can't find those symbols. In your original post, the failure was in
:core-di:linkDebugFrameworkIosArm64
. That means code-di needs to add-lsqlite3
to its linker flags.
I've tried adding it on Xcode and also in build.gradle, like specified in this thread, but none made it work. What could I be doing wrong?
To complicate things further, upon review the thread, I believe I didn't try this:
sqldelight {
linkSqlite = true
}
So I've commented the schema dependency config and added that parameter. It works.
I've commented both the schema dependency and linkSqlite
and it works too. 🫨
Now ./gradlew :core-di:build --no-build-cache
works 🤷🏼 and I only need to specify:
sqldelight {
databases {
create("MyDatabase") {
packageName.set("com.core.database")
}
}
}
🫤😖 what!?
The schema dependency now adds the linker flag in the sqldelight plugin.
So, with the schema dependency config. the flag is being added right? Good!
That's your code, not the code generated by sqldelight. If core-di winds up generating the whole db schema, then you're exporting all of your table-related code. I don't remember how schema dependencies work. If core-di is simply using code generated by core-database, you're not exporting as much. However, sqldelight will generate some accessor and boilerplate code, which will expose sqldelight dependencies, and possibly some coroutines. It's all part of the public Kotlin API.
True, but core-di
does not "use" nor generates any code related to DB. It exposes a manager
that uses the private DAOs, etc... Yes the generated code is public
but iOS does not handle directly this code. Looking at the headers.h
the data exposed does not have anything related with the DB, only the code I need to be public and Koin, KotlinBase, Kotlinx_coroutines, Kotlinx_serialization. All part of the public Kotlin API as you mentioned 😊
This issue also happens to me when running debug builds. Release builds work fine
@GuilhE
Koin, KotlinBase, Kotlinx_coroutines, Kotlinx_serialization
If your Swift code isn't directly calling those, then it's excess. It doesn't just add clutter to the header. Binary adapters are generated, which can bloat the binary with code you don't use. It's not the end of the world, but something to possibly consider.
This issue also happens to me when running debug builds. Release builds work fine
Somewhere, somehow, -lsqlite3
isn't being passed to the linker. In this case, for some reason, only debug builds. If by "release builds" you mean when run from Xcode, then it's possible that the setting in Xcode is only applied to release builds.
SQLDelight Version
2.0.2
Operating System
14.5 (23F79) arm64
Gradle Version
8.7
Kotlin Version
2.0.20-Beta2
Dialect
SQLite
AGP Version
8.3.2
Describe the Bug
I've two modules, core-database and core-di. The first contains the
database.db
and aDao
. The second depends on the first and contains all the project's DI logic including the drivers for each target (android and iOS). On android there's no problem, it runs like expected. On iOS, running./gradlew :core-di:build
fails with the stacktrace shared bellow. The database is not directly exposed to any target, instead, a repository will be shared containing aDao
instance to perform operations.Stacktrace
If I add:
the output becomes:
Gradle Build Script