I'm attempting to build a Rust FFI wrapper for a C++ library using the cmake
and bindgen
crates. The C++ code has a supported cmake
build.
When trying to build I get an error on attempting to link to the compiled C++ library.
My error is produced on running cargo test
and the output is:
error: linking with `cc` failed: exit status: 1
= note: /usr/bin/ld: cannot find -lcppcode: No such file or directory
collect2: error: ld returned 1 exit status
I can see the output lib files though in the build
dir, e.g.
ls ./target/debug/build/myproject-debe94b9fba6ebdb/out/lib`
libcppcode.so libcppcode_static.a
My understanding is that the critical linking lines (in build.rs
are:
println!("cargo:rustc-link-search={}", dst.display());
println!("cargo:rustc-link-lib=cppcode");
But I can't seem to get the rust-link-lib
to find the lib
files.
For completeness, my project layout is like this:
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── ext
│ └── cppcode
├── src
│ └── lib.rs
└── wrapper.h
My build.rs
is
extern crate cmake;
use cmake::Config;
use std::env;
use std::path::PathBuf;
fn main()
{
let mut cfg = Config::new("ext/cppcode");
let dst = cfg.build();
println!("cargo:rustc-link-search={}", dst.display());
println!("cargo:rustc-link-lib=cppcode");
// Specify the include directory for bindgen
let include_dir = "ext/cppcode/include";
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
// 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()
// The input header we would like to generate bindings for.
.header("wrapper.h")
// Set include directory
.clang_arg(format!("-I{}", include_dir))
// 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.
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
}
wrapper.h
is simply:
#include "cppcode.h"
For an initial build test my lib.rs
is just:
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
#[cfg(test)]
mod tests {
#[test]
fn hello_test() {
println!("Hello, tests!");
}
}
and my cargo.toml
is:
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[dependencies]
[build-dependencies]
bindgen = "0.58.1"
autotools = "0.2"
cmake = "0.1.50"
Keys to the issue were from a combination of comments by @Jmb, @eggyal, and @kmdreko.
I needed to update build.rs
to point to the build/lib
directory, and to link the non-standard cppcode_static.a
file.
let dst = cfg.build();
let lib = dst.join("lib");
println!("cargo:rustc-link-search=native={}", lib.display());
println!("cargo:rustc-link-lib=static=cppcode_static");
println!("cargo:rustc-link-lib=dylib=cppcode");
This now generates a new error in my project:
undefined reference to symbol '__asan_stack_malloc_0'
But this is likely due to other issues, and not due to the issue in question