rustcmakerust-bindgen

Rust C++ FFI: How specify path for linking library files?


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"

Solution

  • 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