I'm working on a Rust wrapper for quaser DB that has a C API for all FFI clients.
I am using Bindgen to generate the Rust bindings and that works well meaning I got all bindings generated and I wrote some test code against them which compiles.
However, the tests don't runs because the C headerfiles rely on linking against a dylib called libqdb_api and somehow I cannot convince cargo to find this lib required for linking.
The question is: How do I configure cargo to find the dylib, which is in the project folder?
What I did:
use std::env;
use std::path::PathBuf;
fn main() {
let key = "LLVM_CONFIG_PATH";
env::set_var(key, env::var(key).unwrap_or("/opt/homebrew/opt/llvm/bin/llvm-config".to_string()));
// https://rust-lang.github.io/rust-bindgen/requirements.html#clang
let key = "LIBCLANG_PATH";
env::set_var(key, env::var(key).unwrap_or("/opt/homebrew/opt/llvm/lib".to_string()));
// Tell cargo to look for shared libraries in the specified directory
println!("cargo:rustc-link-search=dylib=/Users/marvin/CLionProjects/quasar-rs/qdb/lib/");
// Tell rustc to link the qdb library.
println!("cargo:rustc-link-lib=dylib=libqdb_api");
// Tell cargo to invalidate the built crate whenever the wrapper changes
println!("cargo:rerun-if-changed=wrapper.h");
// The bindgen::Builder is the main entry point to bindgen,
// and lets you build up options for the resulting bindings.
let bindings = bindgen::Builder::default()
// Derive debug implementation for structs and enums
.derive_debug(true)
// The input header we would like to generate bindings for.
.header("wrapper.h")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
// The resulting bindings will be written to $OUT_DIR/bindings.rs where $OUT_DIR is chosen by cargo and is something like
// ./target/debug/build/quasar-rs..../out/.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
Cargo test results in the following error:
= note: ld: warning: search path 'dylib=qdb/lib/' not found
ld: library 'libqdb_api' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
The linker argument from the output that I suspect:
"-L" "dylib=qdb/lib/" "-L" "/Users/marvin/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/aarch64-apple-darwin/lib" "-llibqdb_api" "/Users/marvin/
Links to files, I've added:
Things I have tried:
When I use the CARGO_MANIFEST_DIR env variable to construct the complete absolute path to the correct lib folder, cargo test throws another error even though the path is actually correct meaning the lib is actually there.
ld: warning: search path 'dylib=/Users/marvin/CLionProjects/quasar-rs/qdb/lib/' not found
ld: library 'libqdb_api' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
So far, nothing worked out. I searched quite a bit online, but I only found multiple conflicting ideas with none of them working either. I am a bit baffled at this point because I never had anything like this on Linux but apparently MacOS is quite different.
How do I configure cargo correctly on MacOS to find the dylib in the project folder?.
More specifically, relative to which folder is the C linker looking for libs?
Apparently, the problem was that the the provided dylib was compiled for AMD64, but somehow it wasn't clear that there is no ARM-64 version. The recommended workaround is to cross compile for AMD/Intel and run the binary via Rosetta on ARM/Apple silicon.