cxx
cxx copied to clipboard
how to use cxx with just .so lib and .h file
I have a simple worked cxx project example with a .cc file and .h file, now I want to replace the .cc file to a .so lib, because that is the real scenario I meet. In my opinion, I just need to revise the build.rs. What should I do?
build.rs
cxx_build::bridge("src/main.rs") .file("src/calculate.cc") // I want replace this .cc file to .so lib, like: .file("src/calculate.so") .include("./lib") .flag_if_supported("-std=c++14") .compile("cxx-demo");
I'd say, just leave the file
call out and link -lcalculate
to your final project link, e.g., by printing a cargo link lib instruction from this build.rs
; see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib.
I'd say, just leave the
file
call out and link-lcalculate
to your final project link, e.g., by printing a cargo link lib instruction from thisbuild.rs
; see https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-link-lib.
I think it doesn't solve my problem.
println!(r"cargo:rustc-link-search=./lib"); println!("cargo:rustc-link-lib=calculate");
If I use bindgen
to translate C to Rust, this cargo code is really helpful, because the code files do not have complex relationship. While when I use cxx
, the code files like main.rs
build.rs
calculate.cc
calculate.h
main.rs.cc
main.rs.h
, they have complex relationship. Now I want replace calculate.cc
to its calculate.so
, I still don't know how it work if I just add a cargo:rustc-link-lib=
. Could you please give me a specific example?
I think it doesn't solve my problem.
println!(r"cargo:rustc-link-search=./lib"); println!("cargo:rustc-link-lib=calculate");
But this does look like it should solve your problem.
The header calculate.h
will be used to satisfy symbol lookup on the C++ side for the generated main.rs.*
. When doing the final link, the linker will link libcalculate.so
, so it will satisfy link-time dependencies. It should just work.
What errors are you facing, if you do it this way?
Could you please give me a specific example?
I didn't have such a use case yet, so, unfortunately, no.
After adding the code above, now the build.rs is like `println!(r"cargo:rustc-link-search=./lib"); println!("cargo:rustc-link-lib=calculate");
cxx_build::bridge("src/main.rs") .file("src/calculate.cc") // I want replace this .cc file to .so lib, like: .file("src/calculate.so") .compile("cxx-demo");`
cxx still need .file("src/calculate.cc")
, but I don't have .cc
file. if I drop this line, compilation will report errors. If I just replace .file("src/calculate.cc")
to .file("src/calculate.so")
, it doesn't work. So I want to know what should I do with cxx.
compilation will report errors
WHAT errors? Without context it's impossible to help you.
cxx still need
.file("src/calculate.cc")
In which sense "need"? You mean it won't compile without adding file
directive? If so, maybe use empty file? But it should work without.
BTW, what's the name of your lib and what is your OS? For example, on Linux and most other OSes calculate.so
is not a valid lib name for standard tools, it would expect libcalculate.so
.
Again, I don't have this use case and didn't try it myself. We are in fact doing quite the opposite - exposing our Rust lib to C++ via a .so (which has its own challenges).
In which sense "need"? You mean it won't compile without adding
file
directive? If so, maybe use empty file? But it should work without.
Yes, it doesn't work without file
directive. Neither the empty file
. This is the compilation error:
In my opinion, cxx
doesn't provide a call the function of. so file or. a file. All the examples from the https://cxx.rs/ show that we need a cxx_build
in our build.rs
, and we can use #[cxx::bridge] mod ffi {}
to link a .cc file which is used in cxx_build::bridge().file()
in build.rs
. While cxx_build
is based on cc
, which is just "a builder for compilation of a native library". (See https://docs.rs/cc/latest/cc/struct.Build.html)
And cxx_build::bridge().file().compile()
will generate a .a file in target
folder, which could linked #[cxx::bridge] mod ffi {}
in main.rs
. So We can call .cc file in Rust. That is what I think cxx works, but I don't know the underlying logic of their connection.
I have read all the author's answer about how to use static or shared C++ lib in cxx
, he said it will work because cxx
just append on .h file, while he didn't give more details or specific examples.
In my opinion, cxx doesn't provide a call the function of. so file or. a file.
Well, I just implemented a small test, and it works just fine.
I created a shared library containing one C++ symbol returning Rust String type (via string type defined in rust/cxx.h
; in fact, I used cxx bridge to create it within cargo as cdylib
- THAT has some issues which won't work with the stock Rust linker due to issues with linker script, but that's another story).
C++ code:
#include "rust/cxx.h"
namespace rte
{
namespace cxx
{
namespace test_lib
{
rust::String hello_from_cxx() {
return rust::String("Hello!");
}
}
}
}
Output from nm
on the linked file:
$ nm -C libcxx_library.dylib | grep -i hello
0000000000003488 T rte::cxx::test_lib::hello_from_cxx()
Then, I created a crate using this library via cxx
.
Cargo.toml:
[package]
name = "cxx_library_use"
version = "0.1.0"
edition = "2021"
[dependencies]
cxx = { workspace = true }
[build-dependencies]
cxx-build = { workspace = true }
build.rs:
fn main() {
cxx_build::bridge("src/lib.rs")
.include("path/to/header")
.flag_if_supported("-std=c++17")
.compile("rte-cxx-use-lib");
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rustc-link-lib=cxx_library");
}
and finally, src/lib.rs:
#[cfg(test)]
mod tests {
#[cxx::bridge(namespace = "rte::cxx::test_lib")]
pub(crate) mod ffi {
unsafe extern "C++" {
include!("path/to/header/cxx_library.hpp");
pub fn hello_from_cxx() -> String;
}
}
#[test]
fn check() {
assert_eq!(ffi::hello_from_cxx(), "Hello!");
}
}
Running this test produces:
cargo test --package cxx_library_use --lib -- tests::check --exact --nocapture
[...]
running 1 test
test tests::check ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Examining the test executable shows, that it indeed depends on the shared library:
$ otool -L target/debug/deps/cxx_library_use-1750aadfc35f7d0e
target/debug/deps/cxx_library_use-1750aadfc35f7d0e:
<path>/libcxx_library.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
And looking at symbols, it has an undefined symbol satisfied by this library:
nm -C target/debug/deps/cxx_library_use-1750aadfc35f7d0e | grep hello
00000001000039c4 t cxx_library_use::tests::ffi::hello_from_cxx::h1919e2bcda45da9d
U rte::cxx::test_lib::hello_from_cxx()
0000000100003d6c T _rte$cxx$test_lib$cxxbridge1$hello_from_cxx
It works.
QED
I tested this on MacOS, which is quite picky. Linux should work exactly the same.
Linux should work exactly the same.
BTW, I added the test to our project's unit tests and pushed it to our CI for verification. Yes, it works on Linux w/o any issues as well.
In my opinion, cxx doesn't provide a call the function of. so file or. a file.
Well, I just implemented a small test, and it works just fine.
I created a shared library containing one C++ symbol returning Rust String type (via string type defined in
rust/cxx.h
; in fact, I used cxx bridge to create it within cargo ascdylib
- THAT has some issues which won't work with the stock Rust linker due to issues with linker script, but that's another story).C++ code:
#include "rust/cxx.h" namespace rte { namespace cxx { namespace test_lib { rust::String hello_from_cxx() { return rust::String("Hello!"); } } } }
Output from
nm
on the linked file:$ nm -C libcxx_library.dylib | grep -i hello 0000000000003488 T rte::cxx::test_lib::hello_from_cxx()
Then, I created a crate using this library via
cxx
.Cargo.toml:
[package] name = "cxx_library_use" version = "0.1.0" edition = "2021" [dependencies] cxx = { workspace = true } [build-dependencies] cxx-build = { workspace = true }
build.rs:
fn main() { cxx_build::bridge("src/lib.rs") .include("path/to/header") .flag_if_supported("-std=c++17") .compile("rte-cxx-use-lib"); println!("cargo:rerun-if-changed=src/lib.rs"); println!("cargo:rustc-link-lib=cxx_library"); }
and finally, src/lib.rs:
#[cfg(test)] mod tests { #[cxx::bridge(namespace = "rte::cxx::test_lib")] pub(crate) mod ffi { unsafe extern "C++" { include!("path/to/header/cxx_library.hpp"); pub fn hello_from_cxx() -> String; } } #[test] fn check() { assert_eq!(ffi::hello_from_cxx(), "Hello!"); } }
Running this test produces:
cargo test --package cxx_library_use --lib -- tests::check --exact --nocapture [...] running 1 test test tests::check ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Examining the test executable shows, that it indeed depends on the shared library:
$ otool -L target/debug/deps/cxx_library_use-1750aadfc35f7d0e target/debug/deps/cxx_library_use-1750aadfc35f7d0e: <path>/libcxx_library.dylib (compatibility version 0.0.0, current version 0.0.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1300.36.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
And looking at symbols, it has an undefined symbol satisfied by this library:
nm -C target/debug/deps/cxx_library_use-1750aadfc35f7d0e | grep hello 00000001000039c4 t cxx_library_use::tests::ffi::hello_from_cxx::h1919e2bcda45da9d U rte::cxx::test_lib::hello_from_cxx() 0000000100003d6c T _rte$cxx$test_lib$cxxbridge1$hello_from_cxx
It works.
QED
I tested this on MacOS, which is quite picky. Linux should work exactly the same.
@schreter Thanks for the post. Is it possible you share a runnable cargo project with the above code? It would be nice if I can clone the runnable project and learn a bit more about what you say in the post. I couldn't get it running because of the placeholders in the code. Also it is not clear to me how to create libcxx_library.dylib.
Is it possible you share a runnable cargo project with the above code?
@ssh352 Well, the above is the runnable project, it only consists of the three files I posted above (plus the one C++ file, which you can compile with g++ -o<libname> -shared <cppfile>
or so, for instance). I didn't put it into GitHub, it was only created locally.
@schreter Thanks I've figured it out.
I have read all the author's answer about how to use static or shared C++ lib in
cxx
, he said it will work becausecxx
just append on .h file, while he didn't give more details or specific examples.
Hello, @Tionofzl. Can you tell me how these authors say to answer you, and if you can give me the link to the website that would be even better, I've also been trying to use C++ to call a .so file recently.