I've created a C binding to the wav2letter c++ framework. The Cmake build works fine.
Next I want to build a Rust executable with it. I link the Cmake output of the static library (libw2l_api.a) in the build.rs. When using cargo build I encounter the first error: the C++ library with the C binding doesn't support PIE.
So I either compile the C library with PIE or I disable PIE via a Rustflag. No matter what I choose I receive countless undefined reference errors (see below). I think the errors are largely the same for both options.
Where does the linking go wrong? From my noob understanding I assumed that the linking is already done (the internal one, from C API to the C++ library etc). That's why I built it with CMake.
Is it correct that Cargo does the linking again?
In more detail the steps I followed:
build C library with -fPIE compile option and then cargo build-> undefined reference errors
see here:
take C lib without PIE enabled and then run cargo build with pie disabled RUSTFLAGS='-C link-args=-no-pie' cargo build
see here:
I also checked whether a missing reference is included in the static library by example via
nm -C ../w2l/build/libw2l_api.a | grep w2l::streaming::IOBuffer::debug
U w2l::streaming::IOBuffer::debugString[abi:cxx11]() const
U w2l::streaming::IOBuffer::debugString[abi:cxx11]() const
to me it seems that the function is included. So it seems that the C(++) library is fine.
I can provide the source repo if needed/it helps.
This is my build.rs
use std::env;
use std::path::PathBuf;
fn main() {
println!("cargo:rustc-link-lib=static=stdc++");
println!("cargo:rustc-link-lib=static=w2l_api");
println!("cargo:rerun-if-changed=../w2l/src/api.h");
println!("cargo:rustc-link-search=native=../w2l/build");
let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("../w2l/src/api.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.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
nm -C ../w2l/build/libw2l_api.a | grep "fbgemm::fbgemmAlignedFree(void"
U fbgemm::fbgemmAlignedFree(void*)
U fbgemm::fbgemmAlignedFree(void*)
U fbgemm::fbgemmAlignedFree(void*)
I thought this was enough.
There are loads of symbols like that in the libw2l_api. It's statically linked in that library via Cmake. Are you telling me that I need to link it again for Rust?
Can Cargo somehow grab all these additional static libraries automatically? I think I have dozens of them. Not very maintainable, if I have to specify the exact path for all of them.
I.e. println!("cargo:rustc-link-search=../w2l/build/3rdparty/wav2letter/inference/inference/common");