From 803a11ce273a7cad8f726bc112ea8981e582b70c Mon Sep 17 00:00:00 2001 From: osmaczko <33099791+osmaczko@users.noreply.github.com> Date: Mon, 23 Feb 2026 20:54:34 +0100 Subject: [PATCH] chore(nim-bindings): replace dynlib dlopen with plain importc The dynlib pragma hard-coded a library path and resolved it via dlopen() at runtime, preventing static linking and forcing a specific load-time path. Using bare {.importc.} lets consumers choose: link liblibchat dynamically at link time (--passL:-llibchat) or link it statically into their binary. --- nim-bindings/conversations_example.nimble | 2 +- nim-bindings/src/bindings.nim | 46 ++++++----------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/nim-bindings/conversations_example.nimble b/nim-bindings/conversations_example.nimble index 3670d1a..dc48df7 100644 --- a/nim-bindings/conversations_example.nimble +++ b/nim-bindings/conversations_example.nimble @@ -18,4 +18,4 @@ before build: exec "cargo build --release --manifest-path ../Cargo.toml" task pingpong, "Run pingpong example": - exec "nim c -r --path:src examples/pingpong.nim" \ No newline at end of file + exec "nim c -r --path:src --passL:../target/release/liblibchat.a examples/pingpong.nim" diff --git a/nim-bindings/src/bindings.nim b/nim-bindings/src/bindings.nim index 0946689..569e81a 100644 --- a/nim-bindings/src/bindings.nim +++ b/nim-bindings/src/bindings.nim @@ -1,29 +1,5 @@ # Nim FFI bindings for libchat conversations library -import std/[os] - -# Dynamic library path resolution -# Can be overridden at compile time with -d:CONVERSATIONS_LIB:"path/to/lib" -# Or at runtime via LIBCHAT_LIB environment variable -when defined(macosx): - const DEFAULT_LIB_NAME = "liblibchat.dylib" -elif defined(linux): - const DEFAULT_LIB_NAME = "liblibchat.so" -elif defined(windows): - const DEFAULT_LIB_NAME = "libchat.dll" -else: - const DEFAULT_LIB_NAME = "libchat" - -# Try to find the library relative to the source file location at compile time -const - thisDir = currentSourcePath().parentDir() - projectRoot = thisDir.parentDir().parentDir() - releaseLibPath = projectRoot / "target" / "release" / DEFAULT_LIB_NAME - debugLibPath = projectRoot / "target" / "debug" / DEFAULT_LIB_NAME - -# Default to release path, can be overridden with -d:CONVERSATIONS_LIB:"..." -const CONVERSATIONS_LIB* {.strdefine.} = releaseLibPath - # Error codes (must match Rust ErrorCode enum) const ErrNone* = 0'i32 @@ -97,23 +73,23 @@ type ## Creates a new libchat Context ## Returns: Opaque handle to the context. Must be freed with destroy_context() -proc create_context*(name: ReprCString): ContextHandle {.importc, dynlib: CONVERSATIONS_LIB.} +proc create_context*(name: ReprCString): ContextHandle {.importc.} ## Returns the friendly name of the context's identity ## The result must be freed by the caller (repr_c::String ownership transfers) -proc installation_name*(ctx: ContextHandle): ReprCString {.importc, dynlib: CONVERSATIONS_LIB.} +proc installation_name*(ctx: ContextHandle): ReprCString {.importc.} ## Destroys a context and frees its memory ## - handle must be a valid pointer from create_context() ## - handle must not be used after this call -proc destroy_context*(ctx: ContextHandle) {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_context*(ctx: ContextHandle) {.importc.} ## Creates an intro bundle for sharing with other users ## Returns: CreateIntroResult struct - check error_code field (0 = success, negative = error) ## The result must be freed with destroy_intro_result() proc create_intro_bundle*( ctx: ContextHandle, -): CreateIntroResult {.importc, dynlib: CONVERSATIONS_LIB.} +): CreateIntroResult {.importc.} ## Creates a new private conversation ## Returns: NewConvoResult struct - check error_code field (0 = success, negative = error) @@ -122,7 +98,7 @@ proc create_new_private_convo*( ctx: ContextHandle, bundle: SliceUint8, content: SliceUint8, -): NewConvoResult {.importc, dynlib: CONVERSATIONS_LIB.} +): NewConvoResult {.importc.} ## Sends content to an existing conversation ## Returns: SendContentResult struct - check error_code field (0 = success, negative = error) @@ -131,7 +107,7 @@ proc send_content*( ctx: ContextHandle, convo_id: ReprCString, content: SliceUint8, -): SendContentResult {.importc, dynlib: CONVERSATIONS_LIB.} +): SendContentResult {.importc.} ## Handles an incoming payload ## Returns: HandlePayloadResult struct - check error_code field (0 = success, negative = error) @@ -141,19 +117,19 @@ proc send_content*( proc handle_payload*( ctx: ContextHandle, payload: SliceUint8, -): HandlePayloadResult {.importc, dynlib: CONVERSATIONS_LIB.} +): HandlePayloadResult {.importc.} ## Free the result from create_intro_bundle -proc destroy_intro_result*(result: CreateIntroResult) {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_intro_result*(result: CreateIntroResult) {.importc.} ## Free the result from create_new_private_convo -proc destroy_convo_result*(result: NewConvoResult) {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_convo_result*(result: NewConvoResult) {.importc.} ## Free the result from send_content -proc destroy_send_content_result*(result: SendContentResult) {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_send_content_result*(result: SendContentResult) {.importc.} ## Free the result from handle_payload -proc destroy_handle_payload_result*(result: HandlePayloadResult) {.importc, dynlib: CONVERSATIONS_LIB.} +proc destroy_handle_payload_result*(result: HandlePayloadResult) {.importc.} # ============================================================================ # Helper functions