Я пытаюсь создать оболочку Rust FFI для библиотеки C++, используя ящики cmake
и bindgen
. Код C++ имеет поддерживаемую сборку cmake
.
При попытке сборки я получаю сообщение об ошибке при попытке подключиться к скомпилированной библиотеке C++.
Моя ошибка возникает при запуске cargo test
, и результат:
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
Я вижу выходные файлы lib в каталоге build
, например.
ls ./target/debug/build/myproject-debe94b9fba6ebdb/out/lib`
libcppcode.so libcppcode_static.a
Насколько я понимаю, критическими связующими линиями (в build.rs
являются:
println!("cargo:rustc-link-search = {}", dst.display());
println!("cargo:rustc-link-lib=cppcode");
Но мне не удается найти rust-link-lib
файлы.
Для полноты макет моего проекта выглядит следующим образом:
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── ext
│ └── cppcode
├── src
│ └── lib.rs
└── wrapper.h
Мой lib
это
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!");
}
build.rs
это просто:
#include "cppcode.h"
Для первоначального теста сборки мой wrapper.h
просто:
#![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!");
}
}
и мой lib.rs
это:
[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"
Я только что создал проект именно с указанными выше файлами, но с папкой ext/cppcode
, содержащей очень простой проект cmake/cpp, у меня все сработало... однако: в моем случае была создана только статическая библиотека cppcode
, названная и расположен иначе, чем у вас: target/debug/build/myproject-bd9c7419240953ba/out/libcppcode.a
. Это меня удивило, поскольку я ожидал, что Rust свяжет динамическую библиотеку в таких случаях (тип ссылки не указан и статический исполняемый файл не создается). Возможно, проблема здесь в названии вашей статической библиотеки (предположительно из вашего файла CMakeLists.txt
)?
Является ли _static.a
стандартным суффиксом, который ищет компоновщик? Я так не думал, я думал это просто .a
и может быть альтернативное расширение, но могу ошибаться.
Пожалуйста, не редактируйте вопросы, чтобы добавлять новые вопросы. Вместо этого вам следует открыть совершенно новый вопрос.
Ключом к разгадке проблемы послужила комбинация комментариев @Jmb, @eggyal и @kmdreko.
Мне нужно было обновить build.rs
, чтобы он указывал на каталог build/lib
и связать нестандартный файл cppcode_static.a
.
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");
Теперь это создает новую ошибку в моем проекте:
undefined reference to symbol '__asan_stack_malloc_0'
Но это, скорее всего, связано с другими проблемами, а не с рассматриваемой проблемой.
Ваш код C++, скорее всего, был скомпилирован с опцией -fsanitize=address
. Вам нужно либо удалить эту опцию из компиляции C++, либо добавить ее к флагам ссылок (например, с помощью Cargo::rustc-link-arg).
Также обратите внимание, что если вы не перекомпилируете свой код C++, смешивание двоичных файлов, скомпилированных с дезинфицирующими средствами и без них, может привести к ложным срабатываниям. Возможно, вы также захотите скомпилировать свой код Rust с поддержкой дезинфицирующего средства.
Какова ценность
dst.display()
? Я подозреваю, что он короче./target/debug/build/myproject-debe94b9fba6ebdb/out/lib
и вам придется самостоятельно добавить недостающую часть хвоста.